Consider this example (typical in OOP books):
I have an Animal
class, where each Animal
can have many friends.
And subclasses like
"Is there a way to figure out the return type at runtime without the extra parameter using instanceof?"
As an alternative solution you could utilise the Visitor pattern like this. Make Animal abstract and make it implement Visitable:
abstract public class Animal implements Visitable {
private Map friends = new HashMap();
public void addFriend(String name, Animal animal){
friends.put(name,animal);
}
public Animal callFriend(String name){
return friends.get(name);
}
}
Visitable just means that an Animal implementation is willing to accept a visitor:
public interface Visitable {
void accept(Visitor v);
}
And a visitor implementation is able to visit all the subclasses of an animal:
public interface Visitor {
void visit(Dog d);
void visit(Duck d);
void visit(Mouse m);
}
So for example a Dog implementation would then look like this:
public class Dog extends Animal {
public void bark() {}
@Override
public void accept(Visitor v) { v.visit(this); }
}
The trick here is that as the Dog knows what type it is it can trigger the relevant overloaded visit method of the visitor v by passing "this" as a parameter. Other subclasses would implement accept() exactly the same way.
The class that wants to call subclass specific methods must then implement the Visitor interface like this:
public class Example implements Visitor {
public void main() {
Mouse jerry = new Mouse();
jerry.addFriend("spike", new Dog());
jerry.addFriend("quacker", new Duck());
// Used to be: ((Dog) jerry.callFriend("spike")).bark();
jerry.callFriend("spike").accept(this);
// Used to be: ((Duck) jerry.callFriend("quacker")).quack();
jerry.callFriend("quacker").accept(this);
}
// This would fire on callFriend("spike").accept(this)
@Override
public void visit(Dog d) { d.bark(); }
// This would fire on callFriend("quacker").accept(this)
@Override
public void visit(Duck d) { d.quack(); }
@Override
public void visit(Mouse m) { m.squeak(); }
}
I know it's a lot more interfaces and methods than you bargained for, but it's a standard way to get a handle on every specific subtype with precisely zero instanceof checks and zero type casts. And it's all done in a standard language agnostic fashion so it's not just for Java but any OO language should work the same.