2013-06-29 28 views
3

如何在我的特定情况下使用泛型propery?该代码,然后再解释:Java中的泛型构建器

AbstractConstraint.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); 
     } 
    } 
} 

ConstraintBuilder.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基础知识,我希望它是一个比肮脏的黑客更多的解决方案。 如果有人可以查看此编辑,我会很高兴。

+1

我不认为你会失去参考。从'not()'你要返回'this',它仍然指向当前的'Builder'实例。可能是我错过了一些东西。你试过了吗? –

+0

是的,并且代码在Netbeans中不起作用。例如'new AccountConstraint.Builder()。'可以访问'not()'和'username(String)'。虽然'新的AccountConstraint.Builder()。not()。'只能访问'not()'。一个特别的情况是,在后者中,build()会返回类型Object,而在第一个时它将返回类型AccountConstraint。 – skiwi

+0

他不会丢失引用,但他会丢失类型信息('not()'返回'ConstraintBuilder',而不是'Builder')。 – herman

回答

1

我认为这应该工作:

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

你的问题是:

new AccountConstraint.Builder().not() 

返回ConstrainBuilder<T>,这并不一定要username(final String)访问。所以,你把它投到Builder builder,然后拨打电话username(final String)builder

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

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

编辑2: 你可以重写生成器not():使呼叫super.not()和转换返回到Builder。如:

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

然而,建设者的一个不错的结果通常是,他们可以被写为oneliners。这是我希望在我的代码中保留的属性之一。 – skiwi

+0

@skiwi:看我的编辑。 –

+0

好吧,它确实有效......但这并不是我想要的,我无法想象没有演员就无法完成,或者我错了吗? – skiwi

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)

+0

我已经测试过它,但它不起作用。 – skiwi

+0

你说得对,看起来不那么简单,我会多尝试一下。对于使用铸造的简单解决方案,请参阅我的其他答案 – herman

+0

已更新的答案。在抽象构造器类中有一个(安全的,只要子类正确地传递类型)动态转换。 – herman

0

如果铸造是可以接受的,对史蒂夫的答案替代将覆盖Buildernot()方法,缩小型是这样的:

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

这样调用者不必每次都投。