没有继承就可以实现多态性吗?
在一次采访中,有人问我是否可以在没有继承的情况下实现多态性.这可能吗?
In an interview I was asked if polymorphism can be achieved without inheritance. Is this possible?
推荐答案
我读过的关于这个主题的最好解释是 Luca Cardelli,著名类型理论家.文章名为论理解类型、数据抽象和多态性.
The best explanation on the subject that I've ever read is an article by Luca Cardelli, a renown type theorist. The article is named On Understanding Types, Data Abstraction, and Polymorphism.
Cardelli 在本文中定义了几种类型的多态性:
Cardelli defines several types of polymorphism in this article:
- 通用
- 参数
- 包含
- 重载
- 强制
与继承相关的多态性分为包含多态性或亚型多态性.
The kind of polymorphism related to inheritance is classified as inclusion polymorphism or subtype polymorphism.
维基百科提供了一个很好的定义:
Wikipedia provides a good definition:
在面向对象编程中,子类型多态或包含多态性是类型论中的一个概念,其中名称可以表示许多不同类的实例,只要它们通过以下方式相关一些常见的超类.包含多态性一般是通过子类型支持,即不同类型的对象是完全可以替代另一种类型的对象(它们的基础type(s)),因此可以通过通用接口进行处理.或者,包含多态性可以通过类型来实现强制转换,也称为类型转换.
In object-oriented programming, subtype polymorphism or inclusion polymorphism is a concept in type theory wherein a name may denote instances of many different classes as long as they are related by some common super class. Inclusion polymorphism is generally supported through subtyping, i.e., objects of different types are entirely substitutable for objects of another type (their base type(s)) and thus can be handled via a common interface. Alternatively, inclusion polymorphism may be achieved through type coercion, also known as type casting.
另一篇名为 面向对象编程中的多态性 的维基百科文章似乎回答了您的问题好吧.
Another Wikipedia article called Polymorphism in object-oriented programming seems to answer your questions as well.
Java 中的这种子类型化特性是通过类和接口的继承等方式实现的.尽管 Java 的子类型化特性在继承方面可能并不总是很明显.以泛型的协变和逆变的情况为例.此外,数组是可序列化和可克隆的,尽管这在类型层次结构中的任何地方都不明显.也可以说,通过原始扩展转换,Java 中的数字运算符是多态的,在某些情况下甚至可以接受完全不相关的操作数(即字符串和数字或字符串加上其他对象的连接).还要考虑对基元进行装箱和拆箱的情况.后面这些多态性(强制和重载)与继承无关.
This subtyping feature in Java is achieved, among other means, through the inheritance of classes and interfaces. Although the subtyping features of Java may not be evident in terms of inheritance all the time. Take for example the cases of covariance and contravariance with generics. Also, arrays are Serializable and Cloneable although this is not evident anywhere in the type hierarchy. It can also be said that through primitive widening conversion the numeric operators in Java are polymorphic, in certain cases even accepting totally unrelated operands (i.e. concatenation of strings and numbers or of a string plus some other object). Consider also the cases of boxing and unboxing of primitives. These latter cases of polymorphism (coercion and overloading) are not at all related to inheritance.
包容
List<Integer> myInts = new ArrayList<Integer>();
这就是您的问题所指的情况,即类型之间存在继承或实现关系时,例如 ArrayList 实现 List 的情况.
This is the case to which your question seems to refer i.e. when there is an inheritance or implementation relationship between the types, as in this case where ArrayList implements List.
不过,正如我所提到的,当您介绍 Java 泛型时,有时子类型化规则会变得模糊:
As I mentioned, though, when you introduce Java generics, some time the rules of subtyping get fuzzy:
List<? super Number> myObjs = new ArrayList<Object>(); List<? extends Number> myNumbers = new LinkedList<Integer>();
在其他情况下,API 中的关系甚至不明显
And in other cases, the relationships are not even evident in the API
Cloneable clone = new int[10]; Serializable obj = new Object[10]
即便如此,根据 Cardelli 的说法,所有这些都是通用多态性的形式.
Even so, all these, according to Cardelli, are forms of universal polymorphism.
参数化
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; } }
相同的算法可用于过滤各种类型的谓词列表,而不必为每种可能的列表类型重复一行代码.实际列表的类型和谓词的类型是参数化的.请参阅 JDK 8 预览版 中提供的 lambda 表达式示例(为了谓词实现的简洁性).
The same algorithm can be used to filter all kinds of lists with all kinds of predicates without having to repeat a single line of code for every possible type of list. The type of the actual list and the type of predicate are parametric. See this example with lambda expressions available in JDK 8 Preview (for the brevity of predicate implementation).
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 的说法,这是一种通用多态性.
According to Cardelli, this is a form of universal polymorphism.
强制
double sum = 1 + 2.0;
整数和浮点运算是完全不同的.如果没有某种形式的强制,在这里将加号运算符应用于不同类型的两个操作数是不可能的.
Integer and floating-point arithmetic are totally different. Applying the plus operator to two operands of different types here is impossible without some form of coercion.
在此示例中,类型 integer 和 double 会自动强制(转换)为 double 类型,而无需显式强制转换.整个表达式提升为两倍.之所以如此,是因为在 Java 中我们有原始的扩展转换.
In this example, the types integer and double, are automatically coerced (converted) to type double without an explicit cast. The entire expression is promoted to double. This is so because in Java we have primitive widening conversions.
根据 Cardelli 的说法,这种形式的自动强制转换是为加号运算符提供的一种特殊多态性形式.
According to Cardelli, this form of automatic coercion is a form of ad-hoc polymorphism provided for the plus operator.
在某些语言中,如果没有显式强制转换,您甚至无法将整数和浮点数相加(即 AFAIK、SML,顺便说一下,参数多态性是克服此类问题的关键).
There are languages in which you could not even sum an integer and a floating-point number without an explicit cast (i.e. AFAIK, SML, in which, by the way, parametric polymorphism is key to overcome this kind of problems).
重载
double sum = 2.0 + 3.0; String text = "The sum is" + sum;
这里的加号运算符表示两种不同的东西,具体取决于所使用的参数.显然,操作员已经超载.这意味着它根据操作数的类型具有不同的实现.根据 Cardelli 的说法,这是为加号运算符提供的一种特殊多态性形式.
The plus operator here means two different things depending on the arguments used. Evidently, the operator has been overloaded. This implies it has different implementations depending on the types of operands. According to Cardelli, this is a form of ad-hoc polymorphism provided for the plus operator.
当然,这也适用于类中的方法重载形式(即 java.lang.Math 方法 min 和 max 被重载以支持不同的原始类型).
This, of course, also applies to forms of method overloading in classes (i.e java.lang.Math methods min and max are overloaded to support different primitive types).
即使继承在这些形式的多态性的实现中发挥了重要作用,当然它也不是唯一的方法.其他非面向对象的语言提供其他形式的多态性.以 Python 等动态语言甚至静态类型的 duck typing 为例Go 等语言,或 SML、Ocaml 和 Scala 等语言中的 代数数据类型,或 在 Haskell 等语言中键入类,多方法,JavaScript 中的 原型继承 等.
Even when inheritance plays an important role in the implementation of some of these forms of polymorphism, certainly it is not the only way. Other languages that are not object-oriented provide other forms of polymorphism. Take, for example, the cases of duck typing in dynamic languages like Python or even in statically-typed languages like Go, or algebraic data types in languages like SML, Ocaml and Scala, or type classes in languages like Haskell, multi methods in Clojure, prototypal inheritance in JavaScript, etc.
相关文章