TofuBeer
TofuBeer

Reputation: 61536

cyclical generics

I am trying to do something along the lines of:

public interface Player<R>
{
    R takeTurn(Game game);
}

and

public interface Game
{
}

public class XPlayer
    implements Player<Interger>
{
    // XGame won't work because the interface takes Game
    public Integer takeTurn(final XGame game)
    {
        return (null);
    }
}

public class XGame
{
}

What I am stuck on is what do I need to change in the Game and Player interfaces to make the generics work (I have paused while I still have some hair in my head :-) Specifically I am hung up on where the Player needs to know the type of Game and the Game needs to know the type of Player.

Upvotes: 0

Views: 167

Answers (3)

PaulMurrayCbr
PaulMurrayCbr

Reputation: 1260

So we have the situation where we have games and players, and these may be subclassed. A TicTacToe GAME uses TicTacToe PLAYERS, and vica-versa.

To do this, put all the classes in the declaration. The declaration winds up being incredibly ugly, but the code that uses it becomes very clean.

I like to keep the list of generic types in the same order across the system of classes I am using.

abstract class Game<//
        G extends Game<G, P>, //
        P extends Player<G, P>> {
    P getCurrentPlayer() {return null;}
}

abstract class Player<//
        G extends Game<G, P>, //
        P extends Player<G, P>> {
    G getCurrentGame() {return null;
}

And you use those abstract classes like so:

class TTTGame extends Game<TTTGame, TTTPlayer> {}

class TTTPlayer extends Player<TTTGame, TTTPlayer> {}

class ChessGame extends Game<ChessGame, ChessPlayer> {}

class ChessPlayer extends Player<ChessGame, ChessPlayer> {}

and so on. The strong typing means that a chess player's game will be a chess game, and a chess game's players will be chess players - in all the return types, parameters, and visible fields.

You can even do tricks like extending an inheritance hierarchy one way but not the other. Lets say we define a class of games where the players only action is to flip a coin and to report heads or tails. We can create a coin player that works for any coingame, and a coin game whose type is still generic, but which binds P.

class CoinGame<G extends CoinGame<G>> //
        extends Game<G, CoinPlayer<G>> {
}

class CoinPlayer<G extends CoinGame<G>> //
        extends Player<G, CoinPlayer<G>> {
    boolean flip() {
        return true;
    };
}

Subclasses of coin game have only one type parameter, because they all take players of type coin player. Most concrete games will bind this parameter to themselves:

class DontFlipHeads //
        extends CoinGame<DontFlipHeads> {
    class YouLose extends Exception {
    }

    void turn() throws YouLose {
        if (getCurrentPlayer().flip()) throw new YouLose();
    }
}

The DontFlipHeads class knows that its player has a flip method, because it extends a game that has bound the player type that has that method, even though getCurrentPlayer() is defined generically right at the bottom of the inheritance hierarchy.

Note that the player class of DontFlipHeads need to have a parameter. Thus:

…
DontFlipHeads game = new DontFlipHeads();
CoinPlayer<DontFlipHeads> player1 = new CoinPlayer<DontFlipHeads>();

game.addPlayer(player1, 1);

This wouldn't be necessary if there weren't this two-way referencing. But because the referencing is there, you can

DontFlipHeads whatGame = player1.getGame();

without any casting.

Upvotes: 1

CodeBlue
CodeBlue

Reputation: 15389

Why not have something like this -

    public Integer takeTurn(Game game)
      {
         XGame xGame = (XGame) game; // assuming XGame implements Game  
         return ...;
      }

Upvotes: 0

Bohemian
Bohemian

Reputation: 425188

This is not a generics question (Game is not typed).

It's an inheritance question. Try this:

public class XGame implements Game // added interface

Upvotes: 5

Related Questions