Reputation: 594
I'm having problems trying to pass a parameter to the constructor of a parent generic class due to the parameter being static and therefore I must explicitly specify the type at compile-time.
Here are the two classes in question:
public class CardStack<T extends Card> extends ArrayList<T>
public class Deck<T extends Card> extends CardStack<T>
The reason I need "T extends Card" and not just Card is because I am sharing these classes between a client (which needs to render them) and a server (which only cares about values).
I am statically creating a single deck of cards, which will be shuffled after being put into each deck.
private static final CardStack<Card> NEW_DECK;
static {
NEW_DECK = new CardStack<>(DECK_SIZE);
for (int i = 0; i < DECK_SIZE; i++) {
NEW_DECK.push(new Card(Card.Suit.values()[i / Card.CARD_VALUE_MAX],
i % Card.CARD_VALUE_MAX + 1));
}
}
Constructor for Deck:
public Deck() {
super(NEW_DECK); // Error Here
Collections.shuffle(this);
}
Constructor for CardStack:
protected CardStack(final CardStack<? extends T> cardStack) {
super(cardStack);
}
I am having trouble figuring out what to put for "? extends T". I've tried a combination of things, but nothing seems to work, so it seems I don't fully understand what's going on. Is this design possible? if not what would be a better solution. Thanks!
=========================================================================
Edit: Better explain reasoning for using generics.
First of all, I want to share these classes between the client and server which I have stated above. The real problem is when I trying to extend these classes to render them. All of the Client classes contain info and methods for rendering (x/y coord, methods to draw and detect clicks).
This works fine for my class ClientCard extends Card
but the problem arises when I try to make ClientCardStack extends CardStack
since CardStack contains Cards, I am unable to render them since they do not contain the right information, and what I really need is ClientCardStack extends CardStack<ClientCard>
which causes all these problems.
Upvotes: 2
Views: 180
Reputation: 7166
Let's check the Deck
constructor:
public Deck() {
super(NEW_DECK); // supposed to invoke CardStack<T>
Collections.shuffle(this);
}
So, what do we have at NEW_DECK
:
private static final CardStack<Card> NEW_DECK;
The point is that this <Card>
is not <T>
. It will be more apparent if you try to add a wildcard to NEW_DECK
:
private static final CardStack<T> NEW_DECK;
// Warning: Cannot make a static reference to a non-static type
Imagine the caller for a moment:
Deck<Card2> deck = new Deck();
This deck
-s left hand type is Deck<Card2>
and the right hand type is Deck<Card>
. Intuitively, it should be right. Unfortunately, java generics is not invariant. It's invariant instead. I.e. Deck<Card2>
is not a subtype of Deck<Card>
.
You need to explicitly define the type in the constructor. E.g.:
public static final CardStack<Card> NEW_DECK; // public now
public Deck() { // creates an empty deck
super(16);
}
public Deck(CardStack<T> stack) {
super(stack);
Collections.shuffle(this);
}
You can invoke the second constructor like this:
public static void main(String[] args) {
Deck<Card> deck = new Deck(NEW_DECK);
}
I presume these changes will change your design a little bit. But they do it in a good way! They force you to do some sort of dependency injection.
Upvotes: 0
Reputation: 5558
The problem is that NEW_DECK
is of type CardStack<Card>
, and the super constructor you are calling takes CardStack<? extends T>
. T
isn't always going to be a superclass of Card
.
The best solution for this is actually not to use generics. Inheritance dictates that whether you have an instance of the client version or the server version, you still have an instance of Card
, so just use Card
when you don't know whether you're on client or server.
Upvotes: 2