Почему ошибка "java.lang.ClassCastException: [Ljava.lang.Object; не может быть приведена к" для параметров ограниченного типа, а не для параметров формального типа? - PullRequest
1 голос
/ 21 сентября 2019

Поскольку у 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 {
}

код, использующий ограниченную ошибку типа throws

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)

1 Ответ

4 голосов
/ 21 сентября 2019

Этот пример напоминает мне о ранних днях, когда я читал об обобщениях в книге «Эффективная Java» ...

Прежде всего, вот золотое правило java generics: не смешивайте массивы и genericsпотому что у вас есть хороший шанс создать небезопасный код.Ваш код смешивает дженерики (например, T, T расширяет карту) с массивами (например, карты T []).Затем вы получили небезопасный код во время выполнения.

Это один из безопасных способов (предпочтение спискам, а не массивам):

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

    public Deck () {
        cards = new ArrayList()<>;
    }

}

Теперь, чтобы ответить на ваш вопрос, высначала вернемся к некоторым основам в java:

1- Массивы являются ко-вариантными конструкциями

2- Дженерики являются инвариантными конструкциями

3- Тип элемента повторно реализован в массивах(reification)

4- Тип параметра стирается в Generic (стирание типа)

Не беспокойтесь, оставьте в стороне страшные понятия и проверьте, что случилось с вашим примером:

  • Формальный тип T стирается во время выполнения.

  • Это означает, что он полностью удален в байт-коде.

  • В первомНапример, T просто заменяется на Object, потому что это ближайший к нему класс (с точки зрения наследования), поэтому

cards = (T []) new Object[52]

переводится в

cards = (Object []) new Object[52];

который является безопасным.

  • Во 2-м примере T привязан к Card и, следовательно, становится ближайшим классом to (с точки зрения наследования), поэтому
cards = (T []) new Object[52]

переводится в

cards = (Card []) new Object[52];

Поскольку Object не является подтипом Card, вы получили исключение приведения во время выполнения.

...