美文网首页
泛型学习(二)

泛型学习(二)

作者: yanlong107 | 来源:发表于2020-02-26 08:06 被阅读0次

    上一章节对泛型有了一个基本的认识,这里进行一深入的学习泛型。

    先我们定义A、B、C、D四个类,他们的关系如下

    class A {}
    class B extends A {}
    class C extends B {}
    class D extends C {}
    

    上边界符 ?extends

    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()); //编译不通过
    

    tips:

    • 上边界符 ? extends 只是限定了赋值给它的实例类型(这里为赋值给listC的实例类型),且边界包括自身。
    • 上边界符 ? 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()); //编译通过
    

    tips:

    • 下边界符 ?super,跟上边界符一样,只是限定了赋值给它的实例类型,也包括边界自身
    • 下边界符 ?super 能存能取,因为设定了下边界,故我们能存下边界以下的类型,当然也包括边界自身;然而取得时候编译器依然不知道 ? 具体是什么类型,故取出默认为Object类型。

    类型擦除

    需要了解的是 Java 的泛型在编译期有效,在运行期会被删除 。

    public class Demo {
      //这两个方法写在同一个类里
      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
    翻译过来就是,在类型擦除后,两个方法具有相同的签名, 代码如下:

    public class Demo {
      //这两个方法写在同一个类里
      public void list(List listA) {}  
      public void list(List listB) {} 
    }
    

    这样可以很清楚的看到,两个方法的签名是完全一样了,所以编译不通过。

    tips:

    • 泛型类并没有自己独有的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);
    

    正确的写法应该如下:

    //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类型,其中type 的定义是个接口
    public static <T> T fromJson(String result, Type type) {
        try {
            return new Gson().fromJson(result, type);
        } catch (Exception ignore) {
            return null;
        }
    }
    
    
    public interface Type {
        default String getTypeName() {
            return toString();
        }
    }
    

    type 接口的实现类如下:有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;
        }
    }
    
    

    之后的应用如下:

    //这里我们直接传递一个Type类型
    public static <T> T fromJson(String result, Type type) {
        try {
            return new Gson().fromJson(result, type);
        } catch (Exception ignore) {
            return null;
        }
    }
    
    
    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));
    
    

    然而,如果我们想得到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);
    

    参考:
    Android、Java泛型扫盲

    相关文章

      网友评论

          本文标题:泛型学习(二)

          本文链接:https://www.haomeiwen.com/subject/ultqchtx.html