Есть ли способ вернуть полиморфное это в Java? - PullRequest
0 голосов
/ 30 октября 2018

В Typescript есть эта концепция полиморфного возвращаемого типа this. https://www.typescriptlang.org/docs/handbook/advanced-types.html#polymorphic-this-types Пример:

export abstract class Animal {
    private name: string;

    public setName(name: string): this {
        this.name = name;
        return this;
    }
}

export class Dog extends Animal {
    private breed: string;

    public setBreed(breed: string): this {
        this.breed = breed;
        return this;
    }
}

export class FluffyDog extends Dog {
    private fluffiness: number;
    public setFluffiness(fluffiness: number): this {
        this.fluffiness = fluffiness;
        return this;
    }
}

export class Main {
    constructor() {
        const dog: FluffyDog = new FluffyDog()
            .setName('Fluffy')
            .setFluffiness(10)
            .setBreed('Lab');
    }
}

Есть ли что-нибудь эквивалентное в Java? Лучшее, что я придумал, это:

public abstract class Animal<T extends Animal<T>> {
    private String name;
    public T setName(String name) {
        this.name = name;
        return (T)this;
    }
}

class Dog extends Animal<Dog> {
    private String breed;
    public Dog setBreed(String breed) {
        this.breed = breed;
        return this;
    }
}


class Main {
    static {
        Dog dog =  new Dog()
                .setName("Fluffy")
                .setBreed("Lab");
    }
}

ИЛИ это:

public abstract class Animal {
    private String name;
    public <T extends Animal> T setName(String name) {
        this.name = name;
        return (T)this;
    }
}

class Dog extends Animal {
    private String breed;
    public <T extends Dog> T setBreed(String breed) {
        this.breed = breed;
        return (T)this;
    }
}

class FluffyDog extends Dog {
    private Long fluffiness;
    public <T extends FluffyDog> T setFluffiness(Long fluffiness) {
        this.fluffiness = fluffiness;
        return (T)this;
    }
}


class Main {
    static {
        FluffyDog dog =  new FluffyDog()
                .<FluffyDog>setName("Fluffy")
                .setFluffiness(10L)
                .setBreed("Lab");
    }
}

Кажется, что первый может быть разделен на подклассы только один раз.
Второй требует явных аргументов типа в некоторых ситуациях.
Есть ли способ вернуть полиморфное это в Java?

Ответы [ 2 ]

0 голосов
/ 30 октября 2018

С вашей первой альтернативой вы все равно можете сделать два уровня наследования. По общему признанию это не очень читабельно и понятно. К сожалению, дженерики были дополнением к Java, добавленной в 1.5, и многие шаблоны Java Bean не очень хорошо подходят для такого рода цепочечных методов. (Вы заметите, что большинство методов установки возвращают void в традиционных классах Java).

public abstract class Animal<T extends Animal<T>> {
    private String name;
    public T setName(String name) {
        this.name = name;
        return (T)this;
    }
}

public class Dog<T extends Dog<T>> extends Animal<T> {
    private String breed;
    public T setBreed(String breed) {
        this.breed = breed;
        return (T) this;
    }
}

public class FluffyDog extends Dog<FluffyDog> {
    private Long fluffiness;
    public <T extends FluffyDog> T setFluffiness(Long fluffiness) {
        this.fluffiness = fluffiness;
        return (T) this;
    }
}

Если вы хотите разрешить переопределение FluffyDog и предложить тот же шаблон, вам придется повторить то же самое с универсальным типом, как в классе Dog.

0 голосов
/ 30 октября 2018

Два других варианта:

  1. Так как порядок методов установки не имеет значения, функционально , вызывайте их в порядке снизу вверх. Приведение не требуется, если вы сначала назначаете переменную.

    final FluffyDog dog = new FluffyDog();
    dog.setFluffiness(10)
       .setBreed("Lab")
       .setName("Fluffy");
    
  2. Переопределить унаследованные методы установки для изменения типа возвращаемого значения, используя super для делегирования фактической реализации:

    class Animal {
        private String name;
        public Animal setName(String name) {
            this.name = name;
            return this;
        }
    }
    
    class Dog extends Animal {
        private String breed;
        @Override
        public Dog setName(String name) {
            super.setName(name);
            return this;
        }
        public Dog setBreed(String breed) {
            this.breed = breed;
            return this;
        }
    }
    
    class FluffyDog extends Dog {
        private long fluffiness;
        @Override
        public FluffyDog setName(String name) {
            super.setName(name);
            return this;
        }
        @Override
        public FluffyDog setBreed(String breed) {
            super.setBreed(breed);
            return this;
        }
        public FluffyDog setFluffiness(long fluffiness) {
            this.fluffiness = fluffiness;
            return this;
        }
    }
    
    class Main {
        public static void main(String[] args) throws Exception {
            final FluffyDog dog = new FluffyDog()
                    .setName("Fluffy")
                    .setBreed("Lab")
                    .setFluffiness(10);
        }
    }
    
...