先看一个Java范型的例子,设B是A的子类:
class A {...}
class B extends A {...}
则我们可以这样使用范型:
import java.util.*;
...
LinkedList<A> a = new LinkedList<A>();
a.add(0, new A());
a.add(1, new B());
这里范型List<A>定义了一个元素为A的List,所以往这个List添加元素的时候可以添加A类或者A的子孙类对象,所以上述两个add()调用很正常。
我们的问题是:子类范型与父类范型相容吗?
比如在上例中,List<A>和List<B>分别被称为父类范型和子类范型,则后者与前者相容吗?
之所以这个问题很重要,是因为子类与父类是相容的。就像上面例子中,凡是父类A的对象可以出现的地方,都可以用子类B的对象替换。这正是面向对象继承机制的精髓。比如,下面语句是正确的:
A a = new B();
但是,子类范型和父类范型之间却没有这种相容性。比如下面的语句是错误的:
LinkedList<A> a = new LinkedList<B>();
为什么呢?很多高手会告诉你因为范型不是类,所以虽然类之间有继承性但是范型之间没有继承性。这话没错,但是说了等于白说,还是没有解释清楚为什么范型之间没有继承性。
假设上述语句是合理的,再假设A的子类有两个,分别是B和C,且B和C之间没有相容性,即相互不是对方的祖先。则下述语句显然是错误的:
LinkedList<B> b = new LInkedList<B>();
b.add(0, new C());
另一方面,
LinkedList<B> b = new LinkedList<B>();
LinkedList<A> a = b;
a.add(0, new C());
看出问题了吗?链表b中居然可以加入C类的对象!这就是问题的所在。所以,如果允许范型继承,则范型可以轻松地突破范型的限制,使得一个不相容类型的对象也能参与计算。这就是不论C++还是Java,抑或是其他的或者未来的面向对象语言,都不可能允许范型继承的原因。
再次强调:类继承和范型继承是两回事。比如下面语句是合理的:
List<A> a = new LinkedList<A>();
有读者问:那Python呢?
Python并没有范型机制。范型是强类型语言才会有的机制。Python是弱类型语言,编译时并不检查数据的类型,所以没有范型机制。范型机制本质上是一种语法机制,是为了防止程序员在语法上犯错,比如向一个LinkedList<B>类型的链表中加入C类型的对象。在Java的字节码(bytecode)中,并没有范型的概念。
现在,你对范型的本质是不是有了更深刻的认识?