为什么有界类型参数出现 "java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to" 错误而不是形式类型参数?
Why "java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to" error for Bounded type parameters and not for Formal Type parameters?
由于 java 没有泛型数组,我使用了将对象数组转换为类型参数的常规技巧。当我有一个像 <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 {
}
以下使用有界类型的代码抛出错误
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)
这个例子让我想起了早年,当我在 "Effective java" 书中阅读泛型时...
首先,这里是 java 泛型黄金法则:不要混合数组和泛型,因为您很有可能产生不安全的代码。
您的代码混合了泛型(例如 T、T extends Card)和数组(例如 T [] cards)。然后,你在运行时得到了不安全的代码。
这是一种安全的方法(首选列表而不是数组):
class Deck <T extends Card> {
private List<T> cards;
public Deck () {
cards = new ArrayList()<>;
}
}
现在,要回答您的问题,您应该先回到 java 中的一些基础知识:
1- 数组是协变结构
2- 泛型是不变的构造
3- 元素类型在数组中具体化(具体化)
4-参数类型在通用中被擦除(类型擦除)
别担心,抛开可怕的概念,看看你的例子发生了什么:
正式类型 T 在运行时被删除。
表示在字节码中完全去掉了
在第一个例子中,T 被替换为 Object,因为它是最接近它的 class(在继承方面)
所以,
cards = (T []) new Object[52]
翻译成
cards = (Object []) new Object[52];
这是安全的。
- 在第二个示例中,T 绑定到 Card 并且它变为,因此,最接近它的 class(在继承方面)
所以,
cards = (T []) new Object[52]
翻译成
cards = (Card []) new Object[52];
由于 Object 不是 Card 的子类型,因此出现了运行时转换异常。
由于 java 没有泛型数组,我使用了将对象数组转换为类型参数的常规技巧。当我有一个像 <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 {
}
以下使用有界类型的代码抛出错误
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)
这个例子让我想起了早年,当我在 "Effective java" 书中阅读泛型时...
首先,这里是 java 泛型黄金法则:不要混合数组和泛型,因为您很有可能产生不安全的代码。 您的代码混合了泛型(例如 T、T extends Card)和数组(例如 T [] cards)。然后,你在运行时得到了不安全的代码。
这是一种安全的方法(首选列表而不是数组):
class Deck <T extends Card> {
private List<T> cards;
public Deck () {
cards = new ArrayList()<>;
}
}
现在,要回答您的问题,您应该先回到 java 中的一些基础知识:
1- 数组是协变结构
2- 泛型是不变的构造
3- 元素类型在数组中具体化(具体化)
4-参数类型在通用中被擦除(类型擦除)
别担心,抛开可怕的概念,看看你的例子发生了什么:
正式类型 T 在运行时被删除。
表示在字节码中完全去掉了
在第一个例子中,T 被替换为 Object,因为它是最接近它的 class(在继承方面) 所以,
cards = (T []) new Object[52]
翻译成
cards = (Object []) new Object[52];
这是安全的。
- 在第二个示例中,T 绑定到 Card 并且它变为,因此,最接近它的 class(在继承方面) 所以,
cards = (T []) new Object[52]
翻译成
cards = (Card []) new Object[52];
由于 Object 不是 Card 的子类型,因此出现了运行时转换异常。