【问题标题】:Java generics infers Object for nested method call instead of T if nested method argument is not typesafe如果嵌套方法参数不是类型安全的,Java 泛型推断嵌套方法调用的对象而不是 T
【发布时间】:2013-12-18 22:13:18
【问题描述】:

今天我遇到了关于泛型类型推断的奇怪 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 cmets。如果你取消注释这段代码,你会得到奇怪的编译器错误:

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 对其进行了测试。

【问题讨论】:

  • Java 有语法规则。这个new HashMap&lt;String, String&gt;());new &lt;String, String&gt;HashMap()); 完全不同。
  • 有什么区别?
  • 哦,我现在明白了。它在您的comment 中进行了描述。但这并不能回答我关于推断 Object 而不是 T 的问题的原始问题
  • 这不是唯一的原因。我会尽快写点东西。

标签: java generics type-inference inferred-type


【解决方案1】:

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

当你这样做时

new <String, String>HashMap()

类型参数&lt;String, String&gt;constructor type arguments,而不是类参数。你可以做得很好

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

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

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

构造函数的类型(第 8.8 节)、实例方法(第 8.4 节、第 9.4 节)或 非继承自原始类型 C 的非静态字段(第 8.3 节)M 它的超类或超接口是对应的原始类型 在对应的泛型声明中擦除其类型 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());

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

相关

【讨论】:

  • 很好的答案。我同意new &lt;String, String&gt;HashMap() 的解释。但是对于我来说,关于返回类型推断,与推断类型 T 无关的 Map 的单个原始参数导致编译器认为该方法将返回 Object 而不是 T
  • @Antanas 经验法则:每当涉及原始类型时,都会删除每个泛型类型。
猜你喜欢
  • 1970-01-01
  • 2011-04-27
  • 1970-01-01
  • 1970-01-01
  • 2020-08-15
  • 1970-01-01
  • 2013-03-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多