"Есть ли способ выяснить тип возвращаемого значения во время выполнения без использования дополнительного параметра instanceof?"
В качестве альтернативного решения вы можете использовать шаблон посетителя , как это. Сделайте Animal абстрактным и сделайте его реализованным Visitable:
abstract public class Animal implements Visitable {
private Map<String,Animal> friends = new HashMap<String,Animal>();
public void addFriend(String name, Animal animal){
friends.put(name,animal);
}
public Animal callFriend(String name){
return friends.get(name);
}
}
Видимость означает, что реализация Animal готова принять посетителя:
public interface Visitable {
void accept(Visitor v);
}
И реализация посетителя может посетить все подклассы животного:
public interface Visitor {
void visit(Dog d);
void visit(Duck d);
void visit(Mouse m);
}
Так, например, реализация Dog будет выглядеть следующим образом:
public class Dog extends Animal {
public void bark() {}
@Override
public void accept(Visitor v) { v.visit(this); }
}
Хитрость в том, что, поскольку Dog знает, что это за тип, он может вызвать соответствующий перегруженный метод посещения посетителя v, передав «this» в качестве параметра. Другие подклассы реализуют accept () точно так же.
Класс, который хочет вызвать специфичные для подкласса методы, должен затем реализовать интерфейс Visitor следующим образом:
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(); }
}
Я знаю, что это намного больше интерфейсов и методов, чем вы рассчитывали, но это стандартный способ получить указатель на каждый конкретный подтип с точно нулевым экземпляром проверок и нулевым типом приведений. И все это делается стандартным языком, не зависящим от языка, так что это не только для Java, но любой ОО-язык должен работать одинаково.