Android、Java泛型扫盲

首先咱们定义A、B、C、D四个类,他们的关系以下java

class A {}
class B extends A {}
class C extends B {}
class D extends C {}
复制代码

不指明泛型类型

//如下代码均编译经过
List list = new ArrayList();
//不指明泛型类型,泛型默认为Object类型,故能往里面添加任意实例对象
list.add(new A());
list.add(new B());
list.add(new C());
//取出则默认为Object类型
Object o = list.get(0);
复制代码

这个好理解,由于全部的类都继承与Object,故能往list里面添加任意实例对象数组

无边界通配符

首先咱们要明白一个概念,通配符意义就是它是一个未知的符号,能够是表明任意的类。数据结构

//咱们发现,这样写编译不经过,缘由很简单,泛型不匹配,虽然B继承A
List<A> listA = new ArrayList<B>(); 

//如下5行代码均编译经过
List<?> list;
list = new ArrayList<A>();
list = new ArrayList<B>();
list = new ArrayList<C>();
list = new ArrayList<D>();

Object o = list.get(0); //编译经过
list.add(new A());      //编译不经过
list.add(new B());      //编译不经过
list.add(new C());      //编译不经过
list.add(new D());      //编译不经过
复制代码

知识点post

  • 无边界通配符 能取不能存。这个好理解,由于编译器不知道?具体是啥类型,故不能存;可是任意类型都继承于Object,故能取,但取出默认为Object对象。

上边界符 ?extends

继续上代码this

List<? extends C> listC;
listC = new ArrayList<A>(); //编译不经过
listC = new ArrayList<B>(); //编译不经过
listC = new ArrayList<C>(); //编译经过
listC = new ArrayList<D>(); //编译经过

C c = listC.get(0); //编译经过
listC.add(new C()); //编译不经过
listC.add(new D()); //编译不经过
复制代码

知识点:spa

  1. 上边界符 ? extends 只是限定了赋值给它的实例类型(这里为赋值给listC的实例类型),且边界包括自身。
  2. 上边界符 ? extends 同样能取不能存,道理是同样的,虽然限定了上边界,但编译器依然不知道 ? 是啥类型,故不能存;可是限定了上边界,故取出来的对象类型默认为上边界的类型

下边界符 ?super

List<? super B> listB;
listB = new ArrayList<A>(); //编译经过
listB = new ArrayList<B>(); //编译经过
listB = new ArrayList<C>(); //编译不经过
listB = new ArrayList<D>(); //编译不经过

Object o = listB.get(0); //编译经过
listB.add(new A()); //编译不经过
listB.add(new B()); //编译经过
listB.add(new C()); //编译经过
listB.add(new D()); //编译经过
复制代码

知识点翻译

  1. 下边界符 ?super,跟上边界符同样,只是限定了赋值给它的实例类型,也包括边界自身
  2. 下边界符 ?super 能存能取,由于设定了下边界,故咱们能存下边界如下的类型,固然也包括边界自身;然而取得时候编译器依然不知道 ? 具体是什么类型,故取出默认为Object类型。

类型擦除

首先咱们要明白一点:Java 的泛型在编译期有效,在运行期会被删除 咱们来看一段代码code

//这两个方法写在同一个类里
public void list(List<A> listA) {}  
public void list(List<B> listB) {} 
复制代码

上面的代码会有问题吗?显然是有的,编译器报错,提示以下信息: list(List<A>) clashed with list(List<B>) ; both methods have same erasure 翻译过来就是,在类型擦除后,两个方法具备相同的签名,咱们来看看类型擦除后是什么样子cdn

public void list(List listA) {}  
public void list(List listB) {} 
复制代码

能够看出,两个方法签名彻底一致,故编译不经过。 明白了类型擦除,咱们还须要明白一个概念对象

  • 泛型类并无本身独有的Class类对象

好比并不存在List<A>.class或是List<B>.class,而只有List.class 接下来这个案例就好理解了

List<A> listA = new ArrayList<A>();
List<B> listB = new ArrayList<B>();
System.out.println(listA.getClass() == listB.getClass());  //输出true
复制代码

泛型传递

现实开发中,咱们常常会用到泛型传递,例如咱们常常须要对Http请求返回的结果作反序列化操做

public static <T> T fromJson(String result, Class<T> type) {
    try {
        return new Gson().fromJson(result, type);
    } catch (Exception ignore) {
        return null;
    }
}
复制代码

此时咱们传进去是什么类型,就会返回自动该类型的对象

String result="xxx";
A a = fromJson(result, A.class);
B b = fromJson(result, B.class);
C c = fromJson(result, C.class);
D d = fromJson(result, D.class);
Integer integer = fromJson(result, Integer.class);
String str = fromJson(result, String.class);
Boolean boo = fromJson(result, Boolean.class);
复制代码

那若是咱们想返回一个集合呢,如List<A>,下面这样明显是不对的。

//编译报错,前面类型擦除时,咱们讲过,不存List<A>.class这种类型
ArrayList<A> list = fromJson(result, ArrayList<A>.class);
复制代码

那咱们该怎么作呢?首先,咱们对fromJson改造一下,以下:

//type为一个数组类型
public static <T> List<T> fromJson(String result, Class<T[]> type) {
    try {
        T[] arr = new Gson().fromJson(result, type);//首先拿到数组
        return Arrays.asList(arr); //数组转集合
    } catch (Exception ignore) {
        return null;
    }
}
复制代码

这个时候咱们就能够这么作了

String result="xxx";
List<A> listA = fromJson(result, A[].class);
List<B> listB = fromJson(result, B[].class);
List<C> listC = fromJson(result, C[].class);
List<D> listD = fromJson(result, D[].class);
List<Integer> listInt = fromJson(result, Integer[].class);
List<String> listStr = fromJson(result, String[].class);
List<Boolean> listBoo = fromJson(result, Boolean[].class);
复制代码

ok,我在再来,相信大多数Http接口返回的数据格式是这样的:

public class Response<T> {
    private T data;
    private int code;
    private String msg;
    //省略get/set方法
}
复制代码

那这种咱们又该如何传递呢?显然用前面的两个fromJson方法都行不通,咱们再来改造一下,以下:

//这里咱们直接传递一个Type类型
public static <T> T fromJson(String result, Type type) {
    try {
        return new Gson().fromJson(result, type);
    } catch (Exception ignore) {
        return null;
    }
}
复制代码

这个Type是什么鬼?点进去看看

public interface Type {
    default String getTypeName() {
        return toString();
    }
}
复制代码

哦,原来就是一个接口,而且只有一个方法,咱们再来看看它的实现类

在这里插入图片描述
发现有5个实现类,其中4个是接口,另一个是Class类,咱们再来看看Class类的声明

public final class Class<T> implements java.io.Serializable, GenericDeclaration, Type, AnnotatedElement {
                              //省略内部代码
}
复制代码

如今有没有明白点,如今咱们重点来关注下Type接口的其中一个实现接口ParameterizedType,咱们来看下它的内部代码,里面就只有3个方法

public interface ParameterizedType extends Type {
    /** * 例如: * List<String> list; 则返回 {String.class} * Map<String,Long> map; 则返回 {String.class,Long.class} * Map.Entry<String,Long> entry; 则返回 {String.class,Long.class} * * @return 以数组的形式返回全部的泛型类型 */
    Type[] getActualTypeArguments();

    /** * 例如: * List<String> list; 则返回 List.class * Map<String,Long> map; 则返回 Map.class * Map.Entry<String,Long> entry; 则返回 Entry.class * * @return 返回泛型类的真实类型 */
    Type getRawType();

    /** * 例如: * List<String> list; 则返回 null * Map<String,Long> map; 则返回 null * Map.Entry<String,Long> entry; 则返回 Map.class * * @return 返回泛型类持有者的类型,这里能够简单理解为返回外部类的类型,若是没有外部类,则返回null */
    Type getOwnerType();
}
复制代码

顾名思义,ParameterizedType 表明一个参数化类型。

这个时候咱们来自定义一个类,并实现ParameterizedType接口,以下:

public class ParameterizedTypeImpl implements ParameterizedType {

    private Type rawType;//真实类型
    private Type actualType;//泛型类型

    public ParameterizedTypeImpl(Type rawType,Type actualType) {
        this.rawType = rawType;
        this.actualType = actualType;
    }

    public Type[] getActualTypeArguments() {
        return new Type[]{actualType};
    }

    public Type getRawType() {
        return rawType;
    }

    public Type getOwnerType() {
        return null;
    }
}
复制代码

咱们再次贴出fromJson方法

//这里咱们直接传递一个Type类型
public static <T> T fromJson(String result, Type type) {
    try {
        return new Gson().fromJson(result, type);
    } catch (Exception ignore) {
        return null;
    }
}
复制代码

此时咱们想获得Response<T>对象,就能够这样写

Response<A> responseA = fromJson(result, new ParameterizedTypeImpl(Response.class, A.class));
Response<B> responseB = fromJson(result, new ParameterizedTypeImpl(Response.class, B.class));
Response<C> responseC = fromJson(result, new ParameterizedTypeImpl(Response.class, C.class));
复制代码

想获得List<T>对象,也能够经过ParameterizedTypeImpl获得,以下:

List<A> listA = fromJson(result, new ParameterizedTypeImpl(List.class, A.class));
List<B> listB = fromJson(result, new ParameterizedTypeImpl(List.class, B.class));
List<C> listC = fromJson(result, new ParameterizedTypeImpl(List.class, C.class));
复制代码

然而,若是咱们想获得Response<List<T>>对象,又该如何获得呢? ParameterizedTypeImpl同样可以实现,以下:

//第一步,建立List<T>对象对应的Type类型
Type listAType = new ParameterizedTypeImpl(List.class, A.class);
Type listBType = new ParameterizedTypeImpl(List.class, B.class);
Type listCType = new ParameterizedTypeImpl(List.class, C.class);

//第二步,建立Response<List<T>>对象对应的Type类型
Type responseListAType = new ParameterizedTypeImpl(Response.class, listAType);
Type responseListBType = new ParameterizedTypeImpl(Response.class, listBType);
Type responseListCType = new ParameterizedTypeImpl(Response.class, listCType);

//第三步,经过Type对象,获取对应的Response<List<T>>对象
Response<List<A>> responseListA = fromJson(result, responseListAType);
Response<List<B>> responseListB = fromJson(result, responseListBType);
Response<List<C>> responseListC = fromJson(result, responseListCType);

复制代码

而后,能不能再简单一点呢?能够,咱们对ParameterizedTypeImpl改造一下

/** * User: ljx * Date: 2018/10/23 * Time: 09:36 */
public class ParameterizedTypeImpl implements ParameterizedType {

    private final Type   rawType;
    private final Type   ownerType;
    private final Type[] actualTypeArguments;

    //适用于单个泛型参数的类
    public ParameterizedTypeImpl(Type rawType, Type actualType) {
        this(null, rawType, actualType);
    }

    //适用于多个泛型参数的类
    public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... actualTypeArguments) {
        this.rawType = rawType;
        this.ownerType = ownerType;
        this.actualTypeArguments = actualTypeArguments;
    }

    /** * 本方法仅使用于单个泛型参数的类 * 根据types数组,肯定具体的泛型类型 * List<List<String>> 对应 get(List.class, List.class, String.class) * * @param types Type数组 * @return ParameterizedTypeImpl */
    public static ParameterizedTypeImpl get(@NonNull Type rawType, @NonNull Type... types) {
        final int length = types.length;
        if (length > 1) {
            Type parameterizedType = new ParameterizedTypeImpl(types[length - 2], types[length - 1]);
            Type[] newTypes = Arrays.copyOf(types, length - 1);
            newTypes[newTypes.length - 1] = parameterizedType;
            return get(rawType, newTypes);
        }
        return new ParameterizedTypeImpl(rawType, types[0]);
    }

    //适用于多个泛型参数的类
    public static ParameterizedTypeImpl getParameterized(@NonNull Type rawType, @NonNull Type... actualTypeArguments) {
        return new ParameterizedTypeImpl(null, rawType, actualTypeArguments);
    }

    public final Type[] getActualTypeArguments() {
        return actualTypeArguments;
    }

    public final Type getOwnerType() {
        return ownerType;
    }

    public final Type getRawType() {
        return rawType;
    }

}
复制代码

此时,咱们就能够这样写

//第一步,直接建立Response<List<T>>对象对应的Type类型
Type responseListAType = ParameterizedTypeImpl.get(Response.class, List.class, A.class);
Type responseListBType = ParameterizedTypeImpl.get(Response.class, List.class, B.class)
Type responseListCType = ParameterizedTypeImpl.get(Response.class, List.class, C.class)

//第二步,经过Type对象,获取对应的Response<List<T>>对象
Response<List<A>> responseListA = fromJson(result, responseListAType);
Response<List<B>> responseListB = fromJson(result, responseListBType);
Response<List<C>> responseListC = fromJson(result, responseListCType);
复制代码

现实开发中,咱们还可能遇到这样的数据结构

{
    "code": 0,
    "msg": "",
    "data": {
        "totalPage": 0,
        "list": []
    }
}
复制代码

此时,Response<T> 里面的泛型传List确定是不能正常解析的,咱们须要再定一个类

public class PageList<T>{
   private int totalPage;
   private List<T> list;
   //省略get/set方法
}
复制代码

此时就能够这样解析数据

//第一步,直接建立Response<PageList<T>>对象对应的Type类型
Type responsePageListAType = ParameterizedTypeImpl.get(Response.class, PageList.class, A.class);
Type responsePageListBType = ParameterizedTypeImpl.get(Response.class, PageList.class, B.class)
Type responsePageListCType = ParameterizedTypeImpl.get(Response.class, PageList.class, C.class)

//第二步,经过Type对象,获取对应的Response<PageList<T>>对象
Response<PageList<A>> responsePageListA = fromJson(result, responsePageListAType);
Response<PageList<B>> responsePageListB = fromJson(result, responsePageListBType);
Response<PageList<C>> responsePageListC = fromJson(result, responsePageListCType);
复制代码

注:ParameterizedTypeImpl get(Type... types)仅仅适用于单个泛型参数的时候,如Map等,有两个泛型参数以上的不要用此方法获取Type类型。若是须要获取Map等两个泛型参数以上的Type类型。可调用getParameterized(@NonNull Type rawType, @NonNull Type... actualTypeArguments)构造方法获取,如:

//获取 Map<String,String> 对应的Type类型
Type mapType = ParameterizedTypeImpl.getParameterized(Map.class, String.classs, String.class)

//获取 Map<A,B> 对应的Type类型
Type mapType = ParameterizedTypeImpl.getParameterized(Map.class, A.classs, B.class)
复制代码

到这,泛型相关知识点讲解完毕,若有疑问,请留言。

感兴趣的同窗,能够查看个人另外一片文章RxHttp 一条链发送请求,新一代Http请求神器(一))里面就用到了ParameterizedTypeImpl类进行泛型传递。

相关文章
相关标签/搜索