为什么这个通用java代码不会编译?

时间:2009-03-19 13:27:25

标签: java generics

在这个简化的例子中,我有一个泛型类,以及一个不管类型参数如何都返回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

4 个答案:

答案 0 :(得分:35)

知道了。这实际上不是一个bug,看起来很奇怪。

来自section 4.8 (raw types) of the JLS

  

构造函数的类型(§8.8),   实例方法(§8.8,§9.4),或   非静态场(§8.3)原始的M   类型C不是从它继承的   超类或超级接口是   擦除它在通用中的类型   宣言对应C.   原始类型的静态成员的类型   C与其中的类型相同   对应的通用声明   下进行。

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

public Map getMap()

换句话说,我认为您可以将原始类型想象为与通用类型相同的API,但是从所有地方删除所有<X>(在API中,而不是实现)。

编辑:此代码:

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

编译因为从原始Map类型到Map<String, String>的隐式但未经检查的转换。通过在最后一种情况下进行显式转换(在执行时无效),您可以获得相同的效果:

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

答案 1 :(得分:3)

嗯......很遗憾,我无法告诉你它失败的原因。但我可以给你一个简单的解决方法:

fail的类型更改为MyClass<?>,然后编译就可以了。

答案 2 :(得分:3)

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

我只想添加一些关于java编译器的这种行为的愚蠢或不愚蠢的东西。

我认为编译器假定如果你没有在generc类中指定type参数,那么你根本不能(或根本不想)使用任何类型参数。您可以使用早于5的java版本,或者喜欢手动制作演员表。

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

答案 3 :(得分:1)

编译后会删除通用类型。

当你这样做时:

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

你正在强制从Map转换为Map&lt; String,String&gt;,这就是未选中警告的原因。但是,之后你可以这样做:

String s2 = map.get("");

因为map的类型为Map&lt; String,String&gt;。

然而,当你这样做时

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

你没有将fail.getMap()转换为任何东西,所以它被认为是明确的Map,而不是Map&lt; String,String&gt ;.

你应该在后者中做些什么:

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

仍然会发出警告,但无论如何都会有效。