Mengapa `private val` dan` private final val` berbeda?

100

Saya dulu berpikir begitu private valdan private final valsama, sampai saya melihat bagian 4.1 di Referensi Scala:

Definisi nilai konstan adalah dari bentuknya

final val x = e

di mana e adalah ekspresi konstan (§6.24). Pengubah terakhir harus ada dan tidak ada anotasi jenis yang dapat diberikan. Referensi ke nilai konstanta x sendiri diperlakukan sebagai ekspresi konstan; dalam kode yang dihasilkan, mereka digantikan oleh sisi kanan definisi e.

Dan saya telah menulis tes:

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

javap -c keluaran:

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
}

Kode byte seperti yang dikatakan Referensi Scala: private valbukan private final val.

Mengapa tidak scalac hanya memperlakukan private valsebagai private final val? Apakah ada alasan yang mendasarinya?

Yang Bo
sumber
28
Dengan kata lain: karena a valsudah tidak dapat diubah, mengapa kita membutuhkan finalkata kunci sama sekali di Scala? Mengapa kompilator tidak bisa memperlakukan semua valdengan cara yang sama seperti final vals?
Jesper
Perhatikan bahwa privatepengubah cakupan memiliki semantik yang sama seperti package privatedi Java. Anda mungkin bermaksud mengatakan private[this].
Connor Doyle
5
@ ConnorDoyle: Sebagai paket pribadi? Saya tidak berpikir begitu: privatemaksudnya itu hanya terlihat untuk instance kelas ini, private[this]hanya instance ini - kecuali untuk instance dari kelas yang sama , privatetidak mengizinkan siapa pun (termasuk dari paket yang sama) untuk mengakses nilai.
Buat 42

Jawaban:

81

Jadi, ini hanya tebakan, tapi itu adalah gangguan abadi di Java bahwa variabel statis terakhir dengan literal di sisi kanan masuk ke bytecode sebagai konstanta. Hal itu membuat manfaat kinerja pasti, tetapi itu menyebabkan kompatibilitas biner dari definisi rusak jika "konstanta" pernah berubah. Saat menentukan variabel statis final yang nilainya mungkin perlu diubah, programmer Java harus menggunakan peretasan seperti menginisialisasi nilai dengan metode atau konstruktor.

Val di Scala sudah final dalam pengertian Java. Sepertinya desainer Scala menggunakan final pengubah redundan yang berarti "izin untuk menyebariskan nilai konstan". Jadi pemrogram Scala memiliki kendali penuh atas perilaku ini tanpa menggunakan peretasan: jika mereka menginginkan konstanta sebaris, nilai yang seharusnya tidak pernah berubah tetapi cepat, mereka menulis "nilai akhir". jika mereka menginginkan fleksibilitas untuk mengubah nilai tanpa merusak kompatibilitas biner, cukup "val".

Steve Waldman
sumber
9
Ya, itulah alasan untuk nilai mata uang non-pribadi, tetapi nilai mata uang pribadi jelas tidak dapat disejajarkan di kelas lain dan merusak kompatibilitas dengan cara yang sama.
Alexey Romanov
3
Apakah ada masalah kompatibilitas biner saat saya mengubah private valke private final val?
Yang Bo
1
@ steve-waldman Maaf, maksud Anda valdi paragraf kedua?
Yang Bo
1
Berikut adalah detail variabel statis terakhir di Java terkait kompatibilitas biner - docs.oracle.com/javase/specs/jls/se7/html/…
Eran Medan
8

Saya pikir kebingungan di sini muncul dari penggabungan kekekalan dengan semantik final. vals dapat diganti dalam kelas anak dan oleh karena itu tidak dapat diperlakukan sebagai final kecuali ditandai secara eksplisit.

@ Brian REPL menyediakan ruang lingkup kelas pada tingkat baris. Lihat:

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
Connor Doyle
sumber
1
Saya berbicara tentang private val. Bisakah itu diganti?
Yang Bo
Tidak, private vals tidak bisa diganti. Anda dapat mendefinisikan ulang val privat lain dengan nama yang sama di subkelas, tetapi val ini benar-benar berbeda yang kebetulan memiliki nama yang sama. (Semua referensi ke yang lama akan tetap mengacu pada yang lama.)
aij
1
tampaknya tidak hanya menjadi perilaku utama ini, karena saya dapat membuat nilai akhir (atau bahkan nilai akhir) pada penerjemah tanpa berada dalam konteks kelas sama sekali.
nairbv