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


100

Раньше я думал, что это одно 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 Reference: private valнет private final val.

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


28
Другими словами: поскольку a valуже является неизменяемым, зачем нам вообще нужно finalключевое слово в Scala? Почему компилятор не может обрабатывать все так valже, как final vals?
Jesper

Обратите внимание, что privateмодификатор области имеет ту же семантику, что и package privateв Java. Вы можете сказать private[this].
Коннор Дойл

5
@ConnorDoyle: Как частный пакет? Я так не думаю: privateозначает, что он виден только экземплярам этого класса, private[this]только этот экземпляр - за исключением экземпляров того же класса , privateне позволяет никому (включая из того же пакета) получить доступ к значению.
Make42

Ответы:


81

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

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


9
Да, в этом причина использования не частных значений, но, очевидно, частные значения не могут быть встроены в другие классы и таким же образом нарушают совместимость.
Алексей Романов

3
Есть ли проблема с двоичной совместимостью при переходе private valна private final val?
Ян Бо

1
@ steve-waldman Простите, вы имели ввиду valво втором абзаце?
Ян Бо

1
Вот подробные сведения о конечных статических переменных в Java относительно двоичной совместимости - docs.oracle.com/javase/specs/jls/se7/html/…
Эран Медан

8

Я думаю, что путаница здесь возникает из-за смешения неизменяемости с семантикой final. vals могут быть переопределены в дочерних классах и, следовательно, не могут рассматриваться как final, если явно не отмечены как таковые.

@Brian REPL обеспечивает область видимости класса на уровне строки. Видеть:

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

1
Я говорю о private val. Можно ли его переопределить?
Ян Бо

Нет, частные вальсы не могут быть отменены. Вы можете переопределить другой частный val с тем же именем в подклассе, но это совершенно другой val, у которого просто такое же имя. (Все ссылки на старый по-прежнему будут относиться к старому.)
aij

1
похоже, это не просто такое переопределяющее поведение, поскольку я могу сделать окончательный val (или даже конечный var) в интерпретаторе, вообще не находясь в контексте класса.
nairbv
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.