2012-02-23 53 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方法编译。看起来我必须在游戏和玩家的圈子中加入泛型才能扩展/实现,但对于我来说,我无法得到它。

这个play方法在AbstractGame类中必须是最终的,并且没有办法进行转换,并且我不想编写像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

Ech,看起来你已经遇到了java通用地狱,就像我曾经一样。然后我放弃了泛型,转而使用接口。 – 2012-02-24 15:16:41

+0

你会如何通过接口来做到这一点?我不介意地狱,当我可以弄明白的时候:-) – TofuBeer 2012-02-24 17:25:50

+0

我的意思是,而不是泛型,我已经制作了需要实现的适当对象的接口,并且我的方法采用了这种接口...... – 2012-02-25 18:39:46

回答

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基于以上:

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); 
} 

但是我不能完全与XGameXPlayer闭合电路:

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); 
    } 
} 

这个问题似乎是的每个通用声明和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); 
    } 
} 

这里的关键是在AbstractGame返回G类型的实例声明了一个抽象方法self()。扩展类必须使用它们自己的类型来解析继承的类型参数,并执行self()以返回this。这仅适用于内部代码,因为延伸类很容易撒谎,例如:

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

见我的回答herethis post有关这种模式的更多细节。

+0

我不介意头发发胶:-)我会给它一个镜头。 – TofuBeer 2012-02-23 23:30:29

+0

让我知道它是如何工作的 - 我只是修复了一些错别字,以防您已经复制了代码。 – 2012-02-23 23:31:24

+0

几乎完美......唯一的问题是我必须做的:value = player.takeTurn((G)this); <---投给G,如果没有办法摆脱它,我可以忍受它 – TofuBeer 2012-02-23 23:40:54

2

正如Paul Bellora指出的那样,您正在混合通用和原始类型 - 而正确的,完全通用的解决方案有点混乱,需要大量冗余。没有好的方法(我知道)在Java中做循环(而不是递归)泛型。

,而不是为此而努力,我会做的只有一个参数都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 2012-02-23 23:43:58

+0

@TofuBeer我刚刚进行了一些重要的编辑,hth。 – yshavit 2012-02-23 23:49:27

+0

+1真正的答案是更好的设计,因为你的答案试图解决。在我看来,这个难题是否真的可以编译。 – 2012-02-23 23:57:32