Sejauh ini saya berpikir bahwa secara efektif final dan final kurang lebih setara dan bahwa JLS akan memperlakukan mereka serupa jika tidak identik dalam perilaku sebenarnya. Kemudian saya menemukan skenario yang dibuat-buat ini:
final int a = 97;
System.out.println(true ? a : 'c'); // outputs a
// versus
int a = 97;
System.out.println(true ? a : 'c'); // outputs 97
Rupanya, JLS membuat perbedaan penting antara keduanya di sini dan saya tidak yakin mengapa.
Saya membaca utas lain seperti
- Perbedaan antara final dan efektif final
- Variabel final vs variabel final yang efektif
- Apa yang dimaksud dengan variabel "efektif akhir"?
tetapi mereka tidak menjelaskan secara detail. Lagi pula, pada tingkat yang lebih luas, mereka tampaknya hampir setara. Namun jika digali lebih dalam, ternyata mereka berbeda.
Apa yang menyebabkan perilaku ini, adakah yang dapat memberikan definisi JLS yang menjelaskan hal ini?
Sunting: Saya menemukan skenario terkait lainnya:
final String a = "a";
System.out.println(a + "b" == "ab"); // outputs true
// versus
String a = "a";
System.out.println(a + "b" == "ab"); // outputs false
Jadi interning string juga berperilaku berbeda di sini (saya tidak ingin menggunakan potongan ini dalam kode sebenarnya, hanya ingin tahu tentang perilaku yang berbeda).
sumber
byte
,short
, atauchar
, dan operan lainnya adalah ekspresi konstan tipeint
yang nilainya dapat direpresentasikan dalam tipe T , maka tipe ekspresi kondisionalnya adalah T. " --- Bahkan menemukan dokumen Java 1.0 di Berkeley. Teks yang sama . --- Ya, selalu seperti itu.Jawaban:
Pertama-tama, kita hanya berbicara tentang variabel lokal . Final yang efektif tidak berlaku untuk bidang. Ini penting, karena semantik untuk
final
bidang sangat berbeda dan tunduk pada pengoptimalan kompiler berat dan janji model memori, lihat $ 17.5.1 tentang semantik bidang akhir.Di tingkat permukaan
final
daneffectively final
untuk variabel lokal memang identik. Namun, JLS membuat perbedaan yang jelas antara keduanya yang sebenarnya memiliki berbagai macam efek dalam situasi khusus seperti ini.Premis
Dari JLS§4.12.4 tentang
final
variabel:Karena
int
primitif, variabelnyaa
adalah variabel konstan .Selanjutnya dari bab yang sama tentang
effectively final
:Jadi dari cara ini bernada, jelas bahwa dalam contoh lain,
a
yang tidak dianggap sebagai variabel konstan, karena belum final , tetapi hanya efektif akhir.Tingkah laku
Sekarang kita memiliki perbedaan, mari kita cari apa yang terjadi dan mengapa keluarannya berbeda.
Anda menggunakan operator bersyarat di
? :
sini, jadi kita harus memeriksa definisinya. Dari JLS§15.25 :Dalam hal ini, kita berbicara tentang ekspresi kondisional numerik , dari JLS§15.25.2 :
Dan itu adalah bagian di mana kedua kasus diklasifikasikan secara berbeda.
efektif akhir
Versi yang
effectively final
cocok dengan aturan ini:Yang merupakan perilaku yang sama seperti yang akan Anda lakukan
5 + 'd'
, yaituint + char
, yang menghasilkanint
. Lihat JLS§5.6Jadi semuanya dipromosikan menjadi
int
apaa
adanyaint
. Itu menjelaskan keluaran dari97
.terakhir
Versi dengan
final
variabel cocok dengan aturan ini:Variabel terakhir
a
adalah tipeint
dan ekspresi konstan (karena memang demikianfinal
). Ini dapat direpresentasikan sebagaichar
, maka hasilnya adalah tipechar
. Itu menyimpulkan hasilnyaa
.Contoh string
Contoh persamaan string didasarkan pada perbedaan inti yang sama,
final
variabel diperlakukan sebagai ekspresi / variabel konstan, daneffectively final
tidak.Di Java, string interning didasarkan pada ekspresi konstan
"a" + "b" + "c" == "abc"
adalah
true
juga (tidak menggunakan konstruksi ini dalam kode nyata).Lihat JLS§3.10.5 :
Mudah untuk dilupakan karena ini terutama berbicara tentang literal, tetapi sebenarnya juga berlaku untuk ekspresi konstan.
sumber
... ? a : 'c'
untuk berperilaku sama apakaha
itu variabel atau konstanta . Tampaknya tidak ada yang salah dengan ekspresi itu. --- Sebaliknya,a + "b" == "ab"
adalah ekspresi yang buruk , karena string perlu dibandingkan menggunakanequals()
( Bagaimana cara membandingkan string di Java? ). Fakta bahwa itu "secara tidak sengaja" bekerja ketikaa
adalah sebuah konstanta , hanyalah sebuah permainan kata dari internal string literal."a" + "b" + "c" == "abc"
harustrue
dalam implementasi Java yang valid.a + "b" == "ab"
masih merupakan ekspresi yang salah . Bahkan jika Anda tahu bahwa itua
adalah sebuah konstanta , itu terlalu rentan untuk tidak meneleponequals()
. Atau mungkin rapuh adalah kata yang lebih baik, yaitu terlalu mungkin untuk berantakan ketika kode dipertahankan di masa mendatang.(final) String str = "a"; Stream.of(null, null). <Runnable>map( x -> () -> System.out.println(str)) .reduce((a,b) -> () -> System.out.println(a == b)) .ifPresent(Runnable::run);
mengubah hasilnya ketikastr
ada (tidak)final
.Aspek lain adalah bahwa jika variabel dinyatakan final dalam tubuh metode, ia memiliki perilaku yang berbeda dari variabel akhir yang diteruskan sebagai parameter.
public void testFinalParameters(final String a, final String b) { System.out.println(a + b == "ab"); } ... testFinalParameters("a", "b"); // Prints false
sementara
public void testFinalVariable() { final String a = "a"; final String b = "b"; System.out.println(a + b == "ab"); // Prints true } ... testFinalVariable();
hal itu terjadi karena compiler tahu bahwa menggunakan
final String a = "a"
satua
variabel akan selalu memiliki"a"
nilai sehinggaa
dan"a"
dapat dipertukarkan tanpa masalah. Berbeda, jikaa
tidak ditentukanfinal
atau ditentukanfinal
tetapi nilainya ditetapkan pada saat runtime (seperti dalam contoh di atas di mana final adalaha
parameternya) kompilator tidak tahu apa-apa sebelum digunakan. Jadi penggabungan terjadi saat runtime dan string baru dibuat, tidak menggunakan kumpulan internal.Pada dasarnya perilakunya adalah: jika kompilator mengetahui bahwa suatu variabel adalah konstanta dapat menggunakannya sama dengan menggunakan konstanta.
Jika variabel tidak ditentukan final (atau final tetapi nilainya ditentukan saat runtime) tidak ada alasan bagi compiler untuk menanganinya sebagai konstanta juga jika nilainya sama dengan konstanta dan nilainya tidak pernah berubah.
sumber
finel
kata kunci diterapkan ke parameter, memiliki semantik berbeda dari yangfinal
diterapkan ke variabel lokal, dll ...final String a; a = "a";
dan mendapatkan perilaku yang sama