在单例对象中
val
不(?)自动成为final的原因是什么?例如
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
本身及其成员不是最终的吗?
这在规范中明确解决,并且它们自动是最终的:
最终类或对象的成员也隐式是最终的,所以
修饰符对他们来说通常也是多余的。但请注意, 常量值定义(第 4.1 节)确实需要显式final
修饰符,即使 它们在最终类或对象中定义。final
您的
final
less 示例在 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();
}
即使值定义的右侧不是常量表达式,您也会看到相同的结果。
所以我会留下我的答案,但这不是决定性的。
你不是在问“为什么它们不是最终的”,而是在问“为什么它们不是内联的”。碰巧,final 是您提示编译器您希望将它们内联的方式。
它们没有自动内联的原因是单独编译。
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 中的不可变。
为了解决有关对象的final的核心问题,我认为spec中的这个条款更相关:
A 常量值定义的形式为
final val x = e
其中
是一个常量表达式。e
修饰符必须存在,并且不能给出类型注释。对常量值final
的引用本身被视为常量表达式;在生成的代码中,它们被定义的右侧替换x
。e
重要意义:
在我看来,规范要求编译器使用这些更像是宏替换,而不是在编译时评估的值,这可能会影响结果代码的运行方式。
我认为特别有趣的是可以不给出类型注释。
我认为这指向了我们的最终答案,尽管我无法提供一个示例来显示这些要求的运行时差异。事实上,在我的 2.9.2 解释器中,我什至没有执行第一条规则。