• Java泛型理解


    java泛型

    通俗的讲,泛型就是操作类型的占位符

    一、通常泛型的写法

     

    1.1定义泛型类

    public class ApiResult<T>{

        int resultCode;

        String resultMsg;

        T resultObject;

    }

    1.2定义泛型方法

     

    public JsonUtil{

        public <T> T str2Json(String jsonText, Class target){

            T result = null;

            ...

     

            return result;

        }

    }

     

    1.3 K,V类型

    public class ResultMap<K,V>{

        private K key;

        private V value;

     

        // 省略set,get方法

     

     

        public void put(K key, V value){

            this.key = key;

            this.value = value;

        }

    }

     

     

    二、使用泛型类和方法

    ApiResult<User> result = new ApiResult<User>();

    result.setResultCode(0);

    result.setResultMsg("success");

    result.setResultObject(new User());

     

    String userJsonText = "userJsonText";

    User u = JsonUtil.str2Json(jsonText, User.class);

     

     

    ResultMap<String,User> resultMap = new ResultMap<String,User>();

    resultMap.put("currentUserKey", new User());

     

     

    三、类型擦除

    public class Operate{

        public static void main(String[] args){

            List<String> names = new ArrayList<String>();

            names.add("Jack");

            names.add("Tom");

            for(String name:names){

                System.out.println(name);

            }

        }

    }

    其对应的class文件反汇编以后,我们使用java-gui反编译.exe  查看编译之后的代码如下:

     

    发现没有,根本没有<String> 这一部分了。这个限制为String类型的泛型被“擦除”了。写代码的时候,泛型会做校验,类型不对应的,无法add,但是编译之后边去掉了泛型类型。

    四、为什么要使用java泛型??

    这里的泛型就相当于“约法三章”,先给你定好“规矩”,我这个List<String> 就是用来操作String类型的,你插入Person对象就不行。说白了就是为了类型安全。所以其好处有:

    4.1类型安全

    通过知道使用泛型定义的变量的类型限制,编译器可以在一个高得多的程度上验证类型假设。没有泛型,这些假设就只存在于程序员的头脑中(或者如果幸运的话,还存在于代码注释中)。

    4.2消除强制类型转换

    //该代码不使用泛型:

    List li = new ArrayList();

    li.put(new Integer(3));

    Integer i = (Integer) li.get(0);

     

     

    //该代码使用泛型:

    List<Integer> li = new ArrayList<Integer>();

    li.put(new Integer(3));

    Integer i = li.get(0);

     


     

     

    <? extends T><? super T>

     

    public class Fruit {

     

    }

     

    public class Apple extends Fruit {

     

    }

     

    public class Plate<T> {

           private T item;

           public Plate(T t){

                  item = t;

           }

           public T getItem() {

                  return item;

           }

           public void setItem(T item) {

                  this.item = item;

           }

          

    }

     

    public class TestGenericParadigm {

          

           public static void main(String[] args){

                 

                 

                  Plate<Fruit> p = new Plate<Apple>(new Apple()); // 编译不通过

                  Plate<Fruit> p = new Plate<Fruit>(new Fruit());

           }

    }

    为什么上面这个会出现编译不通过??

    解释:苹果是水果,但是装苹果的盘子不是装水果的盘子

    所以,就算容器里装的东西之间有继承关系,但容器之间是没有继承关系的

     

    为了让泛型用起来更舒服,Sun的大脑袋们就想出了<? extends T>和<? super T>的办法,来让”水果盘子“和”苹果盘子“之间发生关系

     

    什么是上界?

     

    Plate<? extends Fruit>

    翻译成人话就是:一个能放水果以及一切是水果派生类的盘子。再直白点就是:啥水果都能放的盘子。这和我们人类的逻辑就比较接近了。Plate<? extends Fruit>和Plate<Apple>最大的区别就是:Plate<? extends Fruit>是Plate<Fruit>以及Plate<Apple>的基类。直接的好处就是,我们可以用“苹果盘子”给“水果盘子”赋值了。

    Plate<? extends Fruit> p=new Plate<Apple>(new Apple());

     

    如果把Fruit和Apple的例子再扩展一下

    //Lev 1

    class Food{}

     

    //Lev 2

    class Fruit extends Food{}

    class Meat extends Food{}

     

    //Lev 3

    class Apple extends Fruit{}

    class Banana extends Fruit{}

    class Pork extends Meat{}

    class Beef extends Meat{}

     

    //Lev 4

    class RedApple extends Apple{}

    class GreenApple extends Apple{}

    在这个体系中,上界通配符 Plate<? extends Fruit> 覆盖下图中蓝色的区域

     

    什么是下界?

     

    Plate<? super Fruit>

    表达的就是相反的概念:一个能放水果以及一切是水果基类的盘子。Plate<? super Fruit>是Plate<Fruit>的基类,但不是Plate<Apple>的基类。对应刚才那个例子,Plate<? super Fruit>覆盖下图中红色的区域。

     

     

     

    上下界通配符的副作用

     

    边界让Java不同泛型之间的转换更容易了。但不要忘记,这样的转换也有一定的副作用。那就是容器的部分功能可能失效。

    还是以刚才的Plate为例。我们可以对盘子做两件事,往盘子里set()新东西,以及从盘子里get()东西。

     

    public class TestGenericParadigm {

          

           public static void main(String[] args){

                  /*

                   * 上界通配符

                   * 不能存入任何元素

                   * 读出来的东西只能放在Fruit或它的基类中

                   */

                 

                  Plate<? extends Fruit> p = new Plate<Apple>(new Apple());

     

                  p.setItem(new Fruit()); //Error

                  p.setItem(new Apple()); //Error

                 

                  Fruit newFruit1 = p.getItem();

                  Object newFruit2 = p.getItem();

                  Apple new Fruit3 = p.getItem();//Error

                 

           }

    }

     

    原因是编译器只知道容器内是Fruit或者它的派生类,但具体是什么类型不知道

    所以通配符<?>和类型参数的区别就在于,对编译器来说所有的T都代表同一种类型。比如下面这个泛型方法里,三个T都指代同一个类型,要么都是String,要么都是Integer。

    但通配符<?>没有这种约束,Plate<?>单纯的就表示:盘子里放了一个东西,是什么我不知道

     

    下界<? super T>不影响往里存,但往外取只能放在Object对象里

    使用下界<? super Fruit>会使从盘子里取东西的get( )方法部分失效,只能存放到Object对象里。set( )方法正常。

                  /*

                   * 下界通配符

                   * 存入元素正常

                   * 读取的东西只能存放在Object中

                   */

                  Plate<? super Fruit> p1 = new Plate<Fruit>(new Fruit());

                 

                  p1.setItem(new Fruit());

                  p1.setItem(new Apple());

                 

                  Apple newFruit4 = p1.getItem();//Error

                  Fruit newFruit5 = p1.getItem();//Error

                  Object newFruit6 = p1.getItem();

    因为下界规定了元素的最小粒度的下限,实际上是放松了容器元素的类型控制。既然元素是Fruit的基类,那往里存粒度比Fruit小的都可以。但往外读取元素就费劲了,只有所有类的基类Object对象才能装下。但这样的话,元素的类型信息就全部丢失

     

    PECS原则

    最后看一下什么是PECS(Producer Extends Consumer Super)原则,已经很好理解了:

    频繁往外读取内容的,适合用上界Extends。

    经常往里插入的,适合用下界Super。

     

     

  • 相关阅读:
    WinForm中AssemblyInfo.cs文件参数具体讲解
    ISO18000-6B和ISO18000-6C(EPC C1G2)标准的区别
    Win8.1下VM与Hyper-v冲突解决方法
    Mifare l卡特性简介
    【Python爬虫】beautifulsoup4库的安装与调用
    【Python爬虫】已知jpg文件url-下载图片
    【Python爬虫】测试
    【Python爬虫】Requests库的安装
    【Python】以模块化做比赛模拟
    【真随笔】未来出路,在哪里?
  • 原文地址:https://www.cnblogs.com/wylwyl/p/10234096.html
Copyright © 2020-2023  润新知