【一天一个基础系列】- java之泛型篇 (2)

大多数时候都可以使用泛型方法来代替类型通配符//使用类型通配符 public interface Collection<E>{ void add(Collection<?> p); void delete(Collection<? extends E> p) } //使用泛型方法 public interface Collection<E>{ <T> void add(Collection<T> p); <T extends E> void delete(Collection<T> p) }

也可以同时使用泛型方法和通配符public class Collections{ public static <T> void copy(List<T> dest,List<? extends T> src){} }

“菱形”语法与泛型构造器

“菱形”语法前面已经提到,不再赘述,说一下啥是泛型构造器,其实就是java允许构造器签名中声明泛型形参class Foo{ public <T> Foo(T t){ } } public void method(){ //泛型构造器中T类型为String new Foo(""); //也可以这么定义,显示指定T类型为String new<String> Foo(""); //泛型构造器中T类型为Integer new Foo(10); }

泛型方法与方法重载

因为泛型既允许设定通配符的上限,也允许设定通配符的下限,从而允许在一个类里包含以下两种方法的定义<T> void copy(Collection<T> des,Collection<? extends T> src){}; <T> T copy(Collection<? super T> des,Collection<T> src){};

重载的情况public void method(List<String> list){} public void method(List<Integer> list){}

上述这段代码是不能被编译的,因为参数List<Integer>和List<String>编译之后都被擦除了, 变成了同一种的裸类型List,类型擦除导致这两个方法的特征签名变得一模一样(下面会提到类型擦除)

类型推断

java 8改进了泛型方法的类型推断能力,类型推断主要有如下两方面

1)可通过调用方法的上下文来推断泛型的目标类型

2)可在方法调用链中,将推断得到的泛型传递到最后一个方法

泛型擦除和转换

擦除:当把一个具有泛型信息的对象赋给另一个没有泛型信息的变量时,所有在尖括号之间的类型信息都将被扔掉;Java代码编译成Class文件, 然后再用字节码反编译工具进行反编译后, 将会发现泛型都不见了, 程序又变回了Java泛型出现之前的写法, 泛型类型都变回了裸类型(List<String> 对应的裸类型就是List)

比如:List<String> 类型会被转换成List,则该List对集合元素的类型检查变成了泛型参数的上限(Object),那么在使用,比如插入的时候,又会出现从Object到String的强制转型代码

擦除法所谓的擦除, 仅仅是对方法的Code属性中的字节码进行擦除, 实际上元数据中还是保留了泛型信息, 这也是我们在编码时能通过反射手段取得参数化类型的根本依据

java不支持原生类型的泛型,即是不支持 int/long等,List<int>这种是不支持的,那么一旦把泛型信息擦除后,遇到原生类型时把装箱、 拆箱也自动做了,这也成为Java泛型慢的重要原因

泛型与数组

数组元素的类型不能包含泛型变量或泛型形参,除非是无上限的类型通配符,但可以声明元素类型包含泛型变量或泛型形参的数组。也就是说,只能声明List<String>[]形式的数组,但不能创建ArrayList<String>[10]这样的数组对象

总结:Java的泛型在使用期间需要更加注意泛型擦除的情况,总体而言,其写法也并不优雅。也希望未来的泛型会支持基本类型。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzsjdw.html