最近初始化一个val

时间:2013-06-18 05:48:55

标签: scala functional-programming

是否可以仅使用val:

在Scala中执行此操作
class MyClass {
  private val myVal1: MyClass2 //.....????? what should be here?

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

我的想法是我无法立即初始化myVal1,因为其构造函数的参数尚未知,我必须在myMethod1中执行此操作。 <{1}}应该在类中可见,并且应该是不可变的。

不允许任何可变状态。

有可能吗?

3 个答案:

答案 0 :(得分:4)

不,不可能按照你想要的方式去做。考虑一下,

的结果是什么
val mc = new MyClass
mc.method1(0)
mc.method1(1)

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

答案 1 :(得分:3)

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

  1. var更改为Option; setter myMethod1返回同一个类的新实例,并将Option设置为该值。
  2. 使用Builder创建一个单独的可变var类,并在收集完所有数据后将其转换为不可变的类
  3. 如果您正在处理转发或循环引用,请考虑使用按名称调用lazy val s(example 1example 2

  4. 更新: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)
    

答案 2 :(得分:0)

模仿懒惰的价值&#34;在实例初始化完成之后,可能无法检索其初始值(顺便说一下,这些对象没有什么特别之处,例如Swift有lazy properties,甚至建议将其声明为变量),你可以引入一个包装来重复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]即可保留val关键字:

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)