Почему `private val` и` private final val` отличаются?

Раньше я думал, что private val и private final val такие же, пока я не увидел раздел 4.1 в Scala Ссылка:

Определение постоянной величины имеет вид

final val x = e

где e - постоянное выражение (§6.24). Последний модификатор должен присутствовать, и аннотация типа типа не может быть задана. Ссылки на постоянное значение x сами рассматриваются как постоянные выражения; в сгенерированном коде они заменяются определениями правой стороны e.

И я написал тест:

class PrivateVal {
  private val privateVal = 0
  def testPrivateVal = privateVal
  private final val privateFinalVal = 1
  def testPrivateFinalVal = privateFinalVal
}

javap -c вывод:

Compiled from "PrivateVal.scala"
public class PrivateVal {
  public int testPrivateVal();
    Code:
       0: aload_0       
       1: invokespecial #19                 // Method privateVal:()I
       4: ireturn       

  public int testPrivateFinalVal();
    Code:
       0: iconst_1      
       1: ireturn       

  public PrivateVal();
    Code:
       0: aload_0       
       1: invokespecial #24                 // Method java/lang/Object."<init>":()V
       4: aload_0       
       5: iconst_0      
       6: putfield      #14                 // Field privateVal:I
       9: return
}

Байт-код равен Scala Ссылка: private val не private final val.

Почему скалак просто не относится к private val как private final val? Есть ли какая-то основная причина?

Ответ 1

Итак, это всего лишь предположение, но это было постоянным раздражением в Java, что конечные статические переменные с литералом с правой стороны встраиваются в байт-код в качестве констант. Это дает уверенность в производительности, но при этом бинарная совместимость определения ломается, если "константа" когда-либо изменилась. При определении конечной статической переменной, значение которой может потребоваться изменить, Java-программистам приходится обращаться к хакам, например, инициализировать значение с помощью метода или конструктора.

Значение val в Scala уже является окончательным в смысле Java. Похоже, что дизайнеры Scala используют резервный модификатор final, чтобы означать "разрешение на встраивание постоянного значения". Поэтому программисты Scala имеют полный контроль над этим поведением, не прибегая к хакам: если они хотят встроенную константу, значение, которое никогда не должно меняться, но быстро, они пишут "final val". если они хотят гибкости для изменения значения без нарушения бинарной совместимости, просто "val".

Ответ 2

Я думаю, что путаница здесь возникает из объединения неизменности с семантикой финала. val может быть переопределен в дочерних классах и поэтому не может рассматриваться как окончательный, если явно не указано явно.

@Brian REPL предоставляет класс scope на уровне строки. См:

scala> $iw.getClass.getPackage
res0: Package = package $line3

scala> private val x = 5
<console>:5: error: value x cannot be accessed in object $iw
  lazy val $result = `x`

scala> private val x = 5; println(x);
5