0

I have a two simple Java classes and I am trying to understand more about cloning and how to cast between different levels of classes. This is the superclass Vehicle.

public class Vehicle implements Cloneable 
{
     public int x;

     public Vehicle(int y) { 
          x = y;
     }

     @Override public Object clone() { 
      Object result = new Vehicle(this.x);
      // Location "A"
      return result;
    }
}

And this is the subclass Truck

public class Truck extends Vehicle 
{
   private int y;

   public Truck(int z) {
       super(z); 
       y = z;
   }

   @Override public Object clone() {
      Object result = super.clone();
      System.out.println(result.getClass());
      ((Truck) result).y = this.y; 
      return result; 
    }
}

I am trying to get a copy of Truck while using the superclass to clone but having issues with downcasting not being allowed. I am not really sure how to fix this or where the error is.

I would like to have: Truck a = new Truck(1) and Truck b = a.clone() become the same object.

User 965
  • 179
  • 1
  • 4
  • 17
  • 1
    Your super.clone() is returning a Vehicle in `result`, so it's a Vehicle, not a Truck. – tom Sep 18 '18 at 01:17
  • why not Truck a = new Truck(1) and Truck b = a; ? since you're trying to get the same object? – Wale Sep 18 '18 at 01:21
  • Replace the `clone()` method in `Vehicle` with `@Override public Object clone() { return super.clone(); }`. The `Object` version of `clone()` works fine for what you're doing (including returning an object of the correct type); you just need to expose it as `public`. Better yet, don't use `clone()` and use a copy constructor instead. See [this thread](https://stackoverflow.com/q/1106102/535871). – Ted Hopp Sep 18 '18 at 01:23

2 Answers2

3

You're misusing clone(). All the instantiation and field copying is done by the super implementation when you implement Cloneable. Your Vehicle implementation should look like this:

@Override
public Vehicle clone() {
    try {
        return (Vehicle)super.clone();
    } catch (CloneNotSupportedException e) {
        throw new AssertionError();
    }
}

And you can optionally override it in Truck like this:

@Override
public Truck clone() {
    return (Truck)super.clone();
}
shmosel
  • 49,289
  • 6
  • 73
  • 138
  • Is catching `CloneNotSupportedException` really necessary? After all, `Vehicle` is declared to implement `Cloneable`, so that exception will never be thrown. – Ted Hopp Sep 18 '18 at 01:25
  • @TedHopp It's a checked exception, so there's no choice. But it should never actually happen, hence the assertion error. – shmosel Sep 18 '18 at 01:26
  • I don't think this is addressing the biggest problem, which is casting objects. – clabe45 Sep 18 '18 at 01:29
  • @clabe45 What problem? – shmosel Sep 18 '18 at 01:31
  • See my answer and tom's comment on the question – clabe45 Sep 18 '18 at 01:32
  • 1
    @clabe45 - The biggest problem is the wrong type of object being returned from `Vehicle#clone()`. The symptom of the problem is the problem caused by the cast, but that's not the problem itself. – Ted Hopp Sep 18 '18 at 01:33
  • oops. Forgot about it being a checked exception. – Ted Hopp Sep 18 '18 at 01:33
  • @TedHopp thank you. That's essentially what I meant; the instance is of the wrong type, and casting won't change that. – clabe45 Sep 18 '18 at 01:34
  • @clabe45 No, I don't think you're getting it. If `clone()` is implemented correctly, a copy with the same runtime type is returned, so the cast works fine. – shmosel Sep 18 '18 at 01:37
  • @shmosel Yes, that's what I said in my answer. Right now, an instance of `Vehicle` is being returned by `Truck.clone`, and I think the OP's misconception is that casting it will make it a `Vehicle`. I agree, if `Truck.clone` is implemented correctly, an instance of `Truck` will be returned, not `Vehicle`. – clabe45 Sep 18 '18 at 01:42
0

When you cast from one class to another, you are not changing the actual object, but making it accessible in a different way.

class A { public int x; }
class B extends A { public int y; }
A a = new A();
B aB = new B();
System.out.println(aB); // A@...
System.out.println(aB.y); // throws an error

When you call super.clone(), it creates a Vehicle instance, and Vehicle instances do not have y.

Instead, you probably want to initiate the object and not call super.clone() in Truck.clone:

    return new Vehicle(this.y);
clabe45
  • 2,354
  • 16
  • 27