为什么“java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to”有界类型参数错误而不是形式类型参数错误?

abb*_*abb 2 java generics classcastexception

由于 java 没有通用数组,我使用了将 Object 数组转换为类型参数的常规技巧。当我有一个正式的类型参数时,这工作正常,<T>但当我使用有界类型参数时则不然<T extends something>

使用正式类型的以下代码工作正常

public class Deck <T> {
    private T [] cards;
    private int size;

    public Deck () {
        cards = (T []) new Object[52];
        size = 0;
    }
}

public class BlackJackGame {
    Deck<BlackJackCard> deck;

    public BlackJackGame() {
        deck = new Deck<>();
        populate (deck);
        deck.shuffle();
    }
}

public class BlackJackCard extends Card {
}
Run Code Online (Sandbox Code Playgroud)

以下使用有界类型的代码抛出错误

public class Deck <T extends Card> {
    private T [] cards;
    private int size;

    public Deck () {
        cards = (T []) new Object[52];
        size = 0;
    }
}

public class BlackJackGame {
    Deck<BlackJackCard> deck;

    public BlackJackGame() {
        deck = new Deck<>();
        populate (deck);
        deck.shuffle();
    }
}

public class BlackJackCard extends Card {
}
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [LCard;
    at Deck.<init>(Deck.java:10)
    at BlackJackGame.<init>(BlackJackGame.java:5)
Run Code Online (Sandbox Code Playgroud)

小智 6

这个例子让我想起了早期,当我在“Effective java”一书中阅读泛型时......

首先,这是java泛型的黄金法则:不要混合使用数组和泛型,因为你有很好的机会产生不安全的代码。您的代码将泛型(例如 T、T extends Card)与数组(例如 T [] 卡)混合在一起。然后,您在运行时得到了不安全的代码。

这是一种安全的方法(更喜欢列表而不是数组):

class Deck <T extends Card> {
    private List<T> cards;

    public Deck () {
        cards = new ArrayList()<>;
    }
Run Code Online (Sandbox Code Playgroud)

}

现在,要回答您的问题,您应该先回到 Java 中的一些基础知识:

1- 数组是协变结构

2-泛型是不变的构造

3- 元素类型在数组中具体化(具体化)

4- 参数类型在通用中被擦除(类型擦除)

别担心,先把可怕的概念放在一边,看看你的例子发生了什么:

  • 正式类型 T 在运行时被擦除。

  • 这意味着它已在字节码中完全删除。

  • 在第一个示例中,T 只是替换为 Object,因为它是离它最近的类(就继承而言),因此,

cards = (T []) new Object[52]
Run Code Online (Sandbox Code Playgroud)

被翻译成

cards = (Object []) new Object[52];
Run Code Online (Sandbox Code Playgroud)

这是安全的。

  • 在第二个例子中,T 被绑定到 Card 并且它变成 ,因此,它是最接近它的类(就继承而言),所以,
cards = (T []) new Object[52]
Run Code Online (Sandbox Code Playgroud)

被翻译成

cards = (Card []) new Object[52];
Run Code Online (Sandbox Code Playgroud)

由于 Object 不是 Card 的子类型,因此您会遇到运行时强制转换异常。