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

2022-09-01 06:53:08

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

答案 1

明白了。这实际上不是一个错误,尽管看起来很奇怪。

JLS 的第 4.8 节(原始类型)中

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

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

public Map getMap()

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

编辑:此代码:

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

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

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

答案 2

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

将 的类型更改为 ,然后编译即可。failMyClass<?>


推荐