未经检查的分配警告

时间:2015-03-02 10:56:43

标签: java generics type-erasure unchecked raw-types

我使用的是Android Studio 1.1.0。

这不会导致警告:

public static class A {
    public Map<Integer, String> getMap() {
        return null;
    }
}

public static class B {
    public void processA(A a) {
        Map<Integer, String> map = a.getMap();
    }
}

但要A通用:

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

这一行:

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

立即向您发出警告:"Unchecked assignment: 'java.util.Map to java.util.Map<java.lang.Integer, java.lang.String>'

尽管getMap的签名完全独立于T,但代码对于Map包含的类型仍然是明确的。

我知道我可以通过重新实现processA来解除警告,如下所示:

public <T> void processA(A<T> a) {
    Map<Integer, String> map = a.getMap();
}

但为什么我必须这样做呢? T在这里有什么关系?

所以,问题是 - 为什么类型擦除不仅影响T(这是可以理解的 - 如果我传递A的实例,T是一个未知的,但也&#34;硬编码&#34;在这种情况下,如<Integer, String>这样的通用签名?

2 个答案:

答案 0 :(得分:17)

在你的第二个案例中:

public void processA(A a)

A你是什么意思?这是A<String>还是A<List<String>>还是什么?您可能没有使用与A类型相关的任何内容,但是编译器不知道这个事实。对于编译器来说,A只是恐慌的表现。

在您的情况下,因为您不需要特别知道A的类型,您可以:

public void processA(A<?> a) {
    Map<Integer, String> map = a.getMap();
} 

参数类型为A<?>意味着,您并不特别关注A的类型,只需指定一张外卡。对你而言,它意味着:A的任何对象都可以使用任何类型作为泛型类型。实际上,这意味着你不知道这种类型。它没用,因为你不能像A那样以类型安全的方式做任何与?相关的事情,几乎可以做任何事情!

但是根据你的方法主体,世界上使用A<?>是有道理的,因为身体中你实际上不需要A的类型

答案 1 :(得分:7)

当您的意思是接受任何可能类型A<T>的{​​{1}},但不需要T时,可以使用通配符并写{{}}来正确表达1}}。这样做会消除代码中的警告:

T

使用裸类型A<?>不会被视为等效。正如Java Language Specification中所解释的那样,像这样的原始类型不打算在新代码中使用:

  

未经检查的转换用于实现在引入泛型类型之前编写的遗留代码与已经过转换以使用泛型的库(我们称之为泛化的过程)的平滑互操作。在这种情况下(最值得注意的是,java.util中的Collections Framework的客户端),遗留代码使用原始类型(例如Collection而不是Collection&lt; String&gt;)。原始类型的表达式作为参数传递给库方法,这些库方法使用相同类型的参数化版本作为其相应形式参数的类型。

     

在使用泛型的类型系统下,此类调用无法显示为静态安全。拒绝此类调用将使大量现有代码无效,并阻止它们使用较新版本的库。反过来,这将阻止图书馆供应商利用通用性。为了防止这种不受欢迎的事件转换,可以将原始类型转换为原始类型引用的泛型类型声明的任意调用。虽然转换是不合理的,但它被视为对实用性的让步。在这种情况下会发出未经检查的警告。