Scala:在不使用val的情况下分配变量一次

Scala:在不使用val的情况下分配变量一次,scala,Scala,这就是我想做的- class A(some args) { var v: SomeType = null def method1(args) = { v = something1 ... method3 } def method2(args) = { v = something2 ... method3 } def method3 = { // uses v } } 在这种特定情况下,method1和Meth

这就是我想做的-

class A(some args) {
  var v: SomeType = null
  def method1(args) = {
    v = something1
    ...
    method3
  }

  def method2(args) = {
    v = something2
    ...
    method3
  }
  def method3 = {
    // uses v
  }
}
在这种特定情况下,method1和Method2是互斥的,在A实例的生命周期中,它们中的任何一个都只被调用一次。此外,v被赋值一次。我更喜欢将其设置为val,但因为我需要method2或method3的上下文来初始化v,所以我不能在构造函数中这样做


如何实现这种“val”行为?我可以考虑修改方法1和方法2来应用方法,但我不喜欢这个想法。此外,method1和Method2具有相同的参数签名(因此apply需要更多的信息来区分这两种类型的调用)。

一个重要的问题是:您究竟将“val行为”称为什么?对我来说,“val行为”是在声明时立即赋值一次的行为,可以静态地强制执行。您似乎想要强制执行
v
未分配两次。您可能还希望强制在分配之前从不读取它。您可以为此创建一个非常小的辅助框:

final class OnceBox[A] {
  private[this] var value: Option[A] = None

  def update(v: A): Unit = {
    if (value.isDefined)
      throw new IllegalStateException("Value assigned twice")
    value = Some(v)
  }

  def apply(): A = {
    value.getOrElse {
      throw new IllegalStateException("Value not yet assigned")
    }
  }
}
现在是您的代码片段:

class A(some args) {
  val v = new OnceBox[SomeType]
  def method1(args) = {
    v() = something1
    ...
    method3
  }

  def method2(args) = {
    v() = something2
    ...
    method3
  }
  def method3 = {
    // uses v
    v()
  }
}

哦,开玩笑吧,但是内置了单任务VAL:-p

类似的东西可能:

   class A private (mtdType: Int, ...) {
      val v = mdtType match {
        case 1 => method1(...)
        case 2 => method2(...)
      }
   }


   object A {
     def withMethod1(...) = new A(1, ...)
     def withMethod2(...) = new A(2, ...)
   }
或者,另一种可能性:

   sealed trait A {
     val v
     def method3 = println(v)
   }

   class Method1(...) extends A {
      val v = method1(...)
   }

   class Method2(...) extends A {
      val v = method2(...)
   }

与另一个答案类似,但不是子类型,而是字段

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

class A {
  private[this] var context: Int = _
  lazy val v: String =
    context match {
      case 1 => "one"
      case 2 => "two"
      case _ => ???
    }
  def m1() = { context = 1 ; v }
  def m2() = { context = 2 ; v }
}

// Exiting paste mode, now interpreting.

defined class A

scala> val a = new A
a: A = A@62ce72ff

scala> a.m2
res0: String = two

scala> a.m1
res1: String = two

为什么不能将
v
值作为参数传递给
m3
m3
是否从
类的外部调用?如果是,可以在
m1
m2
之前调用
m3
?(听起来像是一个设计缺陷。)我觉得你只需要强制执行
method1
method2
只能调用一次,而
method3
只能在以后调用。@jwvh m3是一个私有方法,我应该在我的代码片段中提到这一点。@Jasper-M我目前正通过保留一个var并检查null和抛出异常(如果不是)来确保这种排序,这是sjrd的解决方案。我一直在寻找一个更简单/更干净的解决方案。
v
抛出直到设置好上下文,然后正常工作。我之前考虑过这个想法。但我的特定用例的问题是,它是一个复杂的初始化(许多变量以m1和m2计算)。因此,上下文必须是一组值(可能是一个包含所需字段的类)。当然,我可以扩展这种方法,并说-v和context是同一类型的,并且在将结果分配给同一对象时也是如此。但这太复杂了,无法在未来的开发人员身上实施,他们使用v而不是上下文。