3

在我的特定情况下如何使用泛型属性?先上代码,再解释:

抽象约束.java

public abstract class AbstractConstraint {
    public abstract Constraint[] getConstraints();
}

AccountConstraint.java

public class AccountConstraint extends AbstractConstraint {
    private Constraint<Range<Integer>> accountIdConstraint;
    private Constraint<String> usernameConstraint;
    private Constraint<String> passwordConstraint;
    private Constraint<String> emailConstraint;

    private AccountConstraint(Builder builder) {
        this.accountIdConstraint = builder.accountIdConstraint;
        this.usernameConstraint = builder.usernameConstraint;
        this.passwordConstraint = builder.passwordConstraint;
        this.emailConstraint = builder.emailConstraint;
    }

    @Override
    public Constraint[] getConstraints() {
        return new Constraint[] {
            this.accountIdConstraint,
            this.usernameConstraint,
            this.passwordConstraint,
            this.emailConstraint
        };
    }

    public static class Builder extends ConstraintBuilder<AccountConstraint> {
        private Constraint<Range<Integer>> accountIdConstraint;
        private Constraint<String> usernameConstraint;
        private Constraint<String> passwordConstraint;
        private Constraint<String> emailConstraint;

        public Builder() {
            this.accountIdConstraint = null;
            this.usernameConstraint = null;
            this.passwordConstraint = null;
            this.emailConstraint = null;
            init();
        }

        public Builder accountId(final int val) {
            this.accountIdConstraint = new Constraint<>(operation, truthed, new Range<>(val), "accountId");
            return this;
        }

        public Builder accountId(final int min, final int max) {
            this.accountIdConstraint = new Constraint<>(operation, truthed, new Range<>(min, max), "accountId");
            return this;
        }

        public Builder accountId(final Range<Integer> accountId) {
            this.accountIdConstraint = new Constraint<>(operation, truthed, accountId, "accountId");
            return this;
        }

        public Builder username(final String username) {
            this.usernameConstraint = new Constraint<>(operation, truthed, username, "username");
            return this;
        }

        public Builder email(final String email) {
            this.emailConstraint = new Constraint<>(operation, truthed, email, "email");
            return this;
        }

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

约束生成器.java

public abstract class ConstraintBuilder<T> {
    protected boolean truthed;
    protected Operation operation;

    protected void init() {
        truthed = true;
        operation = Operation.IS;
    }

    public ConstraintBuilder not() {
        truthed = false;
        return this;
    }

    public ConstraintBuilder like() {
        operation = Operation.LIKE;
        return this;
    }

    public abstract T build();
}

我希望能够打电话new AccountConstraint.Builder().not().username("test");,但这是不可能的,因为我失去了“对构建器的引用” new AccountConstraint.Builder().not().,即。我不能再选择username("test")了。

我可以通过什么方式解决这个问题?我确实希望AccountBuilder.Builder扩展ConstraintBuilder<AccountConstraint.Builder>,这样我就不必复制常用的共享方法。

问候。


编辑:我设法让它工作:

有关更改,请参阅下面的答案。

我希望我没有用这个解决方案破坏任何 Java 基础,我希望它更像是一个解决方案,而不是一个肮脏的 hack。如果有人可以查看此编辑,我会很高兴。

4

3 回答 3

1

我认为这应该有效:

Builder builder = (Builder) new AccountConstraint.Builder().not();
builder = builder.username("test");

你的问题是:

new AccountConstraint.Builder().not()

返回 a ConstrainBuilder<T>,它不一定有权访问username(final String). 因此,您将其转换为,Builder builder然后调用username(final String)builder

编辑:你可以把它变成一行:

   ((Builder) (new AccountConstraint.Builder().not())).username("test");

编辑 2:您可以not()在 Builder 中覆盖:使其调用super.not()并将返回值转换为Builder. 如:

public Builder not() 
{
    return (Builder) super.not();
}
于 2013-06-29T19:55:31.740 回答
0

您可能需要递归泛型。

像这样的东西应该工作:

public abstract class ConstraintBuilder<T, B extends ConstraintBuilder<T,B>> {
    private final Class<B> concreteBuilderType;
    public ConstraintBuilder(Class<B> concreteBuilderType) {
        if (!concreteBuilderType.isInstance(this)) {
            throw new IllegalArgumentException("Wrong type");
        }
        this.concreteBuilderType = concreteBuilderType;
    }

    ...

    public B not() {
        truthed = false;
        return concreteBuilderType.cast(this);
    }

}

具体的Builder()构造函数必须调用super(Builder.class).

于 2013-06-29T19:58:36.733 回答
0

如果强制转换是可以接受的,史蒂夫回答的替代方法是覆盖像not()in这样的方法Builder并像这样缩小类型:

public Builder not() {
    return (Builder) super.not();
}

这样调用者就不必每次都进行强制转换。

于 2013-06-29T20:11:15.663 回答