Чому "приватний вал" і "приватний остаточний вал" відрізняються?


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? Чому компілятор не може ставитись до всіх vals так само, як final vals?
Джеспер

Зауважте, що privateмодифікатор області має ту саму семантику, що і package privateв Java. Ви можете сказати private[this].
Коннор Дойл

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

Відповіді:


81

Отже, це лише здогадка, але це було багаторічним роздратуванням на Яві, що кінцеві статичні змінні з літералом з правого боку потрапляють у байт-код як константи. Це спричиняє певну користь від продуктивності, але це призводить до порушення бінарної сумісності визначення, якщо "константа" колись змінюється. Визначаючи остаточну статичну змінну, значення якої може знадобитися змінити, програмістам Java доводиться вдаватися до таких хак, як ініціалізація значення методом чи конструктором.

Вал у Scala вже остаточний у сенсі Java. Схоже, дизайнери Scala використовують остаточний модифікатор, щоб означати "дозвіл на введення постійного значення". Тож програмісти Scala мають повний контроль над такою поведінкою, не вдаючись до хак: якщо вони хочуть вбудовану константу, значення, яке ніколи не повинно змінюватися, але швидко, вони пишуть "остаточний вал". якщо вони хочуть гнучкості змінити значення, не порушуючи бінарної сумісності, просто "val".


9
Так, це причина для приватних vals, але приватні vals, очевидно, не можуть бути вписані в інших класах і порушувати сумісність таким же чином.
Олексій Романов

3
Чи є проблема із сумісністю бінарних даних, коли я переходжу private valна private final val?
Ян Бо

1
@ steve-waldman Вибачте, ви мали valна увазі у другому абзаці?
Ян Бо

1
Ось подробиці про кінцеві статичні змінні на Java щодо бінарної сумісності - docs.oracle.com/javase/specs/jls/se7/html/…
Eran Medan

8

Я думаю, що плутанина тут виникає у зв'язку зі зміною незмінності із семантикою остаточного. vals може бути відмінено в дочірніх класах, і тому їх не можна вважати остаточними, якщо явно не позначено як таке.

@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, який просто має те ж ім’я. (Всі посилання на старий буде по- , як і раніше ставляться до старого.)
ау

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