java遗珠之泛型类型推断

Posted 吴冬冬

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java遗珠之泛型类型推断相关的知识,希望对你有一定的参考价值。

类型推断

类型推断是java编译器的一种能力,通过查看方法调用和相应的声明来决定什么样的类型参数或者参数是更为合理的调用。

推断算法先确定参数的类型,分配结果或者返回的类型,最终推断算法查找适合所有参数最适合的类型。

为了说明这点,来看下面的例子:

public class Util 

    static <T> T pick(T a1, T a2) 
        return a2;
    

    public static void main(String[] args) 
        Serializable s = pick("d", new ArrayList<String>());
    

最终符合所有参数的类型是Serializable

泛型方法里的类型推断

泛型方法中的类型推断可以让你像调用普通方法一样来调用泛型方法,而不用指定泛型方法的类型。

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);
  

输出如下:

Box #0 contains [10]
Box #1 contains [20]
Box #2 contains [30]

泛型方法addBox的类型参数是U,编译器会推断方法调用时候的参数,因此在很多情况下你不需要指定他们。在这里例子中,你可以明确指定调用泛型方法addBox的类型参数:

BoxDemo.<Integer>addBox(Integer.valueOf(10), listOfIntegerBoxes);

或者,如果你忽略它,编译器将从方法的传入参数推断出类型参数是Integer

泛型类实例化的类型推断

编译器能从上下文推断出类型参数,那么就可以使用<>来代替泛型类构造方法的类型参数。

比如:

BoxDemo.<Integer>addBox(Integer.valueOf(10), listOfIntegerBoxes);

可以用<>来代替构造器的参数化类型。

要注意的是如果想使用类型推断的特性,你必须加上<>,如果省略就会变成原始类型,而得到unchecked的警告

Map<String, List<String>> myMap = new HashMap(); // unchecked conversion warning

泛型或非泛型类泛型构造方法的推断

要注意的是不管泛型类还是非泛型类的构造方法都可以是泛型构造方法,看下面的例子

class MyClass<X> 
    <T> MyClass(T t) 
        // ...
    

这个类的实例化是这样的:

new MyClass<Integer>("")

这条语句创建了个类型为MyClass<Integer>的实例,语句明确的指定泛型类MyClass<X>中形式类型参数X对应的类型参数为Integer。注意这个类的构造方法也有一个形式类型参数T,因为构造器的实际类型参数为String,编译器推断这个泛型类 构造器的形式类型参数为String

编译器从1.7开始可以像推断泛型方法一样可以推断泛型构造器的实际类型参数,1.7开始编译器也可以推断泛型初始化时的实际类型参数,因此就有了以下代码

MyClass<Integer> myObject = new MyClass<>("");

目标类型

java编译器还可以通过目标类型来泛型方法调用时的类型参数,表达式的目标类型是编译器期望的数据类型,具体取决于表达式出现的位置。比如方法Collections.emptyList。在源码是这样定义的

    public static final <T> List<T> emptyList() 
        return (List<T>) EMPTY_LIST;
    

再看下面的语句

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

这个语句期望返回一个List<String>实例,数据类型就是目标类型。因为emptyList方法返回List<T>类型,编译器推断T的类型参数必须是String

在看下面这个普通方法

void processStringList(List<String> stringList) 
    // process stringList

方法参数期望是List<String>,那么编译器就会推断
泛型方法emptyList放在中的类型参数必须是String

processStringList(Collections.emptyList());

以上是关于java遗珠之泛型类型推断的主要内容,如果未能解决你的问题,请参考以下文章

java遗珠之泛型七大限制

java遗珠之泛型七大限制

java遗珠之泛型的作用

java遗珠之泛型类型擦除

java遗珠之泛型类型擦除

java遗珠之泛型不可靠类型