Java泛型

368 查看

Java泛型的参数只可以代表类,不能代表个别对象。


由于Java泛型的类型参数之实际类型在编译时会被消除,所以无法在运行时得知其类型参数的类型。

In Java, generics are only checked at compile time for type correctness. The generic type information is then removed via a process called type erasure, to maintain compatibility with old JVM implementations, making it unavailable at runtime.

Java编译程序在编译泛型时会自动加入类型转换的编码

For example, a List<String> is converted to the raw type List. The compiler inserts type casts to convert the elements to the String type when they are retrieved from the list, reducing performance compared to other implementations such as C++ templates.


类型擦除

Java的泛型在编译器这个层次上实现,使用泛型的时候加上的类型参数会在编译的时候去掉。生成的Java字节代码中不包含类型信息(例如List<String>编译后JVM看到的只是List>)

  • 静态变量是被泛型类的所有实例所共享的。对于声明为 MyClass<T> 的类,访问其中的静态变量的方法仍然是 MyClass.myStaticVar。不管是通过 new MyClass<String> 还是 newMyClass<Integer> 创建的对象,都是共享一个静态变量。

  • 泛型的类型参数不能用在Java异常处理的 catch 语句中。因为异常处理是由JVM在运行时刻来进行的。由于类型信息被擦除,JVM是无法区分两个异常类型 MyException<String>MyException<Integer> 的。对于JVM来说,它们都是 MyException 类型的。也就无法执行与异常对应的catch语句。


通配符

一个方法如果接收 List<Object> 作为形式参数,但是如果尝试将一个 List<String> 的对象作为实际参数传进去 会因为实际上包含隐含的类型转换问题得到 compile err
但是如果使用 List<? extends Object> 或者 List<? super String> 则不会

    public void inspect(List<? extends Object> list) {
        for (Object obj : list) {
            System.out.println(obj);
        }
    }

    public void test() {
        ArrayList<String> strs = new ArrayList<String>();
        inspect(strs);
    }
    
    //不会报错

当泛型类的类型声明中使用了通配符的时候, 其子类型可以在两个维度上分别展开。
如对 Collection<? extends Number> 来说

  • 其子类型可以在 Collection 这个维度上展开,即 List<? extends Number>Set<? extends Number> 等;

  • 也可以在 Number 这个层次上展开,即 Collection<Double>Collection<Integer> 等。

如此循环下去,ArrayList<Long>和 HashSet<Double>等也都算是Collection<? extends Number>的子类型。