2

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?

Myoch
  • 793
  • 1
  • 6
  • 24
  • The return value from `filterOn` is `MyClass`, not `MySubClass`, so the exception is valid enough. Change the type of `result` to fix it. – Henrik Aasted Sørensen Dec 20 '16 at 10:28
  • `MyClass result=new MyClass();` in your filter method has problem. you create super instance, and returned at the end, you cannot cast it into subtype. I guess you may want to override the filterOn method in MySubClass. – Kent Dec 20 '16 at 10:29
  • The easiest solution would be not to cast `subClass2` as it is not a `MySubClass` intance. If you need an instance of `MySubClass`, than simply overwrite `filteron` and return a `MySubClass` instance instead – n247s Dec 20 '16 at 10:30

4 Answers4

2

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();
   }

}
davidxxx
  • 125,838
  • 23
  • 214
  • 215
  • Easy solution :D . Would there be a way to declare my methods using some kind of "generics" trick (which I don't master at all)? – Myoch Dec 20 '16 at 10:32
  • Indeed :) Yes you could could but you should use reflection if you want avoid the pseudo factory method `createSpecificInstance()`, – davidxxx Dec 20 '16 at 10:40
  • Could you explain a bit more, please? you're saying that I can use generics (but how?) only if I use reflection (like using the Method class?), am I right? – Myoch Dec 20 '16 at 10:46
  • No you are wrong but I was not clear :) What I mean is that your use case is not necessarily a good candidate to show you the interest of generics. The main advantage I see with the shown code is forcing subclasses to declare the same type in the generic declaration of the class and in the method which creates a specific instance. To achieve it, you should have a abstract base class with a abstract method using generic and your two concrete classes should inherit from it. Your design is not like that : you have two classes : one is concrete and the other one inherits from the concrete class. – davidxxx Dec 20 '16 at 11:12
  • Ok, thank you. I really think there is something wrong in my Class design. I will see whether there is a way to make the superclass abstract or not... – Myoch Dec 20 '16 at 11:17
  • Sorry but giving a solution using generics with your design would be clumsy but don't hesitate to open a new question with this code or another about how you could use generics with.I am sure someone addresses it very well :) – davidxxx Dec 20 '16 at 11:19
0
(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

Community
  • 1
  • 1
Azodious
  • 13,752
  • 1
  • 36
  • 71
0

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;
}
fer-rum
  • 211
  • 1
  • 5
0

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);
    }

}
Community
  • 1
  • 1
Ole V.V.
  • 81,772
  • 15
  • 137
  • 161