多态可能没有inheritance?

在一次采访中,我被问到是否可以在没有inheritance的情况下实现多态。 这可能吗?

关于我读过的主题的最好解释是着名的理论家卢卡·卡德利 ( Luca Cardelli )的一篇文章。 这篇文章被命名为理解types,数据抽象和多态性 。

多态性的types

Cardelli在本文中定义了几种types的多态:

  • 普遍
    • 参数
    • 包容
  • 特别指定
    • 超载
    • 强迫

与遗传有关的多态性分为包含多态性或亚型多态性。

维基百科提供了一个好的定义:

在面向对象编程中,子types多态或包含多态是types理论中的一个概念,其中一个名称可以表示许多不同类的实例,只要它们与某个共同的超类相关。 包含多态性通常通过子types来支持,即不同types的对象可以完全替代另一types的对象(它们的基types),因此可以通过通用接口来处理。 或者,包含多态性可以通过types强制来实现,也称为types强制。

另一篇维基百科文章称为面向对象编程中的多态,似乎也回答了你的问题。

在Java中

Java中的这种子types特征是通过inheritance类和接口来实现的。 虽然Java的子types特征在inheritance方面可能始终不明显。 举例来说,与generics协变和相反的情况。 另外,数组是可序列化的和可复制的,尽pipe在types层次结构的任何地方都不是这样。 也可以说,通过原始扩展转换,Java中的数字运算符是多态的,在某些情况下甚至可以接受完全不相关的操作数(即string和数字的串联或string加上一些其他对象)。 还要考虑装箱和拆箱的情况。 后面这些多态的情况(强制和超载)与inheritance无关。

例子

入选

List<Integer> myInts = new ArrayList<Integer>(); 

这就是你的问题似乎涉及的情况,即当types之间存在inheritance关系或实现关系时,如在这种情况下ArrayList实现List。

正如我所提到的,虽然,当您介绍Javagenerics时,有些时候子types的规则会变得模糊:

 List<? super Number> myObjs = new ArrayList<Object>(); List<? extends Number> myNumbers = new LinkedList<Integer>(); 

而在其他情况下,这些关系在API中甚至不明显

 Cloneable clone = new int[10]; Serializable obj = new Object[10] 

即便如此,Cardelli认为,所有这些都是通用多态的forms。

参数

 public <T> List<T> filter(Predicate<T> predicate, List<T> source) { List<T> result = new ArrayList<>(); for(T item : source) { if(predicate.evaluate(item)){ result.add(item); } return result; } } 

可以使用相同的algorithm来过滤各种谓词,而不必为每一种可能的types重复一行代码。 实际列表的types和谓词的types是参数化的。 在JDK 8 Preview中可用的lambdaexpression式来看这个例子(为了简化谓词实现)。

 filter(x -> x % 2 == 0, asList(1,2,3,4,5,6)); //filters even integers filter(x -> x % 2 != 0, asList(1L,2L,3L,4L,5L,6L)); //filters odd longs filter(x -> x >= 0.0, asList(-1.0, 1.0)); //filters positive doubles 

根据Cardelli的说法,这是一种通用多态的forms。

强迫

 double sum = 1 + 2.0; 

整数和浮点运算完全不同。 对这两种不同types的操作数使用加号运算符是不可能的,没有某种forms的强制。

在这个例子中,typesinteger和double被自动强制(转换)为doubletypes,而没有显式强制转换。 整个expression式被提升了一倍。 这是因为在Java中我们有了原始的扩展转换。

根据Cardelli的说法,这种自动强制forms是为加号运算符提供的特殊多态的一种forms。

有些语言中,如果没有明确的转换(即AFAIK,SML,其中参数多态性是解决这类问题的关键),您甚至无法对整数和浮点数进行求和。

超载

 double sum = 2.0 + 3.0; String text = "The sum is" + sum; 

这里的加号运算符意味着两个不同的东西取决于使用的参数。 显然,运营商已经超载。 这意味着它根据操作数的types有不同的实现。 根据Cardelli的说法,这是为加号运算符提供的ad-hoc多态性的一种forms。

当然,这也适用于类中的方法重载forms(即java.lang.Math方法min和max被重载以支持不同的基本types)。

在其他语言

即使inheritance在实现这些多态的某些forms中起着重要作用,当然也不是唯一的方法。 其他不是面向对象的语言提供了其他forms的多态性。 举例来说,用Python这样的dynamic语言,甚至像Go这样的静态types语言,或SML,Ocaml和Scala等语言中的代数数据types ,或Haskell等语言中的types类 ,Clojure中的多种方法 ,JavaScript中的原型inheritance等等。

当然。 在Java中,可以有两个类实现相同的接口,并且它们的结果是多态的。 没有function被inheritance。

 public interface Foo { public int a(); } public class A implements Foo { public int a() { return 5; } } public class B implements Foo { public int a() { return 6; } } 

然后在别处:

 Foo x = new A(); System.out.println(xa()) Foo y = new B(); System.out.println(ya()) 

xy都是Foo ,但是当你调用a()时它们有不同的结果。

Ad-hoc多态性>运算符重载>没有inheritance

Ad-hoc多态>方法重载>没有inheritance

Ad-hoc多态性>方法覆盖> With Inheritence

参数多态性>generics>无遗传

亚型多态性或包含多态性>多态性分配>有inheritance性

亚型多态性或包含多态性>多态返回types>具有inheritance性

亚型多态性或包含多态性>多态性参数types>具有inheritance性

胁迫多态>拓宽>有无遗传

强制多态>自动装箱和拆箱>不带inheritance

胁迫多态性>变异args>无遗传

胁迫多态>types铸造>无遗传

静态types

重载 – 这意味着多重方法具有相同的名称,但不同的签名,这是可能的,而不是重写

 class StaticPolyExample { void print(int s) { //print s } void print(String s) { //print s } } 

dynamictypes

重写 – 这意味着超类中的方法将在需要inheritance的子类中重新定义

 class Printer { void print(String s) { // prints String } } class diffPrinter extends Printer { void print(String s) { // prints String differently } } 

函数重载是多态性之一(尽pipe不是真正的多态性意味着什么),这可以在没有inheritance的情况下实现。

例如

 class Foo { public void Arrest( Animal A){ /*code...*/ } public void Arrest( Terrorist T ) { /*code...*/ } } from main : Foo f= new Foo(); f.Arrest( new Lion() ); f.Arrest(new Terrorist()); 

逮捕方法被称为2次,但执行的代码path是不同的。

*这又不是多态的真正forms。 一般的真正的多态性不能在没有inheritance的情况下实现。

是的,我想他们可能想听到接口的多态性。 所以如果有两个从同一个接口实现的类,那么我们可以在所有我们期望有这样的对象的地方使用。 查看维基百科的代码:

 // from file Animal.java public interface Animal { public String talk(); } // from file Cat.java public class Cat implements Animal { @Override public String talk() { return "Cat says Meow!"; } } // from file Dog.java public class Dog implements Animal { @Override public String talk() { return "Dog says Woof! Woof!"; } } // from file PolymorphismExample.java public class PolymorphismExample { public static void main(String[] args) { Collection<Animal> animals = new ArrayList<Animal>(); animals.add(new Cat()); animals.add(new Dog()); for (Animal a : animals) { System.out.println(a.talk()); } } }