使方法返回类型通用

时间:2018-03-29 08:23:12

标签: java generics

我有以下带有builder()方法的Manager类:

public class Manager extends Employee {

    public static Manager.Builder builder() {
        return new ManagerBuilder();
    }

    public abstract static class Builder<T extends Employee, B extends Builder<T,B>> extends Employee.Builder<T,B>{
    }

    public static class ManagerBuilder extends Builder<Manager,ManagerBuilder> {

    @Override
    protected ManagerBuilder self() {
        return this;
    }

        @Override
    public Manager build() {
        return new Manager(this);
    }
    }
}

不幸地尝试使用Manager.builder().age(25).build(); 构建一个对象,而不是我需要的人,而不是经理

如何更改Manager.builder()返回类型以返回Manager,同时不与Employee.builder()方法签名冲突。

代码Employee.builder().age(25).build();返回Employee,没问题。

员工类看起来像这样:

public class Employee extends Person {

    public static Employee.Builder<Employee, EmployeeBuilder> builder() {
        return new EmployeeBuilder();
    }

    public abstract static class Builder<T extends Person, B extends Builder<T,B>> extends Person.Builder<T,B>{

    }

    public static class EmployeeBuilder extends Builder<Employee, EmployeeBuilder> {

    @Override
    protected EmployeeBuilder self() {
        return this;
    }
    @Override
    public Employee build() {
        return new Employee(this);
    }
    }

}

public class Person implements PersonInterface {

    private Optional<Integer> age;

    protected Person(Builder<?,?> builder) {
        this.age = builder.age;
        }

    public abstract static class Builder<T extends Person, B extends Builder<T,B>> {
        private Optional<Integer> age;

        protected Builder() {
        }

        public B age(Integer age) {
            if (Objects.isNull(age) || age == 0)  throw new IllegalArgumentException("Age ist empty");
            this.age = Optional.of(age);
            return self();
        }

        protected abstract B self();
        public abstract T build();
    }

    public static class PersonBuilder extends Builder<Person, PersonBuilder>{

        @Override
        protected PersonBuilder self() {
            return this;
        }

        @Override
        public Person build() {
            return new Person(this);
        }
    }

}

2 个答案:

答案 0 :(得分:2)

核心问题是你的奇怪的继承结构,它重载了类名Builder。我无法解决问题,但在某些时候,ManagerBuilder的特定类型信息会丢失。这可以简化很多:

public class Employee extends Person {

    public static EmployeeBuilder builder() {
        return new EmployeeBuilder();
    }

    public static class EmployeeBuilder extends Person.Builder<Employee, EmployeeBuilder> {

        @Override
        protected EmployeeBuilder self() {
            return this;
        }

        @Override
        public Employee build() {
            return new Employee(this);
        }
    }
}

public class Manager extends Employee {

    public static ManagerBuilder builder() {
        return new ManagerBuilder();
    }

    public static class ManagerBuilder extends Person.Builder<Manager, ManagerBuilder> {

        @Override
        protected ManagerBuilder self() {
            return this;
        }

        @Override
        public Manager build() {
            return new Manager(this);
        }
    }
}

这可以解决你的一些问题。

现在你还有一个问题。静态方法builder重载了不兼容的返回类型。您可以在Why does Java enforce return type compatibility for overridden static methods?找到相关信息 如果您以不同的方式命名这些方法,它应该可以工作。

答案 1 :(得分:1)

您的代码运行良好,Manager.builder().age(25).build()实际返回Manager。这只是编译时的问题。

以下Junit测试应该成功(它在我的测试中确实):

@Test
public void testEss3() throws Exception {
    Person emp = Manager.builder().age(25).build();
    assertTrue(emp instanceof Manager);
}

实际上,当您声明没有变量来托管构建器时,并且由于方法age未在Manager.Builder中定义,也未在其直接子类Employee.Builder中定义,因此编译器假定它将返回声明它的类的对象,即Person.Builder。它不是假的,因为它实际上是一个祖先类。但是从那时起,编译器不知道build()返回的确切类,只知道它将是Person

但是下面的代码是编译器的接受者:

    Manager.Builder<Manager,?> builder = Manager.builder();
    Manager emp = builder.age(25).build();