Reputation: 4802
I want to make a class with N fields, then subclass it with N-1 fields, and in the subclass the first field should be filled in the constructor. Here's the simplest example:
class Thing {
protected int a;
protected int b;
public Thing(int a, int b) {
this.a = a;
this.b = b;
}
}
class ThreeThing extends Thing {
public ThreeThing(int b) {
this.a = 3;
this.b = b;
}
}
Now, I want to make a method for all Thing
s that respects immutability- it returns a new Thing
with 1 added to b
.
public Thing transform() {
return new Thing(this.a, this.b + 1);
}
However, when this method is inherited into ThreeThing
, the returned object is still a Thing
instead of ThreeThing
, so I'd have to override the method in ThreeThing
. This wouldn't be a big problem, but in my project, there are going to be lots of specialized Thing
s and I don't want to have to override the method in all of them if the behavior is intended to be the same.
Here are the possible solutions I have thought of, none of them satisfy me
clone
for Thing
and ThreeThing
that copies over the fields to a new instance, then use reflection to mutate the private fields to obtain the desired result. The only method that would be needed in ThreeThing
is clone
.getClass()
with the new valuesIs there a way to do this without reflection or a better way to design the objects?
Upvotes: 4
Views: 94
Reputation: 53535
Since the parent can't instantiate the child directly (because it doesn't "know" it) you can use reflection to do it:
class Thing {
protected int a;
protected int b;
public void setB(int b) {
this.b = b;
}
public void setA(int a) {
this.a = a;
}
public Thing(){}
public Thing(int a, int b) {
this.a = a;
this.b = b;
}
Thing copy() { // adding a copy method
return new Thing(a, b);
}
public Thing transform() throws IllegalAccessException, InstantiationException {
// Thing result = (Thing)this.getClass().newInstance(); // one way to do it (reflection)
Thing result = copy(); // a better way to do it
result.setA(a);
result.setB(b+1);
return result;
}
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
ThreeThing x = new ThreeThing(1);
System.out.println(x.a + " : " + x.b);
Thing y = x.transform();
System.out.println(y.a + " : " + y.b);
}
}
class ThreeThing extends Thing {
public ThreeThing(){}
ThreeThing(int b) {
this.a = 3;
this.b = b;
}
@Override
Thing copy() { // adding a copy method
return new ThreeThing(b);
}
}
That said, it's better to avoid using newInstance()
and if you find yourself using it - you might want to back up a few steps and check the overall design and see if it can be improved. As an example, I added a copy()
method which should be overridden in the child classes.
Upvotes: 1
Reputation: 393986
You can do it with just clone and no reflection.
Each non-abstract class of your hierarchy with have a clone method that creates an instance and passes it to another clone method that clones the members. Abstract classes in the hierarchy would only have the protected method that only clones the members.
So, for example, in the sub-class ThreeThing
you will have :
public Object clone ()
{
ThreeThing
thing = new ThreeThing ();
clone (thing);
return thing;
}
protected void clone (Thing other)
{
super.clone (other);
ThreeThing
thing = (ThreeThing) other;
thing.some_member = this.some_member;
....
}
Upvotes: 1