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