【问题标题】:Java Generics: Why does my @SuppressWarnings("unchecked") work in this case, but not the other?Java 泛型:为什么我的 @SuppressWarnings("unchecked") 在这种情况下有效,而在另一种情况下无效?
【发布时间】:2013-06-02 04:07:22
【问题描述】:

问题与Why do we have to use an intermediary variable for @SuppressWarnings("unchecked")? 类似,但我无法从该问题中找到解决方案。

作为一个练习,我正在从头开始构建一个哈希表结构。于是我写了一个类LinkedListDemo辅助HashTableDemo; LinkedListDemo 类测试非常好。特别是,以下子例程在运行时不会导致 ClassCastException 错误:

    public LinkedListDemo<S, T> reverse() {

        if (count == 0)
            return null;

        @SuppressWarnings("unchecked")
        S[] keys = (S[]) new Object[count];
        @SuppressWarnings("unchecked")
        T[] vals = (T[]) new Object[count];

        ListNode<S, T> runner = head;
        for (int k = 0; k < count; k++) {

            keys[k] = runner.pair.getKey();
            vals[k] = runner.pair.getValue();

            runner = runner.next;

        }

        ArrayUtils.reverse(keys);
        ArrayUtils.reverse(vals);

        return new LinkedListDemo<S, T>(keys, vals);

    }

而以下,在我的 HashTable 类中:

public class HashTableDemo<S, T> {

    private LinkedListDemo<S, T>[] table = (LinkedListDemo<S, T>[]) new Object[10]; 

// more code...

}

有谁知道 Java 的 HashMap 类是如何规避这个问题的,和/或我该怎么做?我尝试按照上面链接中的建议在构造函数中创建一个中间变量 - 但它没有用。

【问题讨论】:

  • 应该在运行时收到 ClassCastExceptions。也许你没有彻底测试。仅当 SObject 时,分配 S[] keys = (S[]) new Object[count]; 才合法。如果SString,它将失败。
  • @NathanielWaisbrot 不正确,因为 S 被删除为 Object - 这是 unchecked 转换的含义。

标签: java generics type-erasure


【解决方案1】:

这将编译:

@SuppressWarnings("unchecked") // this is okay because [insert reasoning here]
private LinkedListDemo<S, T>[] table =
        (LinkedListDemo<S, T>[]) new LinkedListDemo<?, ?>[10];

您的代码错误的原因是因为Object[] 不是LinkedListDemo[],类似于Object 不是LinkedListDemo

早期的未检查强制转换有效,因为 S[]T[] 在运行时已从 erased 变为 Object[]

一般来说,不鼓励参数化类型的数组,因为它们的存在本身就是不安全的。这是因为:

  • 数组是协变的 - LinkedListDemo&lt;S, T&gt;[] Object[]。因此,可以将table 分配给Object[] 变量,然后将String 作为元素分配——这在编译时是合法的,但至少在运行时会因ArrayStoreException 而失败,因为数组会进行运行时类型检查.
  • 通用类型信息在运行时不可用,因为它已被编译器擦除。可以再次将table 分配给Object[] 变量,但这次将LinkedListDemo&lt;String, Integer&gt; 分配为元素。在运行时,数组只会看到添加了一个普通的LinkedListDemo,因此如果泛型类型错误,它不会立即失败。这可能会导致稍后出现意外的ClassCastExceptions。

请参阅我的回答 here 以获取有关此问题的更完整说明和示例。另请参阅 Angelika Langer 的泛型常见问题解答的精彩解释:Can I create an array whose component type is a concrete parameterized type?

要点是简单地使用List&lt;LinkedListDemo&lt;S, T&gt;&gt; 会更容易和更安全。如果您最终决定坚持使用LinkedListDemo&lt;S, T&gt;[],请确保将其作为实现细节小心隐藏,并注意它会如何被滥用。

其他一些杂记:

  • @SuppressWarnings("unchecked") 除了抑制未经检查的强制转换编译器警告外,什么都不做。它本质上是在说“相信我,我知道我在做什么”——但你仍然需要小心不要滥用未经检查的演员表。有关更多信息,请参阅此帖子:How do I address unchecked cast warnings?
  • 与所有核心 API 类一样,source for HashMap 可用,因此如果您想更好地了解其内部工作原理,可以随时查看它。

【讨论】:

  • 谢谢保罗;但通过扩展,为什么 LinkedListDemo 在运行时没有类似地被擦除?
  • @VictorAlvarez LinkedListDemo 是一个 reifiable 类型,这意味着它在运行时仍然可用。 LinkedListDemo&lt;S, T&gt;[] 被删除为 LinkedListDemo[]
  • 它是否可以具体化,因为它本身包含类型参数,而不是 S 和 T 本身是类型参数?谢谢 -
  • @VictorAlvarez 它是可具体化的,因为它不是 本身就是一个泛型类型参数或类型参数。泛型类型参数被删除,例如LinkedListDemo&lt;S, T&gt;[]LinkedListDemo&lt;String, Integer&gt;[]LinkedListDemo[]。类型参数被删除,例如SObject.
  • @VictorAlvarez 当类型擦除发生时,适当的转换被添加到编译的字节码中。所以给定Class&lt;MyType&gt; c,它将擦除到Class c,代码MyType obj = c.newInstance();编译成类似MyType obj = (MyType)c.newInstance();的东西(除了字节码)。有关更多信息,请参阅此帖子:Java generics - type erasure - when and what happens
猜你喜欢
  • 1970-01-01
  • 2017-08-09
  • 2022-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多