Kelvin
Kelvin

Reputation: 594

Passing Static field as Generic type

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

Answers (2)

Tamas Rev
Tamas Rev

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

RamenChef
RamenChef

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

Related Questions