Добрый день.
Исходные данные: есть желание использовать Method chainig, т. к. это часто удобно.
Возьмем в пример: "животные".
Пусть есть родительский класс для всех животных, который поддерживает цепочки:
class Animal {
public Animal setWeight(int newWeight) {
return this;
}
public Animal setName(String newName){
return this;
}
}
// Как работает?
Animal animal = new Animal().setName("Murzik").setWeight(20);
// Удобно!
Всё красиво и просто. Теперь усложним задачу. Добавим кота:
class Cat extends Animal {
public Cat setMeowVolume(int volume) {
return this;
}
}
Возникает проблема: методы
setWeight
и
setName
возвращают
Animal
и следующие конструкции работать не будут:
Cat cat1 = new Cat().setName("Murzik").setWeight(20).setMeowVolume(100); // Ошибка: setMeowVolume - не найден
Cat cat2 = new Cat().setMeowVolume(100).setName("Murzik").setWeight(20); // Ошибка: setWeight не возвращает Cat
Можно, конечно, применить кастинг. Но получается ужасно:
Cat cat1 = ((Cat)new Cat().setName("Murzik").setWeight(20)).setMeowVolume(100);
Cat cat2 = (Cat)new Cat().setMeowVolume(100).setName("Murzik").setWeight(20);
Можно наследовать все методы в Cat.
class Animal {
public Animal setWeight(int newWeight) {
return this;
}
public Animal setName(String newName){
return this;
}
}
class Cat extends Animal {
@Override
public Cat setWeight (int newWeight) {
super.setWeight(newWeight);
return this;
}
@Override
public Cat setName (String newName) {
super.setName(newName);
return this;
}
public Cat setMeowVolume(int volume) {
return this;
}
}
// Работает замечательно
Cat cat1 = new Cat().setName("Murzik").setWeight(20).setMeowVolume(100);
Cat cat2 = new Cat().setMeowVolume(100).setName("Murzik").setWeight(20);
// Проблемы:
// 1. Увеличение кода
// 2. Наследование методов там, где не оно не требуется.
Еще один способ. Он менее плох, но всё-же не так удобен как хотелось бы:
class Animal <Return extends Animal<Return>> {
public Return setWeight(int newWeight) {
return (Return)this; // Unchecked cast warning
}
public Return setName(String newName){
return (Return)this; // Unchecked cast warning
}
}
class Cat<Return extends Cat<Return>> extends Animal<Return> {
public Return setMeowVolume(int volume) {
return (Return)this; // Unchecked cast warning
}
}
// Работает:
Cat cat1 = new Cat<>().setName("Murzik").setWeight(20).setMeowVolume(100);
Cat cat2 = new Cat<>().setMeowVolume(100).setName("Murzik").setWeight(20);
// Проблемы:
// 1. Куча предупреждений Unchecked cast.
// 2. Класс Cat становится generic-ом со всеми вытекающими последствиями и предупреждениями. (Например, конструктор необходимо указывать со скобками: new Cat<>()).
// 3. Грамозкое и малопонятное объявление классов.
Вот было бы здорово, если бы все void методы про вызове возвращали бы сам объект, или был бы возможен такой синтаксис:
class Animal {
public this setWeight(int newWeight) {
}
public this setName(String newName){
}
}
class Cat extends Animal {
this setMeowVolume(int volume) {
}
}
Может быть я что-то упускаю?
Как бы вы организовали наследуемые классы с поддержкой chain вызовов?