P
P
pqgg7nwkd42016-08-13 15:29:57
symfony
pqgg7nwkd4, 2016-08-13 15:29:57

How to make chains in inherited java classes?

Good afternoon.
Initial data: there is a desire to use Method chainig, because it is often convenient.
Let's take "animals" as an example.
Let's have a parent class for all animals that supports chains:

class Animal {
    public Animal setWeight(int newWeight) {
        return this;
    }
    public Animal setName(String newName){
        return this;
    }
}

// Как работает?
Animal animal = new Animal().setName("Murzik").setWeight(20);

// Удобно!

Everything is beautiful and simple. Now let's complicate the task. Let's add a cat:
class Cat extends Animal {
    public Cat setMeowVolume(int volume) {
        return this;
    }
}

There is a problem: methods setWeightand setNamereturn Animaland the following constructions will not work:
Cat cat1 = new Cat().setName("Murzik").setWeight(20).setMeowVolume(100); // Ошибка: setMeowVolume - не найден
Cat cat2 = new Cat().setMeowVolume(100).setName("Murzik").setWeight(20); // Ошибка: setWeight не возвращает Cat

You can, of course, apply casting. But it's terrible:
Cat cat1 = ((Cat)new Cat().setName("Murzik").setWeight(20)).setMeowVolume(100);
Cat cat2 = (Cat)new Cat().setMeowVolume(100).setName("Murzik").setWeight(20);

You can inherit all methods in 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. Наследование методов там, где не оно не требуется.

One more way. It is less bad, but still not as convenient as we would like:
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. Грамозкое и малопонятное объявление классов.

It would be great if all void methods on the call would return the object itself, or the following syntax would be possible:
class Animal  {
    public this setWeight(int newWeight) {

    }
    public this setName(String newName){

    }
}

class Cat extends Animal {
    this setMeowVolume(int volume) {
        
    }
}

Maybe I'm missing something?
How would you organize inherited classes with support for chain calls?

Answer the question

In order to leave comments, you need to log in

5 answer(s)
A
Alexey Skobkin, 2016-04-27
@skobkin

It is written to you: the magic method __toString () should not throw an exception, and at you - throws out. You are probably accessing something somewhere in this method that you haven't checked for existence.

I
Ivan Cherny, 2016-04-27
@paalomnik

it's hard to understand without the entity code, judging by the message that toasting cannot throw an exception - this is php
7

public function __toString()
{
    return $this->name ? $this->name : 'New';
}

name to replace with the desired property
another option: organize a catch there https://gist.github.com/magickatt/5434032#file-gis...

F
Fat Lorrie, 2016-08-13
@Free_ze

Override base methods.

R
Ruslan Lopatin, 2016-08-13
@lorus

To avoid unchecked warning, you can also do this:

class Animal <Return extends Animal<Return>> {
    public Return setWeight(int newWeight) {
        return self();
    }
    public Return setName(String newName){
        return self();
    }
    @SuppressWarnings("unchecked")
    protected Return self() {
        return (Return) this;
    }
}
class Cat<Return extends Cat<Return>> extends Animal<Return> {
    public Return setMeowVolume(int volume) {
        return self();
    }
}

D
Dmitry Alexandrov, 2016-08-13
@jamakasi666

I did not encounter similar tasks, and I never used chains. Of course, I'm far from such subtleties and problems as in your case, but maybe it's worth trying to write a silver bullet through annotation \ reflections?

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question