Mari kita lihat kode Java sederhana dalam cuplikan berikut:
public class Main {
private int temp() {
return true ? null : 0;
// No compiler error - the compiler allows a return value of null
// in a method signature that returns an int.
}
private int same() {
if (true) {
return null;
// The same is not possible with if,
// and causes a compile-time error - incompatible types.
} else {
return 0;
}
}
public static void main(String[] args) {
Main m = new Main();
System.out.println(m.temp());
System.out.println(m.same());
}
}
Dalam kode Java yang paling sederhana ini, temp()
metode ini tidak mengeluarkan kesalahan kompiler meskipun jenis fungsi yang dikembalikan adalah int
, dan kami mencoba mengembalikan nilainya null
(melalui pernyataan return true ? null : 0;
). Saat dikompilasi, ini jelas menyebabkan pengecualian waktu berjalan NullPointerException
.
Namun, tampaknya hal yang sama salah jika kita mewakili operator ternary dengan if
pernyataan (seperti dalam same()
metode), yang memang mengeluarkan kesalahan waktu kompilasi! Mengapa?
int foo = (true ? null : 0)
dannew Integer(null)
keduanya mengkompilasi dengan baik, yang kedua adalah bentuk eksplisit dari autoboxing.null
keInteger
... Itu akan terlihat seperti "menebak" kepada saya atau "membuat semuanya bekerja" ...Integer foo() { return "1"; }
tidak akan dikompilasi.)Jawaban:
Compiler mengartikan
null
sebagai referensi nol keInteger
, menerapkan aturan autoboxing / unboxing untuk operator bersyarat (seperti yang dijelaskan dalam Spesifikasi Bahasa Jawa, 15.25 ), dan bergerak dengan senang hati. Ini akan menghasilkanNullPointerException
waktu berjalan, yang dapat Anda konfirmasikan dengan mencobanya.sumber
capture conversion
danlub(T1,T2)
) ?? Juga, Apakah benar-benar mungkin untuk menerapkan tinju ke nilai nol? Bukankah ini seperti "menebak" ??lub(T1,T2)
adalah tipe referensi paling spesifik yang sama dalam tipe hierarki T1 dan T2. (Mereka berdua berbagi setidaknya Object, jadi selalu ada jenis referensi yang paling spesifik.)null
tidak dimasukkan ke dalam bilangan bulat , itu ditafsirkan sebagai referensi ke bilangan bulat (referensi nol, tapi itu bukan masalah). Tidak ada objek Integer yang dibangun dari nol, jadi tidak ada alasan untuk NumberFormatException.null
(yang bukan tipe numerik primitif), klausa yang berlaku adalah "Jika p adalah nilai dari jenis lain, konversi tinju setara dengan konversi identitas ". Jadi konversi tinjunull
untukInteger
menghasilkannull
, tanpa memanggilInteger
konstruktor.Saya pikir, kompiler Java mengartikan
true ? null : 0
sebagaiInteger
ekspresi, yang dapat secara implisit dikonversi menjadiint
, mungkin memberiNullPointerException
.Untuk kasus kedua, ekspresi
null
adalah khusus jenis nol lihat , sehingga kodereturn null
membuat jenis ketidakcocokan.sumber
true ? null : 0
sebagaiInteger
? Dengan autoboxing0
dulu ??Sebenarnya, itu semua dijelaskan dalam Spesifikasi Bahasa Jawa .
Oleh karena itu "null" di Anda
(true ? null : 0)
mendapat tipe int dan kemudian diautobox ke Integer.Coba sesuatu seperti ini untuk memverifikasi ini
(true ? null : null)
dan Anda akan mendapatkan kesalahan kompiler.sumber
int
nilai dari fungsi, yang menyebabkan NPE.null
untukInteger
dengannew Integer(null);
"Let T1 menjadi tipe yang hasil dari penerapan konversi tinju ke S1 ..." Anda akan mendapatkanNumberFormatException
dan ini tidak terjadi ...Dalam kasus
if
pernyataan,null
referensi tidak diperlakukan sebagaiInteger
referensi karena tidak berpartisipasi dalam ekspresi yang memaksanya untuk ditafsirkan seperti itu. Oleh karena itu kesalahan dapat dengan mudah ditangkap pada waktu kompilasi karena lebih jelas kesalahan jenis .Adapun operator kondisional, Spesifikasi Bahasa Jawa §15.25 "Operator Bersyarat
? :
" menjawab ini dengan baik dalam aturan untuk bagaimana konversi tipe diterapkan:sumber
0
diautoboxInteger
maka kompiler sedang menjalankan kasus terakhir dari "aturan operator ternary" seperti yang dijelaskan dalam Spesifikasi Bahasa Jawa. Jika itu benar, maka sulit bagi saya untuk percaya bahwa itu kemudian akan melompat ke kasus 3 dari aturan yang sama yaitu memiliki nol dan tipe referensi yang membuat nilai kembali dari operator ternary menjadi tipe referensi (Integer) .. .Integer
? Itulah tepatnya yang terjadi; NPE sedang dibuat dengan mencoba membuka kotak nilai ekspresi untuk mengembalikan suatuint
dari fungsi. Ubah fungsi untuk mengembalikanInteger
dan itu akan kembalinull
tanpa masalah.null
termasuk dalam kategori ini . Selain itu, kita akan masuk ke langkah "Jika tidak, promosi numerik biner (§5.6.2) diterapkan ... Perhatikan bahwa promosi numerik biner melakukan konversi unboxing (§5.1.8) ..." untuk menentukan jenis pengembalian. Tetapi konversi unboxing akan menghasilkan NPE dan ini hanya terjadi pada saat runtime dan tidak ketika mencoba untuk menentukan jenis operator ternary. Saya masih bingung ..null
diperlakukan seolah-olah itu jenisint
, tetapi sebenarnya setara denganthrow new NullPointerException()
, itu saja.Hal pertama yang perlu diingat adalah bahwa operator ternary Java memiliki "tipe", dan bahwa inilah yang akan ditentukan dan dipertimbangkan oleh kompilator apa pun tipe aktual / nyata dari parameter kedua atau ketiga. Tergantung pada beberapa faktor, tipe operator ternary ditentukan dengan cara berbeda seperti yang diilustrasikan dalam Spesifikasi Bahasa Jawa 15.26
Dalam pertanyaan di atas kita harus mempertimbangkan kasus terakhir:
Sejauh ini, ini adalah kasus yang paling rumit setelah Anda menerapkan konversi tangkapan (§5.1.10) dan yang paling utama di lub (T1, T2) .
Dalam bahasa Inggris biasa dan setelah penyederhanaan ekstrim kita dapat menggambarkan proses sebagai penghitungan "Least Common Superclass" (ya, pikirkan LCM) dari parameter kedua dan ketiga. Ini akan memberi kita "tipe" operator ternary. Sekali lagi, apa yang baru saja saya katakan adalah penyederhanaan ekstrem (pertimbangkan kelas yang mengimplementasikan beberapa antarmuka umum).
Misalnya, jika Anda mencoba yang berikut:
Anda akan melihat bahwa jenis ekspresi kondisional yang dihasilkan adalah
java.util.Date
"Least Common Superclass" untukTimestamp
/Time
pair.Karena
null
dapat diautobox ke apa pun, "Least Common Superclass" adalahInteger
kelas dan ini akan menjadi tipe kembalinya ekspresi kondisional (operator ternary) di atas. Nilai kembali kemudian akan menjadi pointer nol dari jenisInteger
dan itulah yang akan dikembalikan oleh operator ternary.Saat runtime, ketika Java Virtual Machine membuka kotak
Integer
aNullPointerException
dilemparkan. Ini terjadi karena JVM mencoba untuk memanggil fungsinull.intValue()
, di mananull
adalah hasil dari autoboxing.Menurut pendapat saya (dan karena pendapat saya tidak ada dalam Spesifikasi Bahasa Jawa, banyak orang akan menganggapnya salah) penyusun melakukan pekerjaan yang buruk dalam mengevaluasi ekspresi dalam pertanyaan Anda. Mengingat bahwa Anda menulis
true ? param1 : param2
kompiler harus segera menentukan bahwa parameter pertama -null
- akan dikembalikan dan itu akan menghasilkan kesalahan kompiler. Ini agak mirip dengan ketika Anda menuliswhile(true){} etc...
dan kompiler mengeluh tentang kode di bawah loop dan menandainyaUnreachable Statements
.Kasus kedua Anda cukup mudah dan jawaban ini sudah terlalu lama ...;)
KOREKSI:
Setelah analisis lain, saya percaya bahwa saya salah mengatakan bahwa suatu
null
nilai dapat dikotak / diautobosisikan ke apa saja. Berbicara tentang Integer kelas, tinju eksplisit terdiri dari memanggilnew Integer(...)
konstruktor atau mungkinInteger.valueOf(int i);
(saya menemukan versi ini di suatu tempat). Yang pertama akan melemparNumberFormatException
(dan ini tidak terjadi) sementara yang kedua tidak akan masuk akal karenaint
tidak bisanull
...sumber
null
dalam kode asli OP tidak kotak. Cara kerjanya adalah: kompiler mengasumsikan bahwanull
referensi ke Integer. Menggunakan aturan untuk tipe ekspresi ternary, itu memutuskan seluruh ekspresi adalah ekspresi Integer. Ini kemudian menghasilkan kode untuk autobox1
(jika kondisi mengevaluasifalse
). Selama eksekusi, kondisi dievaluasitrue
sehingga ekspresi dievaluasinull
. Ketika mencoba mengembalikan suatuint
dari fungsi, itunull
tidak dikotak. Itu kemudian melempar NPE. (Kompilator mungkin mengoptimalkan sebagian besar dari ini.)Sebenarnya, dalam kasus pertama ekspresi dapat dievaluasi, karena kompiler tahu, bahwa itu harus dievaluasi sebagai
Integer
, namun dalam kasus kedua jenis nilai pengembalian (null
) tidak dapat ditentukan, sehingga tidak dapat dikompilasi. Jika Anda melemparkannya keInteger
, kode akan dikompilasi.sumber
sumber
Bagaimana dengan ini:
Outputnya benar, benar.
Kode warna gerhana 1 dalam ekspresi bersyarat sebagai diautobox.
Dugaan saya adalah kompiler melihat tipe pengembalian ekspresi sebagai Object.
sumber