基于《Java编程思想》第四版
前言
虽然Java的泛型在语法上和C++相比是类似的,但在实现上两者是全然不同的。
语法
Java只需要一个<>
就可定义泛型。在<>
中可以使用任意符合Java语法要求的字符作为类型的标识,可以定义泛型类、泛型接口、泛型方法等。
class A{ T a; public void foo(Q a){ }}interface B{ void foo(T a);}
实现
Java的泛型并不像C++那样是在编译时根据需要按照模板实例化对应的类。比如下面这段C++代码,会以A
为模板实例化两个类。
templateclass A {public: A(){} T a; };int main(){ A a; A b; return 0;}
查看汇编可以证实,调用的是两个不同类的构造函数
0x0000000000400545 <+15>: callq 0x40056c ::A()> 0x0000000000400551 <+27>: callq 0x400578::A()>
Java中所有类都继承自Object
,任意类对象都可以向上转型后,使用Object
变量存储其引用。基于这一点,Java的泛型实现时其实只有一种类型。以下两个类实际是等同的
class A{ T a;}class A{ Object a;}
当使用反射机制获取泛型类的信息时,可以发现class A<T>
实际就是class A
public static void main(String[] args) { Aa = new A(); Field[] f = a.getClass().getDeclaredFields(); System.out.println(Arrays.toString(f));}// 输出为 [java.lang.Object A.a]
由此我们也可以知道为什么下面这段代码总是输出same class
public static void main(String[] args) { Aa = new A(); A b = new A(); if( a.getClass() == b.getClass() ){ System.out.println("same class"); }}
因为基础类型并不继承自Object
,所以Java的泛型是不支持基础类型的。如果这么做了,就会得到一个错误提示Type argument cannot be of primitive type
。
自限定类型
因为使用泛型时,其类型参数会被当做Object
来处理,所以编译器就无法感知真实类型的方法了。
class A{ public void foo(){ System.out.println("A.foo()"); }}class B{ T a; B(T a){ this.a = a; } public bar(){ a.foo(); // 此处会提示编译错误,Object类型不存在foo()方法 }}
此时必须将泛型类B
的类型参数做限定,让编译器能从限定中获取到足够的信息去判断类型参数是存在foo()
方法的。
class B{ T a; B(T a){ this.a = a; } public bar(){ a.foo(); }}