3

我在类型安全的构建器模式中使用幻像类型来确保方法只被调用一次,如以下代码示例所示

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

  class Builder[MethodCalled <: TBoolean] private() {

    def foo()(implicit ev: MethodCalled =:= TFalse): Builder[TTrue] = {
      new Builder[TTrue]
    }
  }

  object Builder {
    def apply() = new Builder[TFalse]()
  }

Builder().foo().foo()不能按要求工作,但是我想将错误消息设置为用户可读的内容。目前消息是

此行有多个标记 - 方法 foo 的参数不足:(implicit ev: =:=[W.TTrue,W.TFalse])W.Builder[W.TTrue]。未指定值参数 ev。- 无法证明 W.TTrue =:= W.TFalse。- 无法证明 W.TTrue =:= W.TFalse。

4

2 回答 2

5

在这里使用类型参数有点矫枉过正。最好只从方法中返回一个功能较弱的类型foo

object Builder {
  trait CanFoo { def foo() : Builder }
  def apply(): Builder with CanFoo = new Builder with CanFoo {
    def foo() = new Builder {}
  }
}
trait Builder

Builder().foo().foo() // value foo is not a member of Builder

有一个注释implicitNotFound用于自定义错误消息,但它需要使用搜索的类型(=:=)而不是使用站点(foo)来定义,所以这是一个非常无用的构造......

...除非您创建自己的替代品=:=

import annotation.implicitNotFound

object Called {
  implicit def same[A]: Called[A, A] = instance.asInstanceOf[Called[A, A]]
  private object instance extends Called[Any,Any]
}
@implicitNotFound(msg = "Cannot call this method twice") sealed trait Called[A, B]

class Builder[Foo <: TBoolean] private() {
  def foo()(implicit ev: Called[Foo, TFalse]): Builder[TTrue] = {
    new Builder[TTrue]
  }
}
object Builder {
  def apply() = new Builder[TFalse]()
}

Builder().foo().foo()  // -> "error: Cannot call this method twice"
于 2013-01-12T12:09:30.273 回答
2

您无法自定义错误消息,但可以自定义特征名称。我会打电话给他们BuiltUnbuilt类似的东西。然后你可以警告图书馆的用户或任何你会得到一个毛茸茸的错误信息,但他们真的只需要发现一些看起来像Cannot prove that Built =:= Unbuilt.

于 2013-01-12T11:55:04.080 回答