Reputation: 5834
I want to do something like a super()
but without passing anything to the constructor.
Something like copying all the variables in their current states, which were set to methods inside that class.
I want to avoid doing stuff like getPlayer()
in a wrapper class when the wrapper class is the Player just with extra methods.
I don't want to move these extra methods into the Player class because it's not part of the Player class it's something a Player class may become into but not at all times.
I still want to be able to run constructor of the SpecialPlayer with it's own variables it has to set while still maintaining it as a Player from whatever instance I pass into it.
The Player
class variables may change at any time while it's casted into a SpecialPlayer
class and the SpecialPlayer
class should have up to date variables same as Player
at all times.. Yes it has to the same reference exactly at all times by the looks of it.
Here is some pseudo code I wrote to try to illustrate the problem
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.
Upvotes: 1
Views: 3402
Reputation: 15517
I think this might be what you want to do.
public SpecialPlayer(Player player) {
super(player.money);
bar=player.bar;
foo = System.currentTimeMillis();
}
A new specialPlayer
will have the money and bar of the player passed to it.
Alternatively, if you want to set every field to the same as the Player
passed you could try this:
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));
}
}
This will set all of the fields of the new SpecialPlayer
which are declared in Player
to the value of that field in the player
passed.
To be able to change the state of the original Player
and have it reflected in the new SpecialPlayer
as your update requires you could change all of your references to the original Player
to point to the new SpecialPlayer
.
Player player= new Player(123);
SpecialPlayer special = new SpecialPlayer(player);
player=special;
Another thing to think about, do you really want to make a new SpecialPlayer
with all the same fields as an old Player
, or do you actually just want to make the old Player
special? It might be easier to make the SpecialPlayer
constructor this:
public SpecialPlayer(int money){
super(money);
}
Then for all your players who might later become special you would create like this:
Player player1 = new SpecialPlayer(123);
Player player2 = new SpecialPlayer(345);
And then just cast them to SpecialPlayer
when you want them to be special.
SpecialPlayer special = (SpecialPlayer)player1;
This way you don't need to change your references or create new objects when you make your player special.
You could also add a method in SpecialPlayer
which does what the constructor currently does and call it whenever you want to make that player special:
public void setSpecial(){
foo = System.currentTimeMillis();
}
.
SpecialPlayer special = (SpecialPlayer)player1;
special.setSpecial();
You may also need a flag which could be toggled when a player is setSpecial()
to distinguish if they should use an overridden method or revert to the super method.
Upvotes: 0
Reputation: 6570
I think you're a little confused about how inheritance works
for example, this slightly changed version of your code below
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;
}
}
with this structure, it's legal for example to do things like
SpecialPlayer sp = new SpecialPlayer();
sp.getFoo(); //method specific to special player
sp.setBar(); //method inherited from Player
there's no sense on wrapping a Player inside SpecialPlayer, if you want SpecialPlayer to be a Player.
UPDATE: how to assign the data from an existent Player into a SpecialPlayer
Think on a class like a memory space. For example
Object SpecialPlayer
[X] money
[X] bar
[X] foo
Object Player
[X] money
[X] bar
[ ] foo
So it's easy to see that you can do something like
Player p = new SpecialPlayer()
because p must know only the data from "money" and "bar" and, despite "foo" is still there, from p point of view, it's irrelevant, because all p knows is "money" and "bar"
the opposite, sure, is not true, because
SpecialPlayer sp = p
will force the interpreter to resolve the problem of assigning the field "foo" for p. There are two situations here
p is not a SpecialPlayer, but a pure Player. In this case, there's no way for the interpreter to guess what value "foo" must have. In java, there's no way to just let SpecialPlayer be initialized with the same fields it has from Player and assume for example that "foo" is null. In this case, you have to manually do this assignment, creating a method such as
public void valueOf(Player p){
this.money = p.money;
this.bar = p.bar;
}
Of course, this could be a static method that returns a SpecialPlayer, or the constructor as you've planned. But the problem is the same. How to assign "foo" to SpecialPlayer. The java interpreter simply "prefer" not to assume it as null (or any other default value) and let the developer implement it.
UPDATE #2 - there's a good question here
why can't (or shouldn't) I assign a superclass into a subclass, so I can just copy the attributes that I need and assuming the remaining ones are null?
for example
Player p = new Player()
p.setMoney(1)
p.setBar(bar)
SpecialPlayer sp = p
meaning
sp.setMoney(1)
sp.setBar(bar)
sb.setFoo(null)
?
well, in java, when you assign an object into another, you're not actually copying the values from one into another. You're just creating a link. That's why you can assign
SpecialPlayer sp = new SpecialPlayer()
//set SpecialPlayer attributes
Player p = sp
SpecialPlayer sp2 = (SpecialPlayer)p;
without losing any information.
If we were copying the values instead, the code above would just erase the value of "foo" in the last line, because Player had no place to put "foo"
Is that good? Maybe. Imagine what would happen if, assigning one object into another, we were performing something like a "deep copy" of all the value attributes.
Imagine typical persistent entities, that are many times complex graphs of objects, sometimes very deep, sometimes with cycles. You would have a lot of memory problems with that.
Upvotes: 3
Reputation: 86041
Okay I know the extends keyword should make the SpecialPlayer a Player but how do I pass my instance of a Player into the SpecialPlayer I can't use the Player's constructor because it's already constructed before hand using new Player(1234);
You can't extend an existing instance of a Player
to make it a SpecialPlayer
, it just doesn't work like that.
You can however make a clone of a Player
that's a SpecialPlayer
class Player
{
public Player(Player other)
{
//Copy all instance variables from other into this here
}
//Other constructors/instance variables/etc here
}
class SpecialPlayer extends Player
{
public SpecialPlayer(Player other)
{
super(other);
//Set SpecialPlayer-specific instance variables here
}
}
//Example of how to create a SpecialPlayer clone of a Player:
Player myPlayer = new Player();
SpecialPlayer specialPlayer = new SpecialPlayer(myPlayer);
Upvotes: 0
Reputation: 29240
If you want to be able to, at runtime, add functionality to an existing class, then you should be looking into dynamic proxies and/or aspect oriented programming (AOP).
See http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Proxy.html for information about creating Proxy objects that implement a given interface but can pass-through calls as desired.
Alternatively, you need to re-evaluate your class model. Specifically, if a Player can become a SpecialPlayer sometimes, then SpecialPlayer doesn't really have an 'is-a' relationship with Player. More likely Player has a 'has-a' relationship with some functionality that might or might not be available. For instance, if you become a SpecialPlayer by picking up some sort of powerup, then that should be expressed by some kind of attribute or inventory functionality on Player that might return null. Unfortunately your example is too abstract to give you any concrete advice on the best course of action in refactoring.
Also, setBar()
should be called toggleBar()
.
Upvotes: 1