如果嵌套方法参数不是类型安全的,则 Java 泛型会推断嵌套方法调用的对象而不是 T



今天我遇到了关于泛型类型推断的奇怪javac行为。下面是说明这种奇怪行为的示例类:

import java.util.Map;
import java.util.Collections;
import java.util.HashMap;
public class Test {
    protected <T> T strange(T t, Map<String, String> map) {
        return t;
    }
    protected void ok(Map<String, String> map) {}
    protected <T> T test(T t) {
        T res = strange(t , new HashMap<String, String>());
        //Doesn't work
        //res = strange(t, new <String, String>HashMap());
        ok(new <String, String>HashMap());
        res = strange(t, Collections.<String, String>emptyMap());
        //Doesn't work
        //res = strange(t, Collections.EMPTY_MAP);
        res = strange(t, (Map<String, String>) Collections.EMPTY_MAP);
        ok(Collections.EMPTY_MAP);
        return res;
    }
}

注意//Doesn't work评论。如果你取消注释这段代码,你会得到奇怪的编译器错误:

Test.java:18: error: incompatible types
        res = strange(t, Collections.EMPTY_MAP);
                     ^
  required: T
  found:    Object
  where T is a type-variable:
    T extends Object declared in method <T>test(T)

奇怪的是错误抱怨strange方法返回类型是 Object 而不是 T,但是当第二个不安全参数被强制转换为正确的类型时,T 被正确推断。

有人可以解释这是否是正确的行为吗?因为对我来说看起来很奇怪。它可以是编译器错误吗?

为什么这条线有效

T res = strange(t , new HashMap<String, String>());

而这个不是吗?

T res = strange(t, new <String, String>HashMap());

我已经用java 7和6测试了它。

根本原因是使用原始类型。

当你这样做时

new <String, String>HashMap()

类型参数<String, String>是构造函数类型参数,而不是类参数。你可以做得很好

new <String, String, String, String, Integer, Foo, Bar, String>HashMap()

因为HashMap构造函数不声明任何类型参数。

但是,由于尚未提供类类型参数,因此有效地使用了原始类型。Java 语言规范对原始类型是这样说

构造函数的类型 (§8.8)、实例方法的类型 (§8.4, §9.4) 或 非静态字段 (§8.3) 未继承自的原始类型 C 的 M 它的超类或超接口是对应的原始类型 在对应于 三.

基本思想是,如果将原始类型与某些泛型方法一起使用,则会擦除该方法的泛型类型。所以使用

res = strange(t, new <String, String>HashMap());

使您的方法看起来像

protected Object strange(Object t, Map map) {
    return t;
}

到编译器。

而你正在努力做

/* T */ res = strange(t, new <String, String>HashMap());

不能将Object分配给类型 T 的引用,因为您不知道T是什么。因此,编译器会引发异常。

相关

  • 组合原始类型和泛型方法
  • 具有独立于泛型类型的泛型方法的原始类型
  • 泛型类中的 Java 泛型方法

最新更新