在这个Scala案例类中,为什么List.mkString不能使用前一个参数的值?

时间:2013-10-02 21:42:24

标签: scala

如果用户不提供name参数,我试图提供合理的默认值。在Eclipse IDE的Scala工作表中执行以下操作会生成编译器错误:

case class Type(id: Int, name: String, description: String = "")
case class Definition(id: Int, types: List[Type], separator: String = ";", name: String = types.mkString(separator))

错误是“找不到:值分隔符”,在列表的mkString方法调用内的标识符“separator”上指示。由于这是一个案例类,并且之前的参数已经定义,为什么我不能在定义name的默认值时使用“separator”。事实证明,我甚至无法使用“类型”来定义“名称”的默认值。

我试图解决这个问题,但我没有看到如何不将名称变成var,这不是我想做的事情。对此有任何指导或理解。

更新: 好吧,我发现解决这个问题的方法是使用伴侣对象转发到构造函数:

  case class Type(id: Int, name: String, description: String = "")
  object Definition {
    def create(id: Int, types: List[Type], separator: String = ";", name: String = "") =
      Definition(id, types, separator, if (name != "") name else types.map(_.name).mkString(separator))
  }
  case class Definition(id: Int, types: List[Type], separator: String = ";", name: String) {
    require(!separator.isEmpty, "separator must not be empty")
    require(!name.isEmpty, "name must not be empty")
  }

与我最初尝试的相比,这确实是一个类似Java的样板。还有其他/更好的方法吗?

3 个答案:

答案 0 :(得分:3)

在Definition的构造函数中,参数不能引用其他参数:

scala> case class Foo(x: Int, y: Int = x + 1)
<console>:7: error: not found: value x
       case class Foo(x: Int, y: Int = x + 1)
                                       ^

你也不能讨论构造函数的参数,虽然它看起来像是有效的:

scala> case class Foo(x: Int)(y: Int = x + 1)
defined class Foo

scala> val test = Foo(3)()
test: Foo = Foo(3)

scala> test.y
<console>:11: error: value y is not a member of Foo
              test.y
                   ^

通过在定义的伴随对象中定义额外的apply方法,我能够达到你想要的效果:

case class Type(id: Int, name: String, description: String = "")
case class Definition(id: Int, types: List[Type], separator: String, name: String)
object Definition {
  def apply(id: Int, types: List[Type], separator: String): Definition = Definition(id, types, separator, types.mkString(separator))  
  def apply(id: Int, types: List[Type]): Definition = Definition(id, types, ";")
}

因为这些是常规方法,而不是构造函数,所以它们可以根据方法体中的需要引用和操作它们的参数。例如:

// Entering paste mode (ctrl-D to finish)

val type1 = Type(1, "foo")
val type2 = Type(2, "bar", "not baz")
val myDef = Definition(1, List(type1, type2))

// Exiting paste mode, now interpreting.

type1: Type = Type(1,foo,)
type2: Type = Type(2,bar,not baz)
myDef: Definition = Definition(1,List(Type(1,foo,), Type(2,bar,not baz)),;,Type(1,foo,);Type(2,bar,not baz))

答案 1 :(得分:2)

只需将name放入新参数列表中即可解决此问题。后续参数列表中的参数可以引用先前列表中的参数值,也可以从中推断出类型。

case class Definition(id: Int, types: List[Type], separator: String = ";")(name_ : String = types.mkString(separator)) {
  def name = name_
}

答案 2 :(得分:0)

你可能想要的是这样的:

case class Type(id: Int, name: String, description: String = "")
case class Definition(id: Int, types: List[Type], separator:String, name: String)
object Definition{
  def apply(id: Int, types: List[Type], separator: String = ";") : Definition = 
    Definition(id,types,separator,types.mkString(separator))
}

这类似于Shadowlands的解决方案。