【问题标题】:Unchecked assignment warning未经检查的分配警告
【发布时间】:2015-05-02 16:51:18
【问题描述】:

我使用的是 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&lt;java.lang.Integer, java.lang.String&gt;'

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

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

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

但是我为什么要这样做呢? T 在这里有什么意义?

所以,问题是 - 为什么类型擦除不仅要影响 T(这是可以理解的 - 如果我传递 A 的实例,T 是未知的),而且“硬编码" 在这种情况下像 &lt;Integer, String&gt; 这样的通用签名?

【问题讨论】:

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


    【解决方案1】:

    在第二种情况下:

    public void processA(A a)
    

    A 是什么意思?它是指A&lt;String&gt;A&lt;List&lt;String&gt;&gt; 还是什么?你可能没有使用任何与A 类型相关的东西,但是编译器不知道这个事实。对于编译器来说,A 是恐慌的标志。

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

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

    具有A&lt;?&gt; 的参数类型意味着,您不必特别关心A 的类型,只需指定一个通配符。对你来说,这意味着:A 的任何对象,其泛型类型可以是任何类型。实际上,这意味着您不知道类型。它没用,因为你不能以类型安全的方式做任何与A 相关的事情,因为? 几乎可以是任何事情!

    但是根据您的方法主体,使用A&lt;?&gt; 是非常有意义的,因为在主体中没有任何地方您真正需要A 的类型

    【讨论】:

    • You might not be using anything related to type of A, but hey the compiler doesn't know this fact - 好吧,它可以很容易地了解它,只需看看processA的实现,不难验证@987654337 @ 无关紧要 ;) 我会按照您的建议使用 &lt;?&gt;,但我对编译器感到失望。
    • @KonradMorawski 在理想世界中,只有A 应该是非法的并且代码不应该编译。但出于向后兼容性的原因,它是允许的。对于声明为A&lt;T&gt; 的类,其泛型类型对于执行与A 相关的任何事情都极为重要。所以不应该只使用A(实际上在Scala中,只使用A是非法的)
    • 是的,我想...我不使用 Scala,但我来自 C# 世界,Java 的泛型感觉就像穿鞋太小了。
    • @KonradMorawski 此外,编译器几乎不可能知道它。你可能正在做反思或做演员并一起工作。显然很难以 100% 的信念暗示该类型未被使用。
    • @Jatin - 感谢您的回答。这是对我来说很清楚的通配符的第一个解释。 (也许是因为我遇到了提问者的确切问题,在那里我有一个带有显式参数化方法的参数化类,它警告使用时存在未经检查的赋值。)
    【解决方案2】:

    如果您的意思是接受任何可能类型的A&lt;T&gt; T,但不需要T,则可以通过使用通配符并写入A&lt;?&gt; 来正确表达。这样做会消除代码中的警告:

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

    使用裸类型A 不会被同等对待。正如Java Language Specification 中所解释的,类似的原始类型不打算在新代码中使用:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多