Как я могу добавить методы в класс и использовать их в цикле в стиле for его суперкласса?

#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 , так и приведение вниз обычно считаются плохой практикой. Чтобы избежать этого, вы могли бы реализовать шаблон посетителя. Вот шаги, которые вам нужно будет предпринять:

  1. Создайте интерфейс посетителя, подобный этому:

     interface FruitVisitor {
        void visit(Banana banana);
        void visit(Apple apple);
    }
      
  2. Пусть все фрукты принимают посетителя:

     abstract class Fruit {
        // ...
        public abstract void accept(FruitVisitor fv);
    }
    
    class Banana extends Fruit {
        public void accept(FruitVisitor fv) {
            fv.visit(this);
        }
    
        public void checkHowRoundBananaIs() { ... }
    }
      
  3. Создайте посетителя, который выполняет соответствующее действие для каждого типа фруктов, и передайте его в качестве аргумента 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 .

Конечно, есть лучшие способы, такие как шаблон посетителя (см. Другие сообщения) или извлечение функции в суперкласс. Хотя, я думаю, вы согласились с тем, что у вас есть некоторые проблемы с дизайном, и вы не хотите это исправлять.