引言
类型兼容的对象可以进行赋值,如可将一个 Integer 对象传给一个 Object 对象,因为 Object 是 Integer 的基类。
Object someObject = new Object();
Integer someInteger = new Integer(10);
someObject = someInteger; // OK
对于方法的参数,也可传递类型兼容的对象。如 Integer 是一个 Number ,故如下代码可正确执行。
public void someMethod(Number n) { /* ... */ }
someMethod(new Integer(10)); // OK
someMethod(new Double(10.1)); // OK
类型兼容不能直接运用到泛型类型上,考虑如下方法:
public static void boxTest(Box<Number> box) {
System.out.println(box.get());
}
该方法接受类型为 Box<Number>
的参数,那么该方法是否接收类型为 Box<Integer>
或 Box<Double>
的参数?要回答这个问题,首先要清楚以下两个问题:
1. boxTest
的字节码签名是什么?
2. Box<Number>
的子类型应满足什么条件?
1. BoxTest的字节码签名
这个话题涉水较深,如果使用 javap -s -c, 只能看到 descriptor 的信息:
public static void boxTest(generic.inheritance.Box<java.lang.Number>);
descriptor: (Lgeneric/inheritance/Box;)V
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: invokevirtual #3 // Method generic/inheritance/Box.get:()Ljava/lang/Object;
7: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
10: return
由上面的信息只能推导出 boxTest 只接受类型为 Box 的参数。而 Box 是泛型类,其基类为 Object. 需要使用 javap -v 才能确定 boxTest 完整签名信息:
public static void boxTest(generic.inheritance.Box<java.lang.Number>);
descriptor: (Lgeneric/inheritance/Box;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: invokevirtual #3 // Method generic/inheritance/Box.get:()Ljava/lang/Object;
7: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
10: return
LineNumberTable:
line 11: 0
line 12: 10
Signature: #19 // (Lgeneric/inheritance/Box<Ljava/lang/Number;>;)V
Signature: #19
才是 boxText
的完整签名信息,表示该方法之接受类型为泛型类型 Box<Number>
的参数。接下来确定 Box<Number>
的子类型。
2. 泛型类的子类型
泛型类或接口的子类型创建方式与创建常规的类或接口的子类型创建方式一致,即使用 extends 或 implements 关键字。
以 Collections 为例,ArrayList<E>
实现了 List<E>
, 同时 List<E>
扩展自 Collections<E>
. 故 ArrayList<E>
是 List<E>
的子类型,List<E>
是 Collections<E>
的子类型。只要类型参数 E 一致,这三个类的继承关系就得到保持。
由于 Box<Number>
和 Box<Integer>
的类型参数不同,故两者没有继承关系,其关系可如下表示:
因此, testBox(Box<Number>)
不接收类型为 Box<Integer>
和 Box<Double>
的参数。
小结
对于类 A 和类 B, MyClass<A>
和 MyClass<B>
没有任何继承关系,即使 A 是 B 的子类。MyClass<A>
和 MyClass<B>
的共同子类为 Object.
拓展:包含额外类型参数的继承关系
对于自定义的列表接口, PlayloadList 包含了一个额外的类型参数 P, 表示列表中的每个元素都关联了类型为 P 的元素。设存在方法:
interface PayloadList<E,P> extends List<E> {
void setPayload(int index, P val);
...
}
以下的三个 PayloadList 都是 List<String>
的子类:
- PayloadList
<String,String>
- PayloadList
<String,Integer>
- PayloadList
<String,Exception>
继承关系如下所示: