Добро пожаловать в мир Двойная динамическая отправка .
AFAIK, вы не можете сделать это легко на Java. Вы можете сделать это двумя способами: Quick'n'dirty и Visitor way:
Quick'n'dirty
Вам необходимо задать тип объекта, поэтому вам понадобится метод стирки для Fruit, который перенаправит вызов нужной функции в соответствии с ее типом:
public void wash(Fruit f)
{
if(f instanceof Apple)
{
wash((Apple) f) ;
}
else if(f instanceof Peach)
{
wash((Peach) f) ;
}
else
{
// handle the error, usually through an exception
}
}
Проблема с quick'n'dirty заключается в том, что компилятор не скажет вам, что существует новый действующий Fruit (например, Orange), который в настоящее время не обрабатывается методом стирки.
посетителей
Вы можете использовать шаблон посетителя для фруктов:
public abstract class Fruit
{
// etc.
public abstract void accept(FruitVisitor v) ;
}
public class Apple extends Fruit
{
// etc.
public void accept(FruitVisitor v)
{
v.visit(this) ;
}
}
public class Peach extends Fruit
{
// etc.
public void accept(FruitVisitor v)
{
v.visit(this) ;
}
}
И определить посетителя как:
public interface class FruitVisitor
{
// etc.
// Note that there are no visit method for Fruit
// this is not an error
public void visit(Apple a) ;
public void visit(Peach p) ;
}
А потом, посетитель вашего кейса:
public class FruitVisitorWasher : implements FruitVisitor
{
// etc.
// Note that there are no visit method for Fruit
// this is not an error
// Note, too, that you must provide a wash method in
// FruitVisitorWasher (or use an anonymous class, as
// in the example of the second edit to access the
// wash method of the outer class)
public void visit(Apple a)
{
wash(a) ;
}
public void visit(Peach p)
{
wash(p) ;
}
}
В итоге ваш код может быть
FruitVisitorWasher fvw = new FruitVisitorWasher() ;
for( Fruit f: arguments)
{
f.accept(fvw) ;
}
Et voilà ...
Шаблон Visitor имеет то преимущество, что компилятор сообщит вам, если вы добавили другой Fruit (например, Orange), в котором вы закодировали метод accept, и если вы забыли обновить шаблон FruitVisitor для его поддержки.
И, наконец, шаблон Visitor расширяемый: вы можете иметь FruitVisitorWasher, FruitVisitorEater, FruitVisitorWh независимо, добавляя их без необходимости изменять Fruit, Apple, Peach и т.д ..
Одна ловушка, однако, вы должны вручную написать в каждом классе Fruit метод accept (который является действием копирования / вставки), потому что именно этот метод выполняет всю работу по «знанию» правильного типа Fruit.
Редактировать
Если вы выберете решение Quick'n'dirty, решение Самуэля Парсонэга может оказаться даже лучше, чем у меня:
Как перебрать этот универсальный список с подстановочными знаками?
, который использует рефлексию Java (я программист на C ++, поэтому рефлексия не является естественным решением ... Мне плохо в этом ...). Я нахожу его решение довольно элегантным, даже если оно пахнет каким-то образом (все проверки будут выполняться во время выполнения, так что вам лучше убедиться, что все в порядке ... Опять же, на фоне C ++: если что-то можно сделать, или ошибка может быть обнаружен во время компиляции, поэтому следует избегать его перемещения во время выполнения)
Редактировать 2
Джон Ассимптот прокомментировал:
Шаблон Visitor, когда вы пишете, не подходит, так как я не могу добавить метод стирки в Fruit.
Так что я предложу встроенный код, чтобы доказать, что wash () не должен находиться внутри Fruit для работы.
(я превратил FruitVisitor из абстрактного класса в интерфейс, что лучше)
Давайте представим, что цикл for находится внутри метода bar класса Foo, который имеет собственный метод мытья:
public class Foo
{
public wash(Apple a) { /* etc. */ }
public wash(Peach p) { /* etc. */ }
public bar(List<? extends Fruit> arguments)
{
for( Fruit f: arguments)
{
wash(f) ; // we wand the right wash method called.
}
}
}
Вы хотите вызвать правильный метод стирки, поэтому приведенный выше код не будет работать правильно.
Давайте повторно использовать шаблон FruitVisitor для исправления этого кода. Мы будем использовать анонимный класс внутри метода bar:
public class Foo
{
public void wash(Apple a) { System.out.println("Apple") ; }
public void wash(Peach p) { System.out.println("Peach") ; }
public void bar(List<? extends Fruit> arguments)
{
FruitVisitor fv = new FruitVisitor()
{
public void visit(Apple a)
{
wash(a) ; // will call the wash method
// of the outer class (Foo)
}
public void visit(Peach p)
{
wash(p) ; // will call the wash method
// of the outer class (Foo)
}
} ;
for(Fruit f: arguments)
{
f.accept(fv) ;
}
}
}
Теперь это работает, и во Фруктах нет метода стирки.
Обратите внимание, что этот код был протестирован на 1.6 JVM, поэтому я могу предоставить полный код при необходимости.