java泛型类型推断_Java 泛型-类型推断

类型推断(Type Inference)是指 Java 编译器能查看每个方法的调用和相应声明,以确定调用合适的类型参数(Type Argument)或参数。推断算法决定参数的类型,如果可用,则指定被赋值的类型或返回的类型。最后,推断算法试图在一起工作的所有参数中找到最具体的类型。

为了说明最后一点,在下面的例子中,泛型推断确定将传入 pick 方法的第二个参数作为 Serializable 的类型:

1

2

3

4

5static T pick(T a1, T a2){

return a2;

}

Serializable s = pick("d", new ArrayList());

类型推断和泛型方法

引入类型推断的泛型方法,能够让你像调用普通方法一样调用泛型方法,而不需要在尖括号中指定类型。思考下面的例子,BoxDemo,它需要 Box 类。

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

28public class BoxDemo{

public static void addBox(U u,

java.util.List> boxes){

Box box = new Box<>();

box.set(u);

boxes.add(box);

}

public static void outputBoxes(java.util.List> boxes){

int counter = 0;

for (Box box: boxes) {

U boxContents = box.get();

System.out.println("Box #" + counter + " contains [" +

boxContents.toString() + "]");

counter++;

}

}

public static void main(String[] args){

java.util.ArrayList> listOfIntegerBoxes =

new java.util.ArrayList<>();

BoxDemo.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 的类型参数。一般 Java 编译器能推断泛型方法调用的类型参数。因此,大多数情况下,你不需要指定类型参数。例如,为了调用泛型方法 addBox,你可以使用类型证据指定一个类型参数:

1BoxDemo.addBox(Integer.valueOf(10), listOfIntegerBoxes);

或者,你可以省略类型证据,Java 编译器能从传入泛型方法的参数中自动推断出类型参数为 Integer:

1BoxDemo.addBox(Integer.valueOf(20), listOfIntegerBoxes);

泛型类的实例化

只要编译器能从上下文中推断出类型参数,我们就可以使用空的类型参数(棱形)来替换类型参数去调用泛型类的构造方法。

例如,思考下面的变量声明:

1Map> myMap = new HashMap>();

我们可以使用空的类型参数(棱形)来代替构造方法的参数化类型:

1Map> myMap = new HashMap<>();

注意,为了在泛型类实例化过程中使用类型推断,你必须使用棱形。下面的例子中,因为 HashMap() 的构造方法引用了 HashMap 原始类型而不是 HashMap>,所以编译器生成一个 unchecked conversion 的警告。

1Map> myMap = new HashMap(); // unchecked conversion warning

泛型构造方法

注意,泛型类或非泛型类的构造方法可以是泛化的(换句话说是声明它们自己的类型参数)。思考下面的例子:

1

2

3

4

5class MyClass{

MyClass(T t) {

// ...

}

}

思考下面 MyClass 类的实例:

1new MyClass("")

这个语句创建一个参数化类型为 MyClass 的实例,也明确指定 MyClass 中类型参数 X 是 Integer 类型。注意,这个泛型类的构造方法包含一个类型参数 T。编译器推断出这个泛型类的构造方法的类型参数 T 是 String 类型,因为构造器的实际参数是一个 String 对象。

在 Java SE 7 之前发布的编译器能够推断泛型构造方法的实际类型参数,类似于泛型方法。但是,Java SE 7 和后面的编译器可以推断出使用了棱形的泛型类的实际类型参数:

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

在这个例子中,编译器能推断出泛型类 MyClass 中 X 是 Integer 类型,泛型构造方法中 T 是 String 类型。

需要注意的是,类型推断算法仅适用调用参数、目标类型,以及可能明显的预期返回类型来推断类型。类型推断算法不使用后面程序中的结果。

目标类型

Java 编译器利用目标类型来推断出泛型方法调用的类型参数。表达式的目标类型是 Java 编译器所期望的数据类型,这取决于表达式出现的位置。思考下面声明的 Collections.emptyList 方法:

1static List emptyList();

思考下面的赋值语句:

1List listOne = Collections.emptyList();

这个语句期望得到一个 List 实例,List 是目标类型。因为 emptyList 方法返回一个 List 类型的值,编译器推断出类型参数 T 一定是一个 String。这在 Java SE 7 或 8 中都有效。或者我们可以使用类型证据并指定 T 的值如下:

1List listOne = Collections.emptyList();

在这个上下文中,我们并不需要这么做。不过在其他上下文中是需要的,思考下面的方法:

1

2

3void processStringList(List stringList){

// process stringList

}

假设你想使用一个 emptyList 来调用 processStringList 方法,在 Java SE 7 中,下面的语句是编译不通过的:

1processStringList(Collections.emptyList());

Java SE 7 编译会生成一个错误类似如下:

1List cannot be converted to List

编译器需要类型参数 T 的值,所以 T 从 Object 开始。因此,调用 Collections.emptyList 会返回 List,这个与 processStringList 方法不兼容。所以在 Java SE 7 中,我们必须制定类型参数:

1processStringList(Collections.emptyList());

在 Java SE 8 中已经不需要这样。例如 processStringList 方法的参数,目标类型已经被扩展到包含方法参数。在这种情况下,processStringList 方法需要一个 List 类型的参数,Collections.emptyList 方法返回一个 List 类型的值,所以使用 List 的目标类型,编译器推断类型参数 T 具有一个 String 的值。所以在 Java SE 8 中,下面的语句能编译通过:

1processStringList(Collections.emptyList());

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值