为什么``对象'里面的`val`不能自动最终?

时间:2012-09-06 22:06:33

标签: scala field final

val不是(?)在单例对象中自动最终的原因是什么? E.g。

object NonFinal {
   val a = 0
   val b = 1

   def test(i: Int) = (i: @annotation.switch) match {
      case `a` => true
      case `b` => false
   }
}

结果:

<console>:12: error: could not emit switch for @switch annotated match
          def test(i: Int) = (i: @annotation.switch) match {
                                                     ^

尽管

object Final {
   final val a = 0
   final val b = 1

   def test(i: Int) = (i: @annotation.switch) match {
      case `a` => true
      case `b` => false
   }
}

在没有警告的情况下编译,因此可能会生成更快的模式匹配表。

必须添加final对我来说似乎是一种恼人的噪音。本身不是object决赛,也不是其成员吗?

3 个答案:

答案 0 :(得分:24)

这在the specification中已明确解决,并且会自动最终确定:

  

最终类或对象的成员也是最终的,所以   final修饰符对他们来说通常也是多余的。但请注意   常量值定义(第4.1节)确实需要明确的final修饰符,即使   它们在最终的类或对象中定义。

您的final - 示例编译没有错误(或警告)2.10-M7,所以我假设在早期版本中@switch检查存在问题,并且成员是事实上是最终的。


更新:实际上这比我预期的更好奇 - 如果我们使用2.9.2或2.10-M7编译以下内容:

object NonFinal {
  val a = 0
}

object Final {
  final val a = 0
}

javap确实有所不同:

public final class NonFinal$ implements scala.ScalaObject {
  public static final NonFinal$ MODULE$;
  public static {};
  public int a();
}

public final class Final$ implements scala.ScalaObject {
  public static final Final$ MODULE$;
  public static {};
  public final int a();
}

即使值定义的右侧不是常量表达式,您也会看到同样的事情。

所以我会留下我的答案,但这不是决定性的。

答案 1 :(得分:21)

你不是在问“为什么他们不是最终的”,你问的是“为什么他们没有内联。”只是碰巧最后是你如何提示你希望它们内联的编译器。

它们未自动内联的原因是单独编译。

object A { final val x = 55 }
object B { def f = A.x }

编译时,B.f返回55,字面意思是:

public int f();
  0: bipush        55
  2: ireturn       

这意味着如果你重新编译A,B将无视这一变化。如果x在A中未标记为final,则B.f看起来像这样:

  0: getstatic     #19                 // Field A$.MODULE$:LA$;
  3: invokevirtual #22                 // Method A$.x:()I
  6: ireturn       

另外,为了纠正其中一个答案,final并不意味着scala中的不可变。

答案 2 :(得分:3)

为了解决关于对象最终的核心问题,我认为规范中的这一条款更具相关性:

  

常量值定义的格式为final val x = e   其中e是常数表达式(§6.24)。必须存在最终修饰符,并且不能给出类型注释。对常量值x的引用本身被视为常量表达式;在生成的代码中,它们被定义的右侧e。

取代

意义重大:

  • 不能给出类型注释
  • 表达式e用于生成的代码中(通过我的阅读,作为原始未评估的常量表达式)

在我看来,规范要求编译器使用这些更像宏替换而不是在编译时就地计算的值,这可能会影响生成的代码的运行方式。

我认为特别有趣的是不能给出类型注释。

我认为这是我们的最终答案,尽管我无法想出一个显示这些要求的运行时差异的示例。事实上,在我的2.9.2翻译中,我甚至没有执行第一条规则。