3

Since java does not have a generic array, I am using the regular trick of casting Object array to a type parameter. This is working fine when I have a formal type parameter like <T> but not when I use bounded type parameter <T extends something>.

Following Code using formal type works fine

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 {
}

Following code using bounded type throws error

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)
4
  • Look up "type erasure" and then what <T> is erased to and what <T extends Card> is erased to. That will help you understand the subject and understand the issue here. Commented Sep 21, 2019 at 0:21
  • Possible duplicate of Java generics type erasure: when and what happens? Commented Sep 21, 2019 at 0:25
  • The second dupe covering bounded types and raw types: What is the erasure of a bounded generic type? Commented Sep 21, 2019 at 0:25
  • Now you know why the compiler flags (T[]) with an “unchecked cast” warning. That cast is not safe. Are you not permitted to use a List? Commented Sep 21, 2019 at 3:08

1 Answer 1

6

This example remind me early days, when I'm reading about Generics in "Effective java" book ...

First thing first, here is java generics golden rule : don't mix arrays and generics, because you get a good chance to produce unsafe code. Your code mixes generics (e.g. T, T extends Card) with arrays (e.g. T [] cards). Then, you got unsafe code in runtime.

This is one safe way (prefer lists over arrays):

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

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

}

Now, to answer your question you should go back to some basics first in java:

1- Arrays are co-variant constructs

2- Generics are invariant constructs

3- Element Type is reified in arrays (reification)

4- Parameter Type is erased in Generic (Type erasure)

No worries, let put aside scary concepts, and check what happened with your example:

  • The Formal type T is erased in Runtime.

  • It means it's completely removed in the bytecode.

  • In 1st example the T is just replaced with Object, because it's the nearest class to it (in terms of inheritance) so,

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

is translated to

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

which is safe.

  • In 2nd example the T is bounded to Card and it becomes , hence, the nearest class to it (in terms of inheritance) so,
cards = (T []) new Object[52]

is translated to

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

Since Object is not a subtype of Card, you got a Runtime cast exception.

Sign up to request clarification or add additional context in comments.

1 Comment

Can you please elaborate, why does the java do this 'nearest class' thing while adding casts?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.