版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lastsweetop/article/details/82993270
类型推断是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());