Scala和Java的类型安全构建器库

4
以下是一种类型安全、流畅的Scala构建器模式,如http://www.tikalk.com/java/blog/type-safe-builder-scala-using-type-constraints中所述。它与Scala和Java的构建器库类似,但专门处理编译时构建器检查。如何从Java调用它?是否可以通过"scala.Predef$$eq$colon$eq"参数为ScalaJava提供一个简洁的API?
sealed trait TBoolean
sealed trait TTrue extends TBoolean
sealed trait TFalse extends TBoolean

class Builder[HasProperty <: TBoolean] private(i: Int) {
  protected def this() = this(-1)
  def withProperty(i: Int)(implicit ev: HasProperty =:= TFalse) = new Builder[TTrue](i)
  def build(implicit ev: HasProperty =:= TTrue) = println(i)
}

//javap output
    public class Builder extends java.lang.Object implements scala.ScalaObject{
    public Builder withProperty(int, scala.Predef$$eq$colon$eq); //How is this called from Java?
    public void build(scala.Predef$$eq$colon$eq);
    public Builder();
}
object Builder {
  def apply() = new Builder[TFalse]
}
2个回答

6

相比于Scala版本,您应该能够在Java中使用此API,但需要添加一些额外的代码。一些方便的字段可以使事情变得更加简单:

object Builder {
   def apply() = new Builder[TFalse]
   val unassigned = =:=.tpEquals[TFalse]
   val assigned = =:=.tpEquals[TTrue] 
}

Java客户端代码应该最终看起来像这样:
Builder$.MODULE$.apply()
   .withProperty(10, Builder$.MODULE$.unassigned())
   .build(Builder$.MODULE$.assigned());

build方法必须检查每个属性是否已被赋值,因此当您推广到多个属性时,它会变得非常嘈杂:

Builder$.MODULE$.apply()
   .withProp1(10, Builder$.MODULE$.unassigned())
   .withProp2(20, Builder$.MODULE$.unassigned())
   .withProp3(30, Builder$.MODULE$.unassigned())
   // ...
   .build(Builder$.MODULE$.assigned(), 
          Builder$.MODULE$.assigned(), 
          Builder$.MODULE$.assigned(), 
          //...
         );

通过一些静态委托和帮助类(以及一些静态导入),您应该能够将其简化为以下内容:

createBuilder()
   .withProp1(10, unassigned())
   .withProp2(20, unassigned())
   .build(assigned(), assigned());

是的,在Java中有点嘈杂,但回答了问题。在Java版本中可能会牺牲类型安全性...但也许可以使用@Bridge来提供一个仅限Java的版本并删除*assigned()参数? - eptx
使用传递null的桥接方法,正如ittayd建议的那样(http://www.tikalk.com/incubator/blog/creating-java-api-scala-using-bridge#comments),可以工作,但它肯定会牺牲类型安全性。这是因为`null`是矛盾的实例:`TFalse =:= TTrue`。 - Aaron Novstrup
1
我还应该指出,这个构建器模式的类型安全并不依赖于隐式搜索。相反,它依赖于一个事实,即无法使用两个不相等的类型参数实例化=:=类(即null是唯一的矛盾)。 - Aaron Novstrup

3

好的,感谢Aaron和ittayd...这里有一个流畅API的Scala和Java构建器:

import annotation.bridge

sealed trait TBoolean
sealed trait TTrue extends TBoolean
sealed trait TFalse extends TBoolean

class Builder[HasProperty <: TBoolean] private(i: Int) {
  protected def this() = this(-1)

  def withProperty(i: Int)(implicit ev: HasProperty =:= TFalse) = new Builder[TTrue](i)
  def build(implicit ev: HasProperty =:= TTrue):Int = i
  @bridge def withProperty(i: Int) = new Builder[TTrue](i)
  @bridge def build = build(null)
}

object Builder {
  def apply() = new Builder[TFalse]
  val unassigned = =:=.tpEquals[TFalse]
  val assigned = =:=.tpEquals[TTrue]
}

Scala 的使用(类型安全):

val v = Builder().withProperty(2).build

Java 使用 I(类型安全,但丑陋):

int i = Builder$.MODULE$.apply()
        .withProperty(5, Builder$.MODULE$.unassigned())
         .build(Builder$.MODULE$.assigned()); 

Java使用II(不是类型安全的,但更加清晰,使用@Bridge调用):

static Builder createBuilder() {
  return Builder$.MODULE$.apply();
}
...
int j = createBuilder().withProperty(7).build();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接