2012-02-23 16 views
4

ここthis質問(初期コードは、問題を強調するのに十分ではなかった)周期的なジェネリック医薬品(2を試してみてください)

の第二試みがコンパイルできないコードは次のとおりです。

interface Player<R, G extends Game> 
{ 
    R takeTurn(G game); 
} 

interface Game<P extends Player> 
{ 
    void play(P player); 
} 

abstract class AbstractGame<R, P extends Player> 
    implements Game<P> 
{ 
    public final void play(final P player) 
    { 
     final R value; 

     value = player.takeTurn(this); 
     turnTaken(value); 
    } 

    protected abstract void turnTaken(R value); 
} 

public class XPlayer 
    implements Player<Integer, XGame> 
{ 
    @Override 
    public Integer takeTurn(final XGame game) 
    { 
     return (42); 
    } 
} 

public class XGame<P extends Player<Integer, XGame>> 
    extends AbstractGame<Integer, XPlayer> 
{ 
    @Override 
    protected void turnTaken(final Integer value) 
    { 
     System.out.println("value = " + value); 
    } 
} 

public class Main 
{ 
    public static void main(final String[] argv) 
    { 
     final XPlayer player; 
     final XGame game; 

     player = new XPlayer(); 
     game = new XGame(); 
     game.play(player); 
    } 
} 

私は何逆に実行すると、AbstractGameでplayメソッドをコンパイルするようにしようとしています。私はGameとPlayerでジェネリックスを追加してサークルで走らなければならないようですが、私の人生ではそれをまっすぐにすることはできません。

playメソッドはAbstractGameクラスでfinalでなければならず、キャストを行う方法がないため、turnTakenのような別のメソッドを記述したくない場合は、に。

編集:ここで要求されるようにコンパイルするコードがあるが、キャストを必要とする:

interface Player<R, P extends Player<R, P, G>, G extends Game<R, G, P>> 
{ 
    R takeTurn(G game); 
} 

interface Game<R, G extends Game<R, G, P>, P extends Player<R, P, G>> 
{ 
    void play(P player); 
} 

abstract class AbstractGame<R, G extends Game<R, G, P>, P extends Player<R, P, G>> 
    implements Game<R, G, P> 
{ 
    public final void play(final P player) 
    { 
     final R value; 

     value = player.takeTurn((G)this); 
     turnTaken(value); 
    } 

    protected abstract void turnTaken(R value); 
} 

class XPlayer 
    implements Player<Integer, XPlayer, XGame> 
{ 
    @Override 
    public Integer takeTurn(final XGame game) 
    { 
     return (42); 
    } 
} 

class XGame 
    extends AbstractGame<Integer, XGame, XPlayer> 
{ 
    @Override 
    protected void turnTaken(final Integer value) 
    { 
     System.out.println("value = " + value); 
    } 
} 

class Main 
{ 
    public static void main(final String[] argv) 
    { 
     final XPlayer player; 
     final XGame game; 

     player = new XPlayer(); 
     game = new XGame(); 
     game.play(player); 
    } 
} 
+0

私は一度のように、あなたはJavaの一般的な地獄に遭遇したようです。私はその後、インターフェイスのためにジェネリック医薬品を放棄しました。 –

+0

どうやってインターフェイスでやりますか? – TofuBeer

+0

ジェネリックではなく、適切なオブジェクトを実装する必要があるインターフェイスを作ったのですが、私のメソッドはこのインターフェイスを引き出しました... –

答えて

8

はジェネリックと生タイプの混在は仕事に行くのではありません。あなたはこれらのインターフェースが互いを参照する必要がある場合、彼らはまた、自分自身を参照する必要があります。

interface Player<R, P extends Player<R, P, G>, G extends Game<R, G, P>> 
{ 
    R takeTurn(G game); 
} 

interface Game<R, G extends Game<R, G, P>, P extends Player<R, P, G>> 
{ 
    void play(P player); 
} 

これはかなりhairbrained探している、と私はあなたがそれを必要とする理由はわからないが。

編集:私は実装することができました

あなたAbstractGame上記に基づいて:

public class XGame 
    extends AbstractGame<Integer, XPlayer> //compile error on XPlayer 
{ 

    protected void turnTaken(Integer value) { } 
} 

public class XPlayer 
    implements Player<Integer, XPlayer, XGame> //compile error on XGame 
{ 
    @Override 
    public Integer takeTurn(final XGame game) 
    { 
     return (42); 
    } 
} 
:私はかなり XGameXPlayerで回路を閉じることができませんでしたしかし

abstract class AbstractGame<R, P extends Player<R, P, AbstractGame<R, P>>> 
    implements Game<R, AbstractGame<R, P>, P> 
{ 
    public final void play(final P player) 
    { 
     final R value; 

     value = player.takeTurn(this); 
     turnTaken(value); 
    } 

    protected abstract void turnTaken(R value); 
} 

この問題は、の一般的な宣言とXPlayerは、もう一方が正しいことが必要です。これはあなたのデザインが本当に周期的な場所です。コンパイラがそれぞれ「正しい」と仮定すると、理論上は動作します。しかし、それはしません。

編集2:

interface Game<R, G extends Game<R, G>> 
{ 
    void play(Player<R, G> player); 
} 

interface Player<R, G extends Game<R, G>> 
{ 
    R takeTurn(G game); 
} 

abstract class AbstractGame<R, G extends AbstractGame<R, G>> 
    implements Game<R, G> 
{ 
    public final void play(final Player<R, G> player) 
    { 
     final R value; 

     value = player.takeTurn(self()); 
     turnTaken(value); 
    } 

    protected abstract G self(); 

    protected abstract void turnTaken(R value); 
} 

public final class XGame extends AbstractGame<Integer, XGame> 
{ 
    protected XGame self() { 
     return this; 
    } 

    protected void turnTaken(Integer value) { } 
} 

public class XPlayer implements Player<Integer, XGame> 
{ 
    @Override 
    public Integer takeTurn(final XGame game) 
    { 
     return (42); 
    } 
} 

ここで重要なのは、タイプGのインスタンスを返しますAbstractGameで抽象メソッドself()を宣言した。このことについてどのように

。クラスを拡張すると、継承された型パラメータが独自の型で解決され、を実装してthisを返す必要があります。

public class EvilGame extends AbstractGame<Integer, AnotherGame> { ... } 

は、このパターンの詳細については、私の答えherethis postを参照してください:拡張クラスは簡単に、たとえば、ある可能性がありので、これは、内部コードにのみ適しています。

+0

私は髪の頭脳を気にしない:-)私はそれを打つだろう。 – TofuBeer

+0

どうすればいいのか教えてください。すでにコードをコピーした場合には、いくつかのタイプミスが修正されました。 –

+0

ほぼ完璧です...問題は私がしなければならないことです:value = player.takeTurn((G)this); <---私はそれを取り除く方法がない場合私が住むことができるGにキャスト – TofuBeer

2

Paul Belloraが指摘しているように、ジェネリックタイプとロータイプを混在させています。正しい完全ジェネリックソリューションはちょっと混乱しており、多くの冗長性が必要です。 Javaの循環型(ただし再帰型ではない)ジェネリックスを行うには良い方法はない(私が知っている)。

よりもむしろこれに苦しんで、私はちょうど1つのパラメータで演奏されている値の型にPlayerGameジェネリックの両方になるだろう - あなたはRとして持っていたもの。

interface Game<R> { 
    void play(Player<? extends R> player); 
} 

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

abstract class AbstractGame<R> implements Game<R> { 
    public final void play(Player<? extends R> player) { 
     final R value; 

     value = player.takeTurn(this); 
     turnTaken(value); 
    } 

    protected abstract void turnTaken(R value); 
} 

class XPlayer implements Player<Integer> { 
    @Override 
    public Integer takeTurn(Game<? super Integer> game) { 
     return 42; 
    } 
} 

class XGame extends AbstractGame<Integer> { 
    @Override 
    public void turnTaken(Integer value) { 
     System.out.println("value = " + value); 
    } 
} 

public class Main { 
    public static void main(String[] argv) { 
     XPlayer player = new XPlayer(); 
     XGame game = new XGame(); 
     game.play(player); 
    } 
} 

さて、Rベースの動きを取る方法を知っているすべてのプレイヤーは、いずれかのRベースのゲームをプレイすることができます。

+0

私は生のジェネリックミキシングがうまくいかないことを知っていました - 私は自分の髪を引っ張っているところにコードを残しました:-)実際のシステムでは、プレーヤーとゲームの両方が知る必要があるという問題があります特定の型とキャストは有用な解決策ではありません。なぜなら、キャストが行く場所は一般的な(無言ではない)コードの階層を上回っているからです。私は仕事をすることができれば私が見るものがありますが、それは大きなリニューアルであると思います。 – TofuBeer

+0

@TofuBeer私はちょうど重要な編集をしました。 – yshavit

+0

+1本当の答えはあなたの答えが上がっているように、より良いデザインです。私の場合は、この難解なことが実際にコンパイルできるかどうかだけを見ています。 –

関連する問題