1

是否可以在 Scala 中仅使用 val 来做到这一点:

class MyClass {
  private val myVal1: MyClass2 //.....????? what should be here?

  def myMethod1(param1: Int) = {
    myVal1 = new MyClass2(param1)
    //....
    // some code....
  }
}

这个想法是我不能myVal1立即初始化,因为它的构造函数的参数是未知的,我必须在myMethod1. myVal1应该在类中可见并且应该是不可变的。

不允许可变状态。

那么有可能吗?

4

3 回答 3

4

不,不可能按照您想要的方式进行。考虑一下,结果会是什么

val mc = new MyClass
mc.method1(0)
mc.method1(1)

? myVal1设置两次抛出异常?还是应该保留第一个值?

于 2013-06-18T06:16:34.577 回答
3

这是不可能的,但是有一些方法(除了使用param1作为构造函数参数)

  1. 将 更改varOption; settermyMethod1返回同一个类的新实例,并将其Option设置为该值。
  2. 用 a创建一个单独的可变Buildervar,然后在收集完所有数据后将其转换为不可变类
  3. 如果您正在处理前向或循环引用,请考虑使用按名称调用lazy vals(示例 1示例 2

更新:示例 1:

class MyClass(val myVal1: Option[Int]) {
  def myMethod1(param1: Int): MyClass = {
    new MyClass(Some(param1))
  }
}

object MyClass {
  def apply() = new MyClass(None)
  def apply(i: Int) = new MyClass(Some(i))
}

例如, immutable.Queue使用了这种模式。


更新:示例 3(循环引用):

// ref ... call by name
class MyClass(val id: Int, ref: => MyClass) {
  lazy val myVal1 = ref

  override def toString: String = s"$id -> ${myVal1.id}"
}

像这样使用:

val a: MyClass = new MyClass(1, b)
val b: MyClass = new MyClass(2, a)
println(a)
println(b)

更新:示例 3(前向参考):

class MyClass2(val id: Int)

// ref ... call by name
class MyClass(val id: Int, ref: => MyClass2) {
  lazy val myVal1 = ref

  override def toString: String = s"$id -> ${myVal1.id}"
}

val a = new MyClass(1, x)
println(a.id) // You can use a.id, but not yet the lazy val
val x = new MyClass2(10)
println(a)
于 2013-06-18T06:40:21.517 回答
0

要模仿一个惰性“值”,其初始值可能要等到实例初始化完成后才会被检索到(顺便说一句,此类对象没有什么特别之处,例如 Swift 具有甚至建议将其声明为变量的惰性属性),您可以引入一个包装器重复 Scala 编译器在内部为 Scala 中的惰性值生成的相同逻辑:

class LazyVar[T] {

  private[this] var value$compute: () => T = () => null.asInstanceOf[T]
  @volatile private[this] var value$: T = null.asInstanceOf[T]
  @volatile private[this] var isInitialized$ = false
  @volatile private[this] var isComputed$ = false

  def value_=(value: T) = this.synchronized {
    if(!isInitialized$) {
      value$compute = () => value
      isInitialized$ = true
    }
    else throw new IllegalStateException("Already initialized")
  }

  def value: T = this.synchronized {
    if(!isInitialized$) throw new IllegalStateException("Not yet initialized")
    else if(isComputed$) value$
    else {
      value$ = value$compute()
      isComputed$ = true
      value$
    }
  }
}

现在您只需要更改MyClass2为根据需要LazyVar[MyClass2]保留 thaval关键字:

case class MyClass2(param: Int)

class MyClass {
  private val myVal1: LazyVar[MyClass2] = new LazyVar[MyClass2]

  def this(param: Int) {
    this()
    println("Storing the result of an expensive function...")
    myVal1.value = new MyClass2(param)
  }

  def debug() = println(myVal1.value)
}

现在,如果你写类似

val myClass = new MyClass(42)
myClass.debug
myClass.debug

您会看到该值仅计算一次:

Storing the result of an expensive function...
MyClass2(42)
MyClass2(42)
于 2015-09-28T10:21:30.713 回答