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

标签 java generics type-inference inferred-type

今天我遇到了关于泛型类型推断的奇怪 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>constructor type arguments ,而不是类参数。你可以很好地做到

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

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

但是,由于您没有提供类类型参数,因此您实际上正在使用原始类型。 Java Language Specification says this about raw types

The type of a constructor (§8.8), instance method (§8.4, §9.4), or non-static field (§8.3) M of a raw type C that is not inherited from its superclasses or superinterfaces is the raw type that corresponds to the erasure of its type in the generic declaration corresponding to C.

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

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

使你的方法看起来像

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

到编译器。

你正在尝试做

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

您无法分配 ObjectT 类型的引用因为你不知道什么T是。因此编译器会抛出异常。

相关

关于如果嵌套方法参数不是类型安全的,Java 泛型会推断嵌套方法调用的对象而不是 T,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20669543/

相关文章:

haskell - 为什么读取不能推导出正确的类型?

java - java中线程没有名字的影响

java - 更正 ClassToInstanceMap 的泛型类型

java - 带有类型删除的方法重载

java - 泛型类的类 <T> 的名称

c++ - 构造函数的模板参数推导

TypeScript:Array.find 实际上可能未定义,但它作为固定类型返回

java - 定期调用Web服务

java - 目标列表迭代映射结构

java - 改造 - Okhttp 客户端如何缓存响应