Using method that return MyClass object within MyClass when MySubClass extends MyClass

447 views Asked by At

I have trouble subclassing a very simple class that has methods returning the initial class too.

public class MyClass {

   public MyClass(){
     }

   public MyClass filterOn(String something){
       MyClass result=new MyClass();
       result.doSomethingUsingThisInstance(this, something);

      return result;
   }

}


 public class MySubClass extends MyClass{
   ....
 }

Ok, now if I want to call this:

  MySubClass subClass=new MySubClass();
  MySubClass subClass2=(MySubClass)subClass.filterOn("Hello World");

Then I have a java.lang.ClassCastException:cannot cast MyClass to MySubClass

How to prevent this?

4

There are 4 answers

6
davidxxx On BEST ANSWER

Override the filterOn() method to create the instance you wish in MySubClass :

 public class MySubClass extends MyClass{

    public MyClass filterOn(String something){
       MySubClass result = new MySubClass();
       result.doSomethingUsingThisInstance(this, something);
       return result;
    }
   ....
 }

You could also avoid duplication in filterOn() method by introducing a method in MyClass to create a instance of the current class that we override in subclass:

public class MyClass {

   public MyClass(){
     }

   public MyClass createSpecificInstance() {
     return new MyClass();
   }

   public MyClass filterOn(String something){
       MyClass result = createSpecificInstance();
       result.doSomethingUsingThisInstance(this, something);

      return result;
   }

}

Now Sub class only override createSpecificInstance() :

public class MySubClass  extends MyClass {

   public MyClass createSpecificInstance() {
     return new MySubClass();
   }

}
0
Anonymous On

This is a side of the problem of covariance. See for example Covariance and contravariance. And Demonstrate covariance and contravariance in Java?. It’s not really where Java excels, but there are a couple of options.

First, when overriding a method, you are allowed to declare a more specific return type. For example, in MySubClass you may declare:

@Override
public MySubClass filterOn(String something) {
    // ...
}

Now this doesn’t solve your problem. You still need a way for this method to create and do something to a MySubClass object. What it does do, it frees the client code from needing a cast at all. You may take the implementation of the method form davidxxx’s answer (provided that result.doSomethingUsingThisInstance() is protected or public):

@Override
public MySubClass filterOn(String something) {
    MySubClass result = new MySubClass();
    result.doSomethingUsingThisInstance(this, something);
    return result;
}

You may be annoyed to have to duplicate this method in all you subclasses. If the real work stays in result.doSomethingUsingThisInstance() I should think you may be able to live with it.

The other idea is to clone() for producing an object of the correct runtime type:

public class MyClass implements Cloneable {

    public MyClass filterOn(String something) {
        try {
            MyClass result = (MyClass) this.clone();
            result.doSomethingUsingThisInstance(this, something);

            return result;
        } catch (CloneNotSupportedException cnse) {
            throw new AssertionError(cnse);
        }
    }

}

I’d think twice before using this idea, though. One reason your clone contains data that you don’t want to be in there, so you may have to make sure it’s cleared before working on the clone. You may still combine it with returning the specific subtype in the subclass, now it just requires a cast (still inside the implementation, nothing the client needs to worry about):

public class MySubClass extends MyClass {

    @Override
    public MySubClass filterOn(String something) {
        return (MySubClass) super.filterOn(something);
    }

}
0
Azodious On
(MySubClass)subClass.filterOn("Hello World");

and

public MyClass filterOn(String something)

You can not cast base class to derived class. if you do, you'll get that exception, ClassCastException

Read this: java.lang.ClassCastException

0
fer-rum On

You try to cast your return value to a derived type, which does not work and results in the class cast exception.

Reason: Casting upwards in the type hierarchy works, since your base class requires only attributes/ methods the derived class (trivially) has. The other way around does not work since it may result in problems. Consider:

class Base {
// some stuff here
}

class Derived1 extends Base {
  private int foo;
}

class Derived2 extends Base {
  private String bar;
}

Base myObject = new Derived1(); 
// this works, Derived1 has everything Base requires
// myObject.foo still exists, but can not be trivially accessed.

Derived2 myOtherObject = (Derived2)myObject;
// Now what? 
// What happens to myObject.foo? 
// Where does myOtherObject.bar come from?

What can you do in your situation:

  • If the implementations of filterOn() are very different depending on the concrete implementation of the derived class, make it abstract in the base class and re-implement it in the derived class.
  • Check if you have a design problem. Do you really need to cast the result of filterOn() to the derived class?
  • Use Generics. Remember you can use a derived class as generic in a base class. This only works if the implementation of filterOn() is exactly the same for each subclass (except the types of course).
  • Provide a constructor that allows creating a derived class instance from the base class. Use it instead of the cast. Something in the line of
Derived1(Base baseObject){
  // copy all the stuff from the base object
  this.foo = 0; // initialize the rest
}
  • Maybe inheritance is not what you need. Composition has the tendency to beat inheritance a lot (Explained nicely here). So you can try that:
class BetterThenDerived {
  private Base myBaseObject;
  private int foo;
}