11

到目前为止的搜索结果让我相信,如果没有非主构造函数,这是不可能的

class Foo {                      // NOT OK: 2 extra lines--doesn't leverage Scala's conciseness
  private var _x = 0
  def this(x: Int) { this(); _x = x }
  def x = _x
}
val f = new Foo(x = 123)         // OK: named parameter is 'x'

或牺牲主构造函数中的参数名称(使使用命名参数的调用变得丑陋)

class Foo(private var _x: Int) { // OK: concise
   def x = _x
}
val f = new Foo(_x = 123)        // NOT OK: named parameter should be 'x' not '_x'

理想情况下,可以做这样的事情:

class Foo(private var x: Int) {  // OK: concise
    // make just the getter public
    public x
}
val f = new Foo(x = 123)         // OK: named parameter is 'x'

我知道命名参数在 Java 世界中是一个新事物,所以它可能对大多数人来说并不那么重要,但是来自命名参数更流行的语言(Python),这个问题立即弹出。

所以我的问题是:这可能吗?(可能不是),如果不是,为什么语言设计没有发现这样一个(在我看来)重要的用例?我的意思是代码要么必须牺牲干净的命名,要么必须牺牲简洁的定义,这是 Scala 的标志。

PS考虑一下一个公共字段突然需要私有化,同时保持getter公共的情况,在这种情况下,开发人员必须更改1行并添加3行才能达到效果,同时保持界面相同:

class Foo(var x: Int) {}       // no boilerplate

->

class Foo {                    // lots of boilerplate
  private var _x: Int = 0
  def this(x: Int) { this(); _x = x }
  def x = _x
}
4

2 回答 2

11

这是否确实是一个设计缺陷是值得商榷的。人们会认为使语法复杂化以允许这种特定用例是不值得的。

此外,Scala 毕竟是一种主要的函数式语言,因此程序中 var 的出现不应该那么频繁,这再次提出了这个特定用例是否需要以特殊方式处理的问题。

但是,似乎解决您的问题的一个简单apply方法是在伴随对象中使用一个方法:

class Foo private(private var _x: Int) {
  def x = _x
}

object Foo {
  def apply(x: Int): Foo = new Foo(x)
}

用法:

val f = Foo(x = 3)
println(f.x)

稍后编辑

这是一个类似于您最初要求的解决方案,但会稍微改变命名:

class Foo(initialX: Int) {
  private var _x = initialX
  def x = _x
}

用法:

val f = new Foo(initialX = 3)
于 2013-09-10T21:46:47.487 回答
0

您试图表达的概念,它是一个对象,其状态在对象内部是可变的,但从其他对象的角度来看是不可变的......这可能会在演员系统的上下文中表示为 Akka 演员。在参与者系统的上下文之外,它似乎是一个 Java 概念,即作为对象意味着什么,移植到了 Scala。

import akka.actor.Actor
class Foo(var x: Int) extends Actor {
  import Foo._
  def receive = {
    case WhatIsX => sender ! x
  } 
}
object Foo {
  object WhatIsX
}
于 2013-09-11T06:00:09.880 回答