为什么“object”内的“val”不会自动成为final?

问题描述 投票:0回答:3

在单例对象中

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
本身及其成员不是最终的吗?

scala field final
3个回答
27
投票

这在规范中明确解决,并且它们自动是最终的:

最终类或对象的成员也隐式是最终的,所以

final
修饰符对他们来说通常也是多余的。但请注意, 常量值定义(第 4.1 节)确实需要显式
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();
}

即使值定义的右侧不是常量表达式,您也会看到相同的结果。

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


23
投票

你不是在问“为什么它们不是最终的”,而是在问“为什么它们不是内联的”。碰巧,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 中的不可变。


4
投票

为了解决有关对象的final的核心问题,我认为spec中的这个条款更相关:

A 常量值定义的形式为

final val x = e

其中

e
是一个常量表达式
final
修饰符必须存在,并且不能给出类型注释。对常量值
x
的引用本身被视为常量表达式;在生成的代码中,它们被定义的右侧替换
e

重要意义:

  • 不能给出类型注释
  • 表达式 e 在生成的代码中使用(根据我的阅读,作为原始未计算的常量表达式)

在我看来,规范要求编译器使用这些更像是宏替换,而不是在编译时评估的值,这可能会影响结果代码的运行方式。

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

我认为这指向了我们的最终答案,尽管我无法提供一个示例来显示这些要求的运行时差异。事实上,在我的 2.9.2 解释器中,我什至没有执行第一条规则。

© www.soinside.com 2019 - 2024. All rights reserved.