代码之家  ›  专栏  ›  技术社区  ›  Craig P. Motlin

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

  •  32
  • Craig P. Motlin  · 技术社区  · 17 年前

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

    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 回复  |  直到 17 年前
        1
  •  34
  •   Jon Skeet    17 年前

    知道了。这实际上 不是 一只看起来很奇怪的虫子。

    从 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,但具有所有 <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("");
    
        2
  •  3
  •   Joachim Sauer    17 年前

    嗯…不幸的是,我不能告诉你失败的原因。但我可以给你一个简单的解决方法:

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

        3
  •  3
  •   alexmeia    17 年前

    这个问题很有趣,乔恩·斯基特的回答也很有趣。

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

    我认为编译器假定 如果不在generic类中指定类型参数,则根本无法(或不想)使用任何类型参数。 . 您可以在5之前使用Java版本,或者喜欢手工制作强制转换。

    在我看来这并不愚蠢。

        4
  •  1
  •   Seb    17 年前

    泛型类型在编译后将被擦除。

    当你这样做的时候:

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

    您将强制从map强制转换为map<string、string>,这就是未选中警告的原因。但是,之后您可以执行以下操作:

    String s2 = map.get("");
    

    因为map的类型是map<string,string>。

    但是,当你这样做的时候

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

    您不会将fail.getmap()强制转换为任何内容,因此它被认为是简单的map,而不是map<string,string>。

    在后者中,您应该做的是:

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

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