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
决赛,也不是其成员吗?
答案 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。
取代
意义重大:
在我看来,规范要求编译器使用这些更像宏替换而不是在编译时就地计算的值,这可能会影响生成的代码的运行方式。
我认为特别有趣的是不能给出类型注释。
我认为这是我们的最终答案,尽管我无法想出一个显示这些要求的运行时差异的示例。事实上,在我的2.9.2翻译中,我甚至没有执行第一条规则。