Доступ к дочерним классам из родительского - Java

Узнай цену своей работы

Формулировка задачи:

Многим известно, что родительский класс ничего не должен знать о своих потомках. Упоминание дочерних классов в родительском считается плохим тоном, нарушающим принципы ООП и т.п. (в Java я знаю только один случай такого - это класс Number). Но у меня возникла ситуация, в которой задача прекрасно решается этим самым "плохим тоном". А пишу я сюда, потому что практически уверен, что имеется иной, более правильный способ, и, быть может, вы его подскажете. Задача самая что ни на есть типичная. Имеется

список объектов, который выводится в GUI-списке

(неважно, будь то ListView или TreeView). Объекты разные и потому у них очень много различающихся полей. Но все они - потомки одного класса, назовем его Animal:
abstract class Animal {
    Property<String> name;
    Property<Double> weight;
    ...
}
 
class Bird extends Animal {
    Property<String> color;
    Property<Boolean> flying;
    Property<Double> tail_length;
    ...
}
 
class Fish extends Animal {
    Property<Integer> type;
    Property<Double> habitat_depth;
    ...
}
 
// и еще штук 20 потомков Animal
Соответственно, GUI-список создается как

ListView<Animal>

из смешанного массива

Animal[]{new Bird(), new Fish(), new Fish(), new Worm(), new Fish(), ...}

. И как только пользователь выделит в этом ListView какой-либо объект, то в программе где-нибудь справа будут выведены

только те

поля TextField, которые необходимы для редактирования

только выделенного

объекта. При этом, разумеется, происходит связка полей TextField с Property текущего объекта:
Animal animal = list_view.getSelected(); // на самом деле метод другой, но это не суть
BidirectionalBinding.bind(text_field_weight.textProperty(), animal.weight);
BidirectionalBinding.bind(text_field_birdcolor.textProperty(), animal.color); // ошибка компиляции! unknown field 'color'
Очевидно, что на момент написания кода компилятор не знает, какой именно дочерний класс будет выделен, поэтому getSelected() всегда возвращает только Animal. Но тогда как определить какое животное сейчас выделено, чтобы получить доступ к свойствам дочерних классов? Как делаю я. Т.к. получить можно только родительский класс, то в него я и добавляю ссылки на всех его потомков:
abstract class Animal {
    Bird asBird = null; // !!!
    Fish asFish = null; // !!!
    ...
}
 
class Bird extends Animal {
    Bird() {
        asBird = this;
    }
}
 
class Fish extends Animal {
    Fish() {
        asFish = this;
    }
}
Таким образом, каждый потомок имеет ссылку только "на самого себя", а ссылки, ведущие на всех остальных "братьев и сестер", всегда равны null. Теперь перед биндингом остается только проверить, каким именно животным является выделенный объект, и тут же сделать необходимые связки:
Animal animal = list_view.getSelected();
BidirectionalBinding.bind(text_field_weight.textProperty(), animal.weight);
if (animal.asBird != null) {
    BidirectionalBinding.bind(text_field_birdcolor.textProperty(), animal.asBird.color);
    ...
} else if (animal.asFish != null) {
    BidirectionalBinding.bind(text_field_fishtype.textProperty(), animal.asFish.type);
    ...
} else if ...
Просьба не придираться к терминологии или к коду (он только для примера). Вопрос у меня лишь в том,

существует ли иное, более элегантное решение для доступа к полям дочерних классов, имея на входе только родительский?

Или мой подход вполне оправдан для подобных случаев?

Решение задачи: «Доступ к дочерним классам из родительского»

textual
Листинг программы
// а это непосредственно сами классы для представления данных
 
abstract class Animal {
    Property<String> name;
    Property<Double> weight;
    ...
    void bind(FXMLController controller) {
        BidirectionalBinding.bind(controller.text_field_weight.textProperty(), weight);
        ...
    }
}
 
class Bird extends Animal {
    Property<String> color;
    ...
    @Override void bind(FXMLController controller) {
        super.bind(controller);
        BidirectionalBinding.bind(controller.text_field_birdcolor.textProperty(), color);
        ...
    }
}
 
class Fish extends Animal {
    Property<Integer> type;
    ...
    @Override void bind(FXMLController controller) {
        super.bind(controller);
        BidirectionalBinding.bind(controller.text_field_fishtype.textProperty(), type);
        ...
    }
}

ИИ поможет Вам:


  • решить любую задачу по программированию
  • объяснить код
  • расставить комментарии в коде
  • и т.д
Попробуйте бесплатно

Оцени полезность:

10   голосов , оценка 3.9 из 5