【问题标题】:Why won't this generic java code compile?为什么这个通用的 java 代码不能编译?
【发布时间】:2009-03-19 13:27:25
【问题描述】:

在这个简化的示例中,我有一个泛型类和一个无论类型参数如何都返回 Map 的方法。为什么当我没有在包含的类上指定类型时,编译器会清除映射上的类型?

import java.util.Map;

public class MyClass<T>
{
    public Map<String, String> getMap()
    {   
        return null;
    }

    public void test()
    {   
        MyClass<Object> success = new MyClass<Object>();
        String s = success.getMap().get("");

        MyClass unchecked = new MyClass();
        Map<String, String> map = unchecked.getMap();  // Unchecked warning, why?
        String s2 = map.get("");

        MyClass fail = new MyClass();
        String s3 = fail.getMap().get("");  // Compiler error, why?
    }
}

我得到这个编译器错误。

MyClass.java:20: incompatible types
found   : java.lang.Object
required: java.lang.String
                String s3 = fail.getMap().get("");  // Compiler error

【问题讨论】:

  • 未经检查的警告的确切文本是什么?
  • 警告:[unchecked] 未经检查的转换

标签: java generics


【解决方案1】:

知道了。这实际上不是一个错误,虽然看起来很奇怪。

来自section 4.8 (raw types) of the JLS

构造函数的类型(第 8.8 节), 实例方法(第 8.8 节、第 9.4 节),或 原始的非静态字段(§8.3)M 不是从其继承的类型 C 超类或超接口是 在泛型中擦除其类型 对应于 C 的声明。 原始类型的静态成员的类型 C 与它的类型相同 对应的泛型声明 C.

因此,即使方法的类型签名不使用类本身的任何类型参数,类型擦除也会启动并且签名变得有效

public Map getMap()

换句话说,我认为您可以将原始类型想象为与泛型类型相同的 API,但所有 &lt;X&gt; 位都从 everywhere 中删除(在 API 中,而不是在实现中) .

编辑:这段代码:

MyClass unchecked = new MyClass();
Map<String, String> map = unchecked.getMap();  // Unchecked warning, why?
String s2 = map.get("");

编译是因为存在从原始Map 类型到Map&lt;String, String&gt; 的隐式但未经检查的转换。在最后一种情况下,您可以通过显式转换(在执行时不执行任何操作)来获得相同的效果:

// Compiles, but with an unchecked warning
String x = ((Map<String, String>)fail.getMap()).get("");

【讨论】:

  • 嗯……这有点道理……但同时也非常愚蠢……
【解决方案2】:

嗯...不幸的是我不能告诉你为什么它失败了。但我可以给你一个简单的解决方法:

fail的类型改成MyClass&lt;?&gt;,就可以编译好了。

【讨论】:

  • 演员阵容也很好。为什么会有一个类型?影响地图的类型?这对我来说仍然没有意义。
  • @Motlin:Jon 解释得很好:MyClass> 不是原始类型。
  • 现在我明白了为什么您的解决方法有效,我将使用它而不是演员表。谢谢。
【解决方案3】:

非常有趣的问题,Jon Skeet 的回答也非常有趣。

我只是想补充一点关于java编译器这种行为的愚蠢或不愚蠢。

我认为编译器假定如果你没有在generc 类中指定类型参数,你就不能(或不想)使用任何类型参数。您可以使用早于 5 的 java 版本,或者喜欢手动进行强制转换。

这对我来说似乎并不那么愚蠢。

【讨论】:

    【解决方案4】:

    泛型类型在编译后被删除。

    当你这样做时:

    Map<String, String> map = unchecked.getMap();
    

    您正在强制从 Map 转换为 Map,这就是未检查警告的原因。但是,之后你可以这样做:

    String s2 = map.get("");
    

    因为地图的类型是 Map

    但是,当你这样做时

    String s3 = fail.getMap().get(""); 
    

    你没有将 fail.getMap() 转换为任何东西,所以它被认为是简单的 Map,而不是 Map

    你应该在后者中做的事情是这样的:

    String s3 = ((Map<String, String>fail.getMap()).get("");
    

    它仍然会发出警告,但无论如何都会起作用。

    【讨论】:

    • 那为什么第一个编译? “擦除”不是这里的完整答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-31
    • 1970-01-01
    • 2013-03-26
    • 2012-07-06
    相关资源
    最近更新 更多