#java #oop #generics
#java #ооп #общие сведения
Вопрос:
Допустим, у меня есть общий массив, подобный этому:
ArrayList<Fruit> fruits = new ArrayList<Fruit>();
Затем я добавляю множество разных фруктов, которые все расширяют класс fruit, и перебираю их
for (Fruit f : fruits) {
}
Если фрукт — это банан, я хочу проверить, насколько он круглый, поэтому..
for (Fruit f : fruits) {
if (f instanceof Bannana)
f.checkHowRoundBannanaIs();
}
Мне придется поместить checkHowRoundBannnaIs()
метод в класс fruit, даже если fruit может не быть бананом, потому что я не могу использовать функцию в f, если ее нет в классе fruit, иначе я получу ошибку неопределенного метода (или что-то в этом роде).
Это нормально для одного или двух методов, но через некоторое время это делает класс громоздким и уродливым. Итак, мой вопрос в том, как я могу добавить методы в класс и использовать их в цикле в стиле for его суперкласса?
Комментарии:
1. Примеры написаны на Java, но это не проблема Java.
2. Правильно. Эта проблема является общей для всех языков с единым механизмом отправки.
Ответ №1:
Итак, мой вопрос в том, как я могу добавить методы в класс и использовать их в цикле в стиле for его суперкласса?
Короткий ответ, вы действительно не можете.
Если вы хотите вызвать методы в теле цикла for, instanceof
и приведение вниз — это единственный способ, который я могу придумать:
for (Fruit f : fruits) {
if (f instanceof Banana)
((Banana) f).checkHowRoundBananaIs();
// ...
}
Хотя как instanceof
, так и приведение вниз обычно считаются плохой практикой. Чтобы избежать этого, вы могли бы реализовать шаблон посетителя. Вот шаги, которые вам нужно будет предпринять:
-
Создайте интерфейс посетителя, подобный этому:
interface FruitVisitor { void visit(Banana banana); void visit(Apple apple); }
-
Пусть все фрукты принимают посетителя:
abstract class Fruit { // ... public abstract void accept(FruitVisitor fv); } class Banana extends Fruit { public void accept(FruitVisitor fv) { fv.visit(this); } public void checkHowRoundBananaIs() { ... } }
-
Создайте посетителя, который выполняет соответствующее действие для каждого типа фруктов, и передайте его в качестве аргумента
accept
методу каждого фрукта в вашем списке:FruitVisitor fv = new FruitVisitor() { public void visit(Banana banana) { banana.checkHowRoundBananaIs(); } public void visit(Apple apple) { // ... } }; for (Fruit f : fruits) f.accept(fv);
Ответ №2:
Метод, наиболее похожий на то, что вы пробовали, заключался бы в приведении f
как Banana
:
if(f instanceof Bannana)
((Banana)f).checkHowRoundBannanaIs()
Я не уверен, действительно ли это то, что вы хотите, или нет.
Ответ №3:
Вам не нужно помещать checkHowRoundBanannaIs()
в Fruit
класс. Как только вы узнаете, что это f
экземпляр Banana
, вы можете безопасно выполнить приведение f
к типу Banana
. Теперь, когда у вас есть Banana
экземпляр, вы можете вызвать любой Banana
конкретный метод (включая checkHowRoundBananaIs()
.
for(final Fruit f : fruits)
{
if(f instanceof Banana)
{
final Banana b = (Banana) f;
b.checkHowRoundBananaIs();
}
}
Ответ №4:
Поскольку вы уже проверяете, является ли это бананом, вы могли бы привести его к банану, а затем выполнить проверку..
for(fruit f : fruits)
{
if(f instanceof Banana) {
Banana b = (Banana)f;
b.checkHowRoundBananaIs()
}
}
Ответ №5:
for(fruit f : fruits)
{
if(f instanceof Bannana)
f.checkHowRoundBannanaIs()
}
… это НЕ «цикл в стиле for его суперкласса». Логически говоря.
КАК вы это делаете, instanceof Bannana
вам нужно импортировать Bannana
(хотя, я полагаю, вы хотели ввести Banana вместо этого). После импорта нет причин не приводить f
к Banana
. Когда вы делаете это, у вас есть доступ ко всем методам Banana
.
Короче говоря, обычно вы не выполняете obj1 instanceof XXX
проверку, если вы на самом деле не хотите использовать obj1
as XXX
.
Конечно, есть лучшие способы, такие как шаблон посетителя (см. Другие сообщения) или извлечение функции в суперкласс. Хотя, я думаю, вы согласились с тем, что у вас есть некоторые проблемы с дизайном, и вы не хотите это исправлять.