交换构造函数参数

时间:2013-07-17 11:14:38

标签: scala

是否可以交换构造函数的参数? 请考虑以下示例:

case class Foo(a:Int, b:Int) {
  if (a > b) {
    val tmp = a
    a = b
    b = tmp
  }
}

编译器抛出错误,因为我在第4行重新分配给val a,这非常好。但是,我需要不可变对象。因此,将ab声明为变量不是一种选择。

有没有已知的模式如何解决这个问题?

4 个答案:

答案 0 :(得分:4)

制作内部交换方法:

case class Foo(a: Int, b: Int) {
  def ifSwap = if (a > b) Foo(b, a) else this
}

val f1 = Foo(1,2).ifSwap // the result is Foo(1,2)
val f2 = Foo(2,1).ifSwap // the result is Foo(1,2)

如果你想保留不变性然后改变状态,你需要在每次修改时返回新实例,或者使用一些核心方式,如LensesStateRecords等......正如奥德斯基教授在SD'13谈话中所说,有些情况下你不应该害怕vars

答案 1 :(得分:3)

我想你想要实现的是Foo的每个实例都订购了它的一对值,是吗?

一种可能性不是创建类case而是自己定义它的构造和提取。该类不会继承产品,也不会继承默认的toString等,但除此之外它还可以作为案例类使用:

class Foo private (val a: Int, val b: Int);
object Foo {
  def apply(a: Int, b: Int): Foo =
    if (a < b)
      new Foo(a, b)
    else
      new Foo(b, a)

    def unapply(f: Foo): Option[(Int,Int)] = Some((f.a, f.b))
}

// test:
def printFoo(f: Foo) = f match {
  case Foo(x, y) => println(x + ", " + y);
}
printFoo(Foo(1,2))
printFoo(Foo(3,2))

另见:

答案 2 :(得分:2)

看来你可以将case类构造函数设为私有!扩展@PetrPudlák答案,我将构造函数设为私有,并定义一个foo帮助器来创建案例类对象:

case class Foo private (a: Int, b: Int)
object Foo {
  def foo(x: Int, y: Int) = if (x > y) Foo(y, x) else Foo(x, y)
}

然后我只使用foo来实例化格式良好的Foo对象,其余的case类功能按预期工作(相等,hashcode,unapply):

import Foo._

foo(1, 2)                                 //> res0: worksheets.so.Foo = Foo(1,2)
foo(2, 1)                                 //> res1: worksheets.so.Foo = Foo(1,2)
foo(3, 4) == foo(4, 3)                    //> res2: Boolean = true
// Foo(4, 2) does not compile

// extractor/pattern matching works:
val Foo(a, b) = foo(10,1)                 //> a  : Int = 1
                                          //| b  : Int = 10

您也可以将foo命名为OrderedFooNormalFoo更有意义的内容。

答案 3 :(得分:1)

scala> :paste
// Entering paste mode (ctrl-D to finish)

object Foo {
  def swapped(b: Int, a: Int) = Foo(a=a, b=b)
}
case class Foo(a: Int, b: Int)

// Exiting paste mode, now interpreting.

defined module Foo
defined class Foo

scala> Foo.swapped(1, 2) == Foo(2, 1)
res0: Boolean = true