【问题标题】:Can I customise the value components in a case class?我可以自定义案例类中的值组件吗?
【发布时间】:2016-04-06 04:58:46
【问题描述】:

假设我在库中有一些案例类:

case class MyClass(a: Int, b: Int)

后来发现我的库中存在错误,我需要对这些参数之一应用一些额外的逻辑以保持工作正常,以便从用户的角度实例可以发生这种情况:

val x = MyClass(1, 2)
println(x.a)  // prints '3' or whatever I happen to compute for 'a'

换句话说,x.a 的最终值不一定是传递给构造函数的值。我知道这看起来很疯狂,但相信我,我需要它。 x.a 在大多数情况下仍会返回传递给构造函数的任何内容,但构造函数参数有一个值会导致错误,我需要对其进行转换。

我看到了两种方法来实现这一点。我可以把a 变成var

case class MyClass(var a: Int, b: Int) {
  a = someComputation()
}

但是类变得可变,因为a 可以从外部设置。如果我可以删除或“隐藏”生成的设置器,我的问题将得到解决,但这似乎是不可能的。如果我添加

private def a_=(newA: Int) {}

它不会覆盖var 生成的setter,因此它会看到两个方法定义并且不会编译。

第二个选项是创建一个独立于构造函数参数的字段/方法:

case class MyClass(private val _a: Int, b: Int) {
  val a = someComputation(a)
}

_a 用于所有特殊生成的方法,例如equalstoString 等,而自定义字段a 没有功能。

有什么方法可以在不影响 API 其余部分的情况下转换构造函数参数?

【问题讨论】:

    标签: scala constructor case-class


    【解决方案1】:

    我要做的是覆盖伴随对象上的apply 方法,以使用正确的计算创建MyClass 的实例。

    object MyClass {
       def apply(a: Int, b: Int) = new MyClass(someComputation(a),b))
    }
    

    然后您可以将其称为val x = MyClass(1, 2),但如果您仍希望进行计算,则无法将其称为val x = new MyClass(1, 2)

    显然以上所有方法都不适用于 REPL。

    相反,我会在伴生对象上选择另一种方法,它不是一个很好的解决方案,但它应该可以工作:

    object MyClass {
       def create(a: Int, b: Int) = new MyClass(someComputation(a),b))
    }
    

    【讨论】:

    • 可以将MyClass的构造函数设为private,禁止用new创建:case class MyClass private (a: Int, b: Int)
    • 也是个好主意!
    • 这是一个好主意,我很兴奋,但是当我尝试它时它没有编译。自定义的apply 与生成的冲突。
    • 真的吗?我刚才在 REPL 中试了一下,效果很好。这很奇怪,也许试试override def
    • @LukaJacobowitz 这是 REPL 的一个奇怪之处。编译常规 .scala 文件时出现错误。如果您将定义包装在其他对象中,以便它们同时执行,您可以在 REPL 中看到错误。由于不涉及继承,因此覆盖也不起作用。
    【解决方案2】:

    那么,您希望 MyClass(a=1).a == 2 之类的东西返回 true 吗? 你真的需要解释为什么这是一个坏主意吗? ;)

    感谢上帝,这是不可能的!

    【讨论】:

    • 是的。我有一个基本上断言的测试。如果a 保持为 1,事情将会中断。我知道这很奇怪,但过去做错了什么,现在需要在不破坏向后兼容性的情况下进行修复。这是一个混乱的局面。
    • 好吧,我的问题是,如果你愿意忍受 this,为什么你会对可变类成员有意见?是比赛吗?
    • 使用 var 并不是一场灾难,我现在已经将其作为一种折衷方案,但尝试解决所有问题并没有什么坏处。我也在探索 Scala 的局限性以及如何做事。
    • @AlexHall 这不是“探索 scala 的限制”,而是试图用 scala 语法编写 perl 代码。是的,在 scala 中使用(公共)var “灾难”,因为它违背了语言的概念和精神。
    • 那么 Scala 不应该让一个只能从内部修改的案例类中的 var 成为不可能。
    猜你喜欢
    • 2013-03-14
    • 1970-01-01
    • 1970-01-01
    • 2022-04-28
    • 1970-01-01
    • 2023-03-30
    • 2012-11-26
    • 1970-01-01
    • 2011-02-20
    相关资源
    最近更新 更多