泛型是Java 5的一项新特性,它的本质是参数化类型(Parameterized Type)的应用,也就是说所操作的数据类型被指定为一个参数,在用到的时候在指定具体的类型。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口和泛型方法。
泛型使类型(类和接口)在定义类、接口和方法时成为参数,好处在于:
Java 5推出了泛型,也就是在原本的基础上加上了编译时类型检查的语法糖。泛型对于JVM来说是透明的,有泛型的和没有泛型的代码,通过编译器编译后所生成的二进制代码是完全相同的。这个语法糖的实现被称为擦除。Java中的泛型基本上都是在编译器这个层次来实现的。在生成的Java字节码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会在编译器在编译的时候去掉。这个过程就称为类型擦除。
无论何时定义一个泛型类型,都自动提供一个相应的原始类型(Raw Type,这里的原始类型并不是指int、boolean等基本数据类型),原始类型的类名称就是带有泛型参数的类删去泛型参数后的类型名称,而原始类型会擦除(Erased)类型变量,并且把它们替换为限定类型(如果没有指定限定类型,则擦除为Object类型)。
泛型变量的类型的使用:
什么是桥方法(Bridge Method),从实际代码入手:
// 父类 public interface Supper<T> { void method(T t); } // 其中一个子类 public class Sub implements Supper<Integer> { @Override public void method(Integer value) { System.out.println(value); } }
父类Supper
public interface Supper{ void method(Object t); }
子类Sub虽然实现了父类Supper,但是它只实现了void method(Integer value)而没有实现父类中的void method(Object t),这个时候,编译期编译器会为子类Sub创建此方法,也就是子类Sub会变成这样:
public class Sub implements Supper<Integer> { @Override public void method(Integer value) { System.out.println(value); } public void method(Object value) { this.method((Integer) value); } }
a instanceof Pair<String>
是错误的 void method(List<String> list)
与 void method(List<Integer> list)
冲突 Pair<String>[]
是错误的 new T()
泛型与向上转型的概念:
无限定通配符使用<?>的格式,代表未知类型的泛型。 当可以使用Object类中提供的功能或当代码独立于类型参数来实现方法时,这样的参数可以使用任何对象。
限定通配符对类型进行了限制:
PECS原则,Producer-Extend,Customer-Super,也就是泛型代码是生产者,使用Extend,泛型代码作为消费者Super
由于Java中的泛型,在编译后会被擦除类型参数。如果用instanceof来查询对象的类型,只能查到对应的原始类型(raw type)。虽然有类型擦除,但也不是所有的地方都会被擦除。
Java泛型有这么一种规律:
位于使用一侧的,源码里写什么到运行时都没了。
import java.util.List; import java.util.Map; public class GenericClass<T> { // 1 private List<T> list; // 2 private Map<String, T> map; // 3 public <U> U genericMethod(Map<T, U> m) { // 4 return null; } }
上面的代码实际上:
这是因为从Java 5开始class文件的格式有了调整,规定这些泛型信息要写到class文件中。在Java里面可以通过反射获取泛型信息的场景有:
不能通过反射获取泛型类型信息的场景有:
Java 5在java.lang.reflect中新引入四种泛型类型:ParameterizedType、TypeVariable、WildcardType、GenericArrayType都是接口。
也就是参数化类型,注释里面说到ParameterizedType表示一个参数化类型,例如 Collection<String>
,实际上只要带有参数化(泛型)标签 <ClassName>
的参数或者属性,都属于ParameterizedType。
public interface ParameterizedType extends Type { Type[] getActualTypeArguments(); Type getRawType(); Type getOwnerType(); }
也就是类型变量,它是各种类型变量的公共父接口,它主要用来表示带有上界的泛型参数的信息,它和ParameterizedType不同的地方是,ParameterizedType表示的参数的最外层一定是已知具体类型的(如 List<String>)
,而TypeVariable面向的是K、V、E等这些泛型参数字面量的表示。常见的TypeVariable的表示形式是 <T extends KnownType-1 & KnownType-2>
public interface TypeVariable<D extends GenericDeclaration> extends Type { //获得泛型的上限,若未明确声明上边界则默认为Object Type[] getBounds(); //获取声明该类型变量实体(即获得类、方法或构造器名) D getGenericDeclaration(); //获得名称,即K、V、E之类名称 String getName(); //获得注解类型的上限,若未明确声明上边界则默认为长度为0的数组 AnnotatedType[] getAnnotatedBounds() }
public void query(java.util.List<Person>)
用于表示通配符(?)类型的表达式的泛型参数,例如<? extends Number>等。根据WildcardType注释提示:现阶段通配符表达式仅仅接受一个上边界或者下边界,这个和定义类型变量时候可以指定多个上边界是不一样。但是为了保持扩展性,这里返回值类型写成了数组形式。实际上现在返回的数组的大小就是1。
public interface WildcardType extends Type { Type[] getUpperBounds(); Type[] getLowerBounds(); }
也就是泛型数组,也就是元素类型为泛型类型的数组实现了该接口。它要求元素的类型是ParameterizedType或TypeVariable(实际中发现元素是GenericArrayType也是允许的)。
public interface GenericArrayType extends Type { Type getGenericComponentType(); }
Class
Constructor:
Method:
Field
注:以上内容收集于互联网多篇文章,在此感谢原作者们。