【问题标题】:Is there any way to return polymorphic this in Java?有没有办法在 Java 中返回多态 this ?
【发布时间】:2018-10-31 01:50:08
【问题描述】:

在 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 中返回多态 this?

【问题讨论】:

  • Java 中没有自类型,因此使用递归类型参数 (class Animal&lt;T extends Animal&lt;T&gt;&gt;) 是最好的选择。这种方法的缺点是没有什么可以阻止子类做class Dog extends Animal&lt;Cat&gt;
  • 更多信息(例如“getThis()-trick”)请看这里:angelikalanger.com/GenericsFAQ/FAQSections/… - marstran 提到的基本缺点和缺点仍然存在。
  • 为了 Pete 的爱,不要在方法可以使用的地方使用静态初始化器。 class Main static { 呃,这种风格很糟糕。

标签: java typescript generics this f-bounded-polymorphism


【解决方案1】:

另外两个选项:

  1. 由于 setter 方法的顺序无关紧要,在功能上,以自下而上的顺序调用它们。如果您先分配给变量,则无需强制转换。

    final FluffyDog dog = new FluffyDog();
    dog.setFluffiness(10)
       .setBreed("Lab")
       .setName("Fluffy");
    
  2. 重写继承的 setter 方法以更改返回类型,使用 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);
        }
    }
    

【讨论】:

    【解决方案2】:

    使用您的第一个替代方案,您仍然可以进行两个级别的继承。诚然,它的可读性和可理解性不是很高。不幸的是,泛型是在 1.5 中添加的 Java 的附加组件,并且许多 Java Bean 模式并不适合这些类型的链式方法。 (您会注意到大多数 setter 方法在传统 Java 类中返回 void)。

    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 类)做同样的事情。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-16
      • 1970-01-01
      • 2015-12-06
      相关资源
      最近更新 更多