构造函数的 Scala 元组解包

Scala tuple unpacking for constructors

我有一个 Scala class 有两个参数,另一个是一个参数构造函数。 对于单参数构造函数,我调用了一个方法来获取两个元素的元组,并尝试将该元组用作需要两个参数的构造函数的参数。这是一个例子:

def vals(v:Int) = {
    // computation
    (v,v) // returns two element tuple
}

class A(a:Int, b:Int) {
    def this(v:Int) = {
        this(vals(v))
    }
}

object Main extends App {
    val a = new A(10)
}

但是,我收到类型不匹配错误。 我在 scala tuple unpacking 中找到了一个适用于函数调用但不适用于构造函数的解决方案。

def foo(x: Int, y: Int) = x * y
def getParams = {
    (1,2)  //where a & b are Int
}

object Main extends App {
    println((foo _).tupled(getParams))
    println(Function.tupled(foo _)(getParams))
}

如何解决这个问题?

为 class 创建一个伴随对象并向其添加几个 "factory" ("apply") 方法,然后放弃额外的构造函数以支持工厂方法。如果需要,您还可以在此处包含 vals 方法以将所有内容保持在一起(尽管您也可以在其他地方定义它,如果这对您来说效果更好的话)。然后你会得到类似下面的结果:

class A(val a:Int, val b:Int)

object A {

  def apply(pair: (Int, Int)): A = new A(pair._1, pair._2)

  def apply(v: Int): A = A(vals(v))

  def vals(v:Int) = {
    // computation
    (v,v) // returns two element tuple
  }
}

并创建您的 A

scala> val a = A(10)
a: A = A@36d6ec03

scala> a.a
res6: Int = 10

请注意,我将 'a' 和 'b' 字段声明为 'val'。这使它们可以访问,如上面的行 a.a 所示。事实上,我建议将 A 设为 "case class",例如。 case class(a: Int, b: Int) 会自动将 'val' 添加到字段中,并且还会为您创建一个伴随 class(另一种默认的 "apply" 方法需要两个 Ints) .您还可以免费获得 toStringequalshashcode 的实现。

虽然我同意并推荐 by Shadowlands, you might encounter a compiler bug in versions prior to Scala 2.12.2 (see SI-3772) 阻止为方法拥有的 case classes 定义伴随对象(这取决于声明顺序,导致错误 MyClass is already defined as object MyClassMyClass is already defined as (compiler-generated) case class companion object MyClass).

一个非正统的解决方法是向您的案例添加两个构造函数class:一个接受传递给外部函数的初始值,另一个接受来自您的计算的元组,该元组被解包为案例的参数class' 参数.

def values(i: Int): (Int, String) = i -> i.toString

case class Foo(i: Int, s: String) {
  private def this(values: (Int, String)) = this(values._1, values._2)
  def this(i: Int) = this(values(i))
}

assert(Foo(10, "10") == new Foo(10))

用引用上述错误的评论显眼地装饰它。