4

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 Things 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 Things 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

  • Make a method 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.
  • Simply use reflection to instantiate the result of getClass() with the new values

Is there a way to do this without reflection or a better way to design the objects?

itdoesntwork
  • 4,666
  • 3
  • 25
  • 38

2 Answers2

1

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.

Community
  • 1
  • 1
Nir Alfasi
  • 53,191
  • 11
  • 86
  • 129
  • This is pretty much my overall design. Do you have any suggestions to improve it? – itdoesntwork Aug 01 '14 at 19:05
  • It seems to me that it'll make more sense to implement `transform()` in the child classes, or alternatively, create a `copy()` in each of the child classes and use it inside `transform` – Nir Alfasi Aug 01 '14 at 19:28
1

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;
    ....
  }
Eran
  • 387,369
  • 54
  • 702
  • 768
  • Remember to implement `Cloneable` – mattbdean Aug 01 '14 at 18:30
  • @whowantsakookie I agree, I simply didn't include the full class definition. – Eran Aug 01 '14 at 18:32
  • 1
    [Clone interface is broken](http://www.artima.com/intv/bloch13.html) (and should be avoided) – Nir Alfasi Aug 01 '14 at 18:32
  • How can this work when `some_member` is a private field? – itdoesntwork Aug 01 '14 at 19:03
  • @itdoesntwork It works, because you are accessing a private field from the same class that introduced it. `clone` of `ThreeThing` would only clone members introduced by `ThreeThing`, `clone` of `Thing` would only clone members introduced by `Thing`, etc... – Eran Aug 01 '14 at 19:10