我想做一些像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.
答案 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”的问题。这里有两种情况
p不是SpecialPlayer,而是纯粹的播放器。在这种情况下,解释器无法猜出“foo”必须具有的值。在java中,没有办法让SpecialPlayer使用与Player相同的字段进行初始化,并假设“foo”为空。在这种情况下,您必须手动执行此分配,创建一个方法,如
public void valueOf(Player p){
this.money = p.money;
this.bar = p.bar;
}
当然,这可能是一个静态方法,它返回一个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成为播放器,但是如何将我的Player实例传递给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()
时切换的标志,以区分他们是否应该使用重写方法或恢复为超级方法。