Search code examples
javacastingclonecloning

Java: How do I cast a cloned object to it's original subclass?


Imagine super class A:

public class A {
    private int x;

    A(int x){
        this.x = x;
    }

    public int getX() {
        return x;
    }
}

And it's 2 subclasses B and C:

public class B extends A{
    private int y = 2;

    B(int x){
        super(x);
    }
}
public class C extends A{
    private int y = 3;

    B(int x){
        super(x);
    }
}

Now imagine a list of objects that all inherit class A. I want to iterate through that list and clone each object and also cast it to it's original subclass (B or C). This is how I tried to do that:

public static void main(String args[]) {
    ArrayList<A> aList = new ArrayList<A>();
    aList.add(new B(5));
    aList.add(new C(6));

    ArrayList<A> aClones = new ArrayList<A>();

    for(A a : aList) {
        A aNew = new A(a.getX());
        a.getClass().cast(aNew);
        aClones.add(aNew);
    }
}

But this gives me an error saying that I can't cast A to B. How am I able to achieve this without knowing up front what subtype of object I'm dealing with? I could do an if-statement inside the for loop for every type of subclass to check if a is an instance of that subclass and then simply create a new instance of that particular subclass and clone it that way. However, in my situation there will be many more subclasses than just B and C and many more might come. I don't want the for loop to contain any knowledge about the existing subclasses of A. Is this possible?


Solution

  • This can't be done in the way you're writing it.

    The reason is fairly simple: B and C have an is-a relationship with A, but A doesn't share the same is-a relationship with either B or C.

    This is chiefly because you're trying to iterate using the superclass to describe everything.

    for(A a : aList) { }
    

    While you're iterating, you do not have any way of knowing from type inference alone what concrete class A actually is.

    So this fails, as expected.

     a.getClass().cast(aNew);
    

    Without explicitly asking the class what type it is, you have no way of doing this.

    This means that you'll be writing a lot of instanceof of statements if you plan to expand upon this.

    for(A a : aList) {
        if(a instanceof B) {
            B b = new B(a.getX());
            aClones.add(b);
        }
        if(a instanceof C) {
            C c = new C(a.getX());
            aClones.add(c);
        }
    }