Edw*_*rzo 66
我读过的关于这个主题的最佳解释是Luca Cardelli的一篇文章,他是一位着名的理论家.该文章名为On Understanding Types,Data Abstraction和Polymorphism.
Cardelli在本文中定义了几种类型的多态:
与继承相关的多态性类别被分类为包含多态性或亚型多态性.
Wikipedia提供了一个很好的定义:
在面向对象的编程中,子类型多态性或包含多态性是类型理论中的概念,其中名称可以表示许多不同类的实例,只要它们与某些共同的超类相关.通常通过子类型支持包含多态性,即,不同类型的对象完全可替代另一种类型的对象(它们的基本类型),因此可以通过公共接口来处理.或者,包合多态性可以通过类型强制来实现,也称为类型转换.
在面向对象编程中另一篇名为Polymorphism的维基百科文章似乎也回答了你的问题.
除了其他方法之外,Java中的这种子类型功能通过类和接口的继承来实现.尽管Java的子类型特征在继承方面可能并不明显.例如,协方差和与泛型相反的情况.此外,数组是Serializable和Cloneable,尽管在类型层次结构中的任何位置都不明显.也可以说通过原始扩展转换,Java中的数字运算符是多态的,在某些情况下甚至接受完全不相关的操作数(即字符串和数字或字符串加上一些其他对象的串联).还要考虑基元的装箱和拆箱的情况.后面这些多态性(强制和重载)的情况与继承完全无关.
入选
List<Integer> myInts = new ArrayList<Integer>();
Run Code Online (Sandbox Code Playgroud)
这是您的问题似乎引用的情况,即类型之间存在继承或实现关系时,就像ArrayList实现List的情况一样.
正如我所提到的,当你引入Java泛型时,有时候子类型的规则会变得模糊:
List<? super Number> myObjs = new ArrayList<Object>();
List<? extends Number> myNumbers = new LinkedList<Integer>();
Run Code Online (Sandbox Code Playgroud)
而在其他情况下,API中的关系甚至不明显
Cloneable clone = new int[10];
Serializable obj = new Object[10]
Run Code Online (Sandbox Code Playgroud)
即便如此,根据Cardelli的说法,所有这些都是通用多态的形式.
参数
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;
}
}
Run Code Online (Sandbox Code Playgroud)
可以使用相同的算法来过滤具有各种谓词的所有类型的列表,而不必为每种可能类型的列表重复单行代码.实际列表的类型和谓词的类型是参数化的.使用JDK 8 Preview中可用的lambda表达式查看此示例(为了简化谓词实现).
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
Run Code Online (Sandbox Code Playgroud)
根据Cardelli的说法,这是一种通用多态性.
强迫
double sum = 1 + 2.0;
Run Code Online (Sandbox Code Playgroud)
整数和浮点运算完全不同.如果没有某种形式的强制,将plus运算符应用于两个不同类型的操作数是不可能的.
在此示例中,integer和double类型会自动强制(转换)为double类型而不进行显式强制转换.整个表达式被提升为双倍.这是因为在Java中我们有原始的扩展转换.
根据Cardelli的说法,这种形式的自动强制是为plus运算符提供的一种ad-hoc多态.
有些语言甚至无法对没有显式转换的整数和浮点数求和(即AFAIK,SML,顺便说一句,参数多态是解决此类问题的关键).
超载
double sum = 2.0 + 3.0;
String text = "The sum is" + sum;
Run Code Online (Sandbox Code Playgroud)
这里的plus运算符意味着两种不同的东西,具体取决于使用的参数 显然,操作员已经超负荷运转.这意味着它具有不同的实现,具体取决于操作数的类型.根据Cardelli的说法,这是为plus运算符提供的一种ad-hoc多态.
当然,这也适用于类中方法重载的形式(即java.lang.Math方法min和max被重载以支持不同的基元类型).
即使继承在实现某些形式的多态中起着重要作用,当然它也不是唯一的方法.其他非面向对象的语言提供其他形式的多态性.举个例子来说,案件鸭打字像Python或即使是在像围棋,或静态类型语言的动态语言的代数数据类型,像SML,ocaml的和Scala,或语言类型类的象Haskell语言,多方法用Clojure ,JavaScript中的原型继承等
小智 6
Ad-hoc多态>运算符重载>无继承
Ad-hoc多态>方法重载>无继承
Ad-hoc多态>方法覆盖>具有继承性
参数多态>泛型>无遗传
亚型多态性或包含多态性>多态分配>具有遗传性
亚型多态性或包含多态性>多态返回类型>具有继承性
亚型多态性或包含多态性>多态参数类型>具有继承性
强制多态>扩展>有或没有继承
强制多态>自动装箱和拆箱>没有继承
强制多态> Var args>无遗传
强制多态>类型铸造>无遗传
当然.在Java中,您可以让两个类实现相同的接口,并且它们的结果是多态的.没有继承任何功能.
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;
}
}
Run Code Online (Sandbox Code Playgroud)
其他地方:
Foo x = new A();
System.out.println(x.a())
Foo y = new B();
System.out.println(y.a())
Run Code Online (Sandbox Code Playgroud)
这两个x
和y
是Foo
S中,但有不同的结果,当你打电话a()
.