泛型的出现,让我们写一些容器的时候变的更爽了。比如我只想在列表中添加Message,非 Message不能添加进来的话,我们就需要使用泛型了。再比如RxJava的出现,让泛型的使用 更加出神入化了。
泛型是从Java5开始被支持的,泛型的出现可以让编译器提前告知你类型错误,从而避免(减 少)运行时出现类型错误等等。
下面我们来先举个例子:
// Case 1 final List<Integer> integers = new ArrayList<>(); final List<Object> objects = integers; // Case 2 final Integer[] aI = new Integer[1]; final Object[] aO = aI;
当我们IDE中写出如上语句的时候,你会发现Case 1会报错误,而Case 2不会报错。主要原 因是Java中泛型默认是不允许协变,而数组是允许协变的。如果Case 1也能像Case 2一样的 话,那么就破坏了泛型的安全性。
类型擦除
其实泛型是在编译器层面实现的,简单来说就是编译的时候编译器会将泛型给擦除,只留下 RawType。比如:List<String>编译后会变成List。
之所以会出现泛型擦除主要原因是泛型是Java1.5之后才出现的,也就是说我们之前写的代 码是没法使用的。主要是兼容性方面的考虑,故而编译器编译的时候会进行泛型擦除。
正因为会类型擦除,从而不会存在List<String>.class这种class发生。如果我们写代码的 时候想要告诉调用的类型是这样的方式怎么办呢?一种方式是反射,一种是自己实现一个 ParameterizedType。下面将Type的时候会讲到。
通配符
extends 表示的是类型的上界,即表示类型的最上层的超类。故而其表示的类型是其本身或 者其子类。
super 表示的是类型的下界,即表示类型的最下层的子类。故而其表示的是类型本身或者其 超类,一直到Object。
下面分别距离讲讲extent和super的读取逻辑。
extends
// 可以使用Number本身 List<? extends Number> list = new ArrayList<Number>(); // Integer继承自Number List<? extends Number> list = new ArrayList<Integer>(); // Long继承自Number List<? extends Number> list = new ArrayList<Long>();
读取的时候,以上面代码为例,列表可以保证的是读取的数据类型一定是Number或者其子类。
但是使用 extend 方式的通配符没法进行写入,因为没法知道 list 具体是什么类型。比如 你想往里面 add 一个 Number,但是 list 有可能是 ArrayList;如果你想往里面写入一个 Integer 的时候,list 也有可能是 ArrayList。所以使用 extends 的时候是没法往 list 里面写入的。
但是,在list初始化的时候可以直接引用另一个list。比如:
final List<? extends Number> list = new ArrayList<Long>(); final List<? extends Number> list2 = list;
super
final Integer integer = Integer.valueOf(0); final List<? super Integer> numbers = new ArrayList<Number>();// Number是Integer的父类 final List<? super Integer> objects = new ArrayList<Object>();// Object是所有对象的父类 final List<? super Integer> integers = new ArrayList<Integer>();// 可以使用Integer本身 numbers.add(integer); objects.add(integer); integers.add(integer);
读取的数据的时候,由于不知道具体的泛型是什么,所以没法确认其类型。但是可以肯定的 是,必然是Object或者其子类(废话)。
而 super 则可以直接往里面写入数据。如上,不论 list 是一个 ArrayList<Integer> 还 是一个ArrayList<Number>,我们都可以往里面写入一个 Integer。因为可以确定的是 list 中的泛型必然是 Integer 或者其父类。但是,Integer 的父类比如 Number 是不允许写入 的,因为编译器不能确定 list 是一个 ArrayList<Number>,编译器只知道当前的泛型是 Integer 或者其父类。
协变
前面提到了协变,在 Java 中数据时支持协变的。对于数组而言,Number 是 Integer 的父 类,那么 Number[] 也是 Integer[] 的父类了。而泛型的出现就是为了让我们写代码的时 候类型安全,如 果List 是 List 的父类的话,我们编译器会运行我们往 list 里面添加一 个Long,但是它需要的是 Integer,故而就破坏了泛型的初衷:类型安全。所以默认泛型是 不支持协变的。
但是,使用通配符的时候泛型是支持协变的。比如:
final List<Number> numbers = new ArrayList<>(); final List<? extends Object> list = integers;
原因是使用 extends 的时候,编译器要求 list 的泛型必须是 Object 的子类,故而 Number 可以支持。
下面这种方式是逆变,它与协变是反过来的。
final List<Number> numbers = new ArrayList<>(); final List<? super Integer> list = numbers;
对了,开头提到的那种不能编译通过的方式是不变。
Type
Type 是所有类型的父接口,比如 Class 本身就是继承自 Type 的。 在我们使用反射的时 候通常会用到 Type。
public final class Class<T> implements java.io.Serializable, java.lang.reflect.GenericDeclaration, java.lang.reflect.Type, java.lang.reflect.AnnotatedElement {}
它大概会分为下面几种方式:
Class
除泛型之外Class本身就是一种Type,包括PrimitiveType也会被box成对应的Class对象。
PrimitiveType
基本类型
比如:
boolean.class/byte.class/char.class/double.class/float.class/int.class/long.class/short.class 。当我们反射需要用到的时候需要将其转换成对应的Class,比如Boolean.class等等。
ParameterizedType
参数化类型。
比如: List/Map<Integer,String>等等。
主要三个方法:
Type[] getActualTypeArguments();
返回的是泛型的参数的类型,比如 List<String>
会
返回 String
,如果是 Map<String,Integer>
则为 String
和 Integer
组成的数组~Type
getRawType();~ 返回的是泛型擦除后的类型,比如上面的 List<String>
会返回~List~ ,
Type getOwnerType();
一般返回的是类的 Owner
,比如声明为A.B,则此处返回为A例如:
public static class LIST<T extends View & Comparable & Cloneable> extends ArrayList<T> { private T key; private OBJ<T>[] array = new OBJ[0]; } // public static class OBJ extends View implements Comparable<OBJ>, Cloneable { public OBJ(Context context) { super(context); } @Override public int compareTo(@NonNull OBJ o) { return 0; } } // private final ReflectTypeFragment.LIST<OBJ> list = new ReflectTypeFragment.LIST<>(); private void testParameterizedType() throws NoSuchFieldException { final Field field = this.getClass().getDeclaredField("list"); final Type type = field.getGenericType(); final ParameterizedType pt = (ParameterizedType) type; log(String.format("type = %s (%s)\nActualTypeArguments=%s\nOwnerType = %s", print(type), (type instanceof ParameterizedType), print(pt.getActualTypeArguments()), print(pt.getOwnerType()))); }
最后的结果为:
> type = ReflectTypeFragment$LIST<ReflectTypeFragment$OBJ>(true) > ActualTypeArguments = class ReflectTypeFragment$OBJ > OwnerType = class ReflectTypeFragment
TypeVariable
类型变量
Type[] getBounds()
, 返回的是泛型的上边界。也就是说是只能通过”extends”方式声明类型。
D getGenericDeclaration()
, 返回的是声明的此类型的地方。
String getName()
, 源码中定义泛型时的名字。
例如:
private void testVariable() throws NoSuchFieldException { final Field field = LIST.class.getDeclaredField("key"); final Type type = field.getGenericType(); final TypeVariable t = (TypeVariable) type; log(String.format("type = %s (%s)\nBounds = %s\nGenericDeclaration = %s\nName = %s", print(type), (type instanceof TypeVariable), print(t.getBounds()), t.getGenericDeclaration(), t.getName())); }
> type = T (true) > Bounds = class android.view.View, interface java.lang.Comparable, interface java.lang.Cloneable > GenericDeclaration = class ReflectTypeFragment$LIST > Name = T > GenericArrayType
数组类型。
需要注意的是,只能是 TypeVariable
或者是 ParameterizedType
的数组才能称得上是数
组类型, 比如 String[]
, List
都不是。
Type getGenericComponentType();
返回的是数组的类型。
private void testGenericArrayType() throws NoSuchFieldException { final Field field = LIST.class.getDeclaredField("array"); final GenericArrayType t = (GenericArrayType) field.getGenericType(); log(String.format("testGenericArrayType:\ntype = %s\ngetGenericComponentType = %s", print(t), print(t.getGenericComponentType()))); }
结果:
> type = [ GenericArrayType: ReflectTypeFragment$OBJ<T>[] ] > getGenericComponentType = [ ParameterizedType: ReflectTypeFragment$OBJ<T> ] > 可以看到t.getGenericComponentType()返回的是ParameterizedType。
WildcardType
通配符类型.
Type[] getUpperBounds()
获取到的是类型的上限,如果没有设定上限那么默认会是Object.class
Type[] getLowerBounds()
获取到的类型的下限,如果没通过super设定那么默认为null。
例如:
private final Map<? super View, ? extends View> map = new HashMap<>(); private void testWildcardType() throws NoSuchFieldException { final Field field = this.getClass().getDeclaredField("map"); final ParameterizedType pt = (ParameterizedType) field.getGenericType(); for (Type type : pt.getActualTypeArguments()) { final WildcardType t = (WildcardType) type; log(String.format("testWildcardType:\ntype = %s\ngetUpperBounds = %s\ngetLowerBounds = %s", print(t), print(t.getUpperBounds()), print(t.getLowerBounds()))); } }
打印结果如下:
> testWildcardType: > type = [ ? super android.view.View ] > getUpperBounds = [ class java.lang.Object ] > getLowerBounds = [ class android.view.View ] > testWildcardType: > type = [ ? extends android.view.View ] > getUpperBounds = [ class android.view.View ] > getLowerBounds = null
在这里使用了一个 Map,map 对象是一个 ParameterizedType,然后通过其 getActualTypeArguments,获取里面的多个 Parameter。之后每个 Parameter 都是通配符。