Java使用新类向现有类添加功能,同时仍然暴露前一类的所有方法

时间:2014-02-22 02:12:35

标签: java class this subclass extends

我想做一些像super()这样的事情,但没有向构造函数传递任何东西。
像复制当前状态中的所有变量一样,它们被设置为该类中的方法。
当包装类是使用额外方法的Player时,我想避免在包装类中执行getPlayer()之类的操作。
我不想将这些额外的方法移动到Player类中,因为它不是Player类的一部分,它是Player类可能进入的东西,但不是在任何时候。

我仍然希望能够运行SpecialPlayer的构造函数,使用它自己必须设置的变量,同时仍然将它作为一个Player从我传入它的任何实例中保存。

Player类变量在转换为SpecialPlayer类时可能随时更改,SpecialPlayer类应始终具有与Player相同的最新变量。是的,它的外观在任何时候都必须完全相同。



这是我写的一些伪代码,试图说明问题

public class Player {
    private int money;
    private boolean bar;

    public Player(int money) {
        this.money = money;
        bar = true;
    }

    public void toggleBar() {
        bar != bar;
    }
}

public class SpecialPlayer extends Player {
    private Player player;
    private long foo;

    public SpecialPlayer(Player player) {
    this.player = player; //wrapper way not good...
    //this = player; ??? doesn't compile...
    foo = System.currentTimeMillis();
    }

    public long getFoo() {
        return foo;
    }

    public Player getPlayer() { //<-- this is stupid.
        return player;
    }
}


Player player = new Player(12345);
player.toggleBar(); //it's now false, also it's not a constructor variable.

SpecialPlayer specialPlayer = new SpecialPlayer(player);
specialPlayer.getFoo(); //some long number value.
specialPlayer.toggleBar(); //now it should be true!.
player.toggleBar(); //should be false.
specialPlayer.toggleBar(); //should be true.

4 个答案:

答案 0 :(得分:2)

我认为你对继承如何运作有点困惑

例如,这个稍微改变的代码版本

class Player {
    private int     money;
    private boolean bar;

    public Player() {} // I've added this guy, so your subclass is not forced to implement a similar constructor as Player(int money)

    public Player(int money) {
        this.money = money;
        bar = true;
    }

    public void setBar() {
    }
}

class SpecialPlayer extends Player {
    private long    foo;

    public long getFoo() {
        return foo;
    }
}

使用这种结构,例如做

这样的事情是合法的
SpecialPlayer sp = new SpecialPlayer();
sp.getFoo(); //method specific to special player
sp.setBar(); //method inherited from Player
如果你想让SpecialPlayer成为一名玩家,那么将一个Player包装在SpecialPlayer中是没有意义的。

更新:如何将现有播放器中的数据分配到SpecialPlayer

在类似内存空间的类上思考。例如

Object SpecialPlayer

[X] money
[X] bar
[X] foo

Object Player

[X] money
[X] bar
[ ] foo

所以很容易看出你可以做类似

的事情
Player p = new SpecialPlayer()

因为p必须只知道来自“money”和“bar”的数据,尽管“foo”仍然存在,但从p的角度来看,这是无关紧要的,因为所有p都知道是“钱”和“bar”< / p> 相反,当然,事实并非如此,因为

SpecialPlayer sp = p

将强制解释器解决为p分配字段“foo”的问题。这里有两种情况

  1. p实际上是一个SpecialPlayer,所以你可以像sp =(SpecialPlayer)p那样对它进行类型转换。解释器只会意识到p最初是作为SpecialPlayer创建的,然后,某处有一个“foo”。
  2. p不是SpecialPlayer,而是纯粹的播放器。在这种情况下,解释器无法猜出“foo”必须具有的值。在java中,没有办法让SpecialPlayer使用与Player相同的字段进行初始化,并假设“foo”为空。在这种情况下,您必须手动执行此分配,创建一个方法,如

    public void valueOf(Player p){
        this.money = p.money;
        this.bar = p.bar;           
    }
    
  3. 当然,这可能是一个静态方法,它返回一个SpecialPlayer,或者你计划的构造函数。但问题是一样的。如何将“foo”分配给SpecialPlayer。 java解释器只是“喜欢”不将它假设为null(或任何其他默认值),并让开发人员实现它。

    更新#2 - 这里有一个很好的问题

    为什么不能(或者不应该)将超类分配到子类中,所以我可以复制我需要的属性并假设剩下的属性为空?

    例如

     Player p = new Player()
     p.setMoney(1)
     p.setBar(bar)
    
     SpecialPlayer sp = p
    

    含义

     sp.setMoney(1)
     sp.setBar(bar)
     sb.setFoo(null)
    

    好吧,在java中,当你将一个对象分配给另一个对象时,你实际上并没有将值从一个复制到另一个。你只是创建一个链接。这就是你可以分配

    的原因
     SpecialPlayer sp = new SpecialPlayer()
     //set SpecialPlayer attributes
     Player p = sp
     SpecialPlayer sp2 = (SpecialPlayer)p;
    

    不会丢失任何信息。

    如果我们正在复制值,上面的代码只会删除最后一行中“foo”的值,因为Player无处放置“foo”

    这样好吗?也许。想象一下,如果将一个对象分配给另一个对象,我们会执行类似于所有值属性的“深层复制”的事情。

    想象一下典型的持久性实体,它们很多时候是对象的复杂图形,有时非常深,有时还有周期。你会遇到很多内存问题。

答案 1 :(得分:1)

如果您希望能够在运行时向现有类添加功能,那么您应该研究动态代理和/或面向方面编程(AOP)。

有关创建实现给定接口但可以根据需要传递调用的代理对象的信息,请参阅http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Proxy.html

或者,您需要重新评估您的班级模型。具体来说,如果玩家有时可以成为SpecialPlayer ,那么SpecialPlayer与玩家之间并没有真正的'is-a'关系。更有可能的是,玩家与某些可能或可能不具备的功能存在“拥有”关系。例如,如果您通过选择某种启动来成为一个SpecialPlayer,那么应该通过Player上可能返回null的某种属性或库存功能来表示。遗憾的是,您的示例过于抽象,无法就重构中的最佳行动方案提供任何具体建议。

此外,setBar()应该被称为toggleBar()

答案 2 :(得分:0)

  

好的,我知道extends关键字应该使SpecialPlayer成为播放器,但是如何将我的Pl​​ayer实例传递给SpecialPlayer我不能使用Player的构造函数,因为它已经在使用新的Player(1234)之前构建; < / p>

您无法扩展Player的现有实例,使其成为SpecialPlayer,它只是不起作用。

可以克隆一个Player <{1}}

SpecialPlayer

答案 3 :(得分:0)

我认为这可能是你想要做的。

public SpecialPlayer(Player player) {
    super(player.money);
    bar=player.bar;
    foo = System.currentTimeMillis();
}

specialPlayer会将玩家的金钱和金条传递给它。

或者,如果您想将每个字段设置为与Player相同,则可以尝试:

public SpecialPlayer(Player player) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException{
    super(player.money); //Still need to do this
    for(Field sfield:player.getClass().getDeclaredFields();){
        sfield.setAccessible(true);
        sfield.set(this, sfield.get(player));
    }
}

这会将在SpecialPlayer中声明的新Player的所有字段设置为player中传递的字段的值。

为了能够更改原始Player的状态并将其反映在新的SpecialPlayer中,因为您的更新要求您可以将所有对原始Player的引用更改为指向新的SpecialPlayer

Player player= new Player(123);
SpecialPlayer special = new SpecialPlayer(player);
player=special;

要考虑的另一件事是,您是否真的想要使用与旧版SpecialPlayer相同的字段创建新的Player,或者您实际上只想制作旧的Player特别的?制作SpecialPlayer构造函数可能更容易:

public SpecialPlayer(int money){
    super(money);
}

然后对于所有后来变得特别的球员,你会像这样创造:

Player player1 = new SpecialPlayer(123);
Player player2 = new SpecialPlayer(345);

然后当你希望它们变得特别时,将它们投射到SpecialPlayer

SpecialPlayer special = (SpecialPlayer)player1;

这样,当您使播放器变得特别时,您无需更改引用或创建新对象。

您还可以在SpecialPlayer中添加一个方法来执行构造函数当前所执行的操作,并在您想要使该播放器特殊时调用它:

public void setSpecial(){
    foo = System.currentTimeMillis();
}  

SpecialPlayer special = (SpecialPlayer)player1;
special.setSpecial();

你可能还需要一个可以在玩家setSpecial()时切换的标志,以区分他们是否应该使用重写方法或恢复为超级方法。

相关问题