• Java高级特性-泛型类型推断


    Java高级特性-泛型类型推断

    Java泛型类型推导是Java 7中引入的一种新特性,指 Java 编译器根据上下文推断出泛型类型参数的类型。

    类型推导的目的

    类型推导的目的是简化泛型编程,减少代码中的冗余。在 Java 7 之前,需要在定义泛型类型或调用泛型方法时显式指定泛型类型。使用类型推导后,可以让编译器根据上下文推断出泛型类型的实际类型,从而简化代码。

    Java 7 以前的要创建一个列表类型:

    List<String> list = new ArrayList<String>();
    
    • 1

    在有了类型推到后,上面的代码可以改写为:

    List<String> list = new ArrayList<>();
    
    • 1

    类型推导和泛型方法

    类型推导使得一个泛型方法的调用就像普通方法调用一样,可以省略尖括号(<>)。

    public class BoxDemo {
    
      public static <U> void addBox(U u, java.util.List<Box<U>> boxes) {
        Box<U> box = new Box<>();
        box.set(u);
        boxes.add(box);
      }
    
      public static <U> void outputBoxes(java.util.List<Box<U>> boxes) {
        int counter = 0;
        for (Box<U> box: boxes) {
          U boxContents = box.get();
          System.out.println("Box #" + counter + " contains [" +
                 boxContents.toString() + "]");
          counter++;
        }
      }
    
      public static void main(String[] args) {
        java.util.ArrayList<Box<Integer>> listOfIntegerBoxes =
          new java.util.ArrayList<>();
        BoxDemo.<Integer>addBox(Integer.valueOf(10), listOfIntegerBoxes);
        BoxDemo.addBox(Integer.valueOf(20), listOfIntegerBoxes);
        BoxDemo.addBox(Integer.valueOf(30), listOfIntegerBoxes);
        BoxDemo.outputBoxes(listOfIntegerBoxes);
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    上述代码中,BoxDemo.addBox(Integer.valueOf(10), listOfIntegerBoxes);调用泛型方法时显示指定了类型参数为 Integer。紧接着的下面2行代码并没有显示指定类型参数类型,但不影响执行结果,因为在方法调用时,Java编译器使用类型推导根据参数类型可以指导泛型方法的类型参数的类型。

    泛型类的类型推导和实例化

    泛型类的推导规则

    • 如果泛型类只有一个泛型类型参数,则编译器会将该参数的类型作为泛型类型的实际类型。
    • 如果泛型类有多个泛型类型参数,则编译器会根据参数之间的关系推导出泛型类型的实际类型。
    1. 泛型类只有一个泛型类型参数,则编译器会将该参数的类型作为泛型类型的实际类型。

      public class MyClass {
        public void doSomething(T t) {
          System.out.println(t);
        }
      }
      
      public class Main {
        public static void main(String[] args) {
          MyClass myClass = new MyClass<>();
          myClass.doSomething(10); // 编译器推导出泛型类型的实际类型是Integer
      
          // 使用类型推导
          MyClass myClass2 = new MyClass();
          myClass2.doSomething("Hello, world!"); // 编译器推导出泛型类型的实际类型是String
        }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16

      这个示例代码中,myClass实例创建时指定了类型参数是Integer,编译器推导出泛型类型参数的实际类型是IntegermyClass2示例的创建会引发Java编译器报错,因为语句MyClass myClass2 = new MyClass();使用的是原始类型,未指定明确的类型,因此泛型类MyClass中类型参数 T 在编译时变为 Object 被对待,在语句myClass2.doSomething(“Hello, world!”);进行方法调用时,传入的参数类型是String,编译时给出的信息有所不同。

      在IDEA中给出的是 unchecked call 警告。

      Unchecked call to 'doSomething(T)' as a member of raw type '...MyClass'
      
      • 1

      在vscode提示类型安全提示。

      Type safety: The method doSomething(Object) belongs to the raw type MyClass. References to generic type MyClass<T> should be parameterized
      
      • 1
    2. 泛型类有多个泛型类型参数,则编译器会根据参数之间的关系推断出泛型类型的实际类型。

      public class MyClass<T, U> {
        public void doSomething(T t, U u) {
          // 编译器会根据参数之间的关系推断出泛型类型的实际类型
          System.out.println(t);
          System.out.println(u);
        }
      }
      
      public class Main {
        public static void main(String[] args) {
          MyClass<Integer, String> myClass = new MyClass<>();
          myClass.doSomething(10, "Hello, world!"); // 编译器推导出泛型类型的实际类型是Integer和String
      
          // 使用类型推导
          MyClass myClass2 = new MyClass();
          myClass2.doSomething(10, "Hello, world!"); // 编译器推导出泛型类型的实际类型是Integer和String
        }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18

    类型推导有以下几个注意事项:

    • 类型推导只能在编译时进行,不能在运行时进行。
    • 类型推导不能推断出原始类型。
    • 类型推导不能推断出通配符类型。(通配符后面文章讲述)

    目标类型

    Java 编译器利用目标类型在泛型方法调用时推导类型参数。拿Collections.emptyList()方法举例,它的声明

    static <T> List<T> emptyList();
    
    • 1

    使用这个方法来进行赋值:

    List<String> listOne = Collections.emptyList();
    
    • 1

    这面这个赋值语句中期望的类型是List,而Collections.emptyList()方法返回的数据类型是List,这里Java编译器会推导类型TString。这在Java 7和Java 8中都可以通过编译。或者使用显示的类型指名需要的类型。

    List<String> listOne = Collections.<String>emptyList();
    
    • 1

    但不是所有情况下都能通过编译。

    void processStringList(List<String> list) {
        // ...
    }
    
    • 1
    • 2
    • 3

    在Java 7中,向方法中传入Collections.emptyList()就无法通过编译。

    提示的错误信息类似:List cannot be converted to List。因为Collections.emptyList()方法返回的数据类型List不是List的子类型,无法转换。因此Java 7中使用上面的方法时需要显示执行泛型类型。

    这种情况,在Java 8中不再需要显示指定泛型类型。

  • 相关阅读:
    JavaScript基础 JavaScript第一天 4. 类型转换
    php 实现websocket服务
    python--- MySQL字段约束条件,外键约束条件,表关系
    ssl证书申请流程
    Linux之分区【详细总结】
    SDL2 简单介绍以及Windows开发环境搭建
    JS-项目实战-点击水果名修改特定水果库存记录
    2023年个人怎么报考湖北建筑安全员C2C3证-叙后尘
    golang报错fatal error: all goroutines are asleep - deadlock
    MindSponge分子动力学模拟——自定义控制器(2024.05)
  • 原文地址:https://blog.csdn.net/snowgeneral/article/details/133656821