Sementara mencari melalui Java Language Specification untuk menjawab pertanyaan ini , saya belajar bahwa
Sebelum kelas diinisialisasi, superclass langsungnya harus diinisialisasi, tetapi antarmuka yang diimplementasikan oleh kelas tersebut tidak diinisialisasi. Demikian pula, superinterfaces dari sebuah antarmuka tidak diinisialisasi sebelum antarmuka diinisialisasi.
Untuk rasa ingin tahu saya sendiri, saya mencobanya dan, seperti yang diharapkan, antarmuka InterfaceType
tidak diinisialisasi.
public class Example {
public static void main(String[] args) throws Exception {
InterfaceType foo = new InterfaceTypeImpl();
foo.method();
}
}
class InterfaceTypeImpl implements InterfaceType {
@Override
public void method() {
System.out.println("implemented method");
}
}
class ClassInitializer {
static {
System.out.println("static initializer");
}
}
interface InterfaceType {
public static final ClassInitializer init = new ClassInitializer();
public void method();
}
Program ini mencetak
implemented method
Namun, jika antarmuka mendeklarasikan default
metode, maka inisialisasi memang terjadi. Pertimbangkan InterfaceType
antarmuka yang diberikan sebagai
interface InterfaceType {
public static final ClassInitializer init = new ClassInitializer();
public default void method() {
System.out.println("default method");
}
}
maka program yang sama di atas akan dicetak
static initializer
implemented method
Dengan kata lain, static
bidang antarmuka diinisialisasi ( langkah 9 dalam Prosedur Inisialisasi Terperinci ) dan static
penginisialisasi jenis yang diinisialisasi dijalankan. Ini berarti antarmuka telah diinisialisasi.
Saya tidak dapat menemukan apa pun di JLS yang menunjukkan bahwa ini harus terjadi. Jangan salah paham, saya mengerti bahwa hal ini harus terjadi jika kelas pelaksana tidak menyediakan implementasi untuk metode tersebut, tetapi bagaimana jika demikian? Apakah kondisi ini hilang dari Spesifikasi Bahasa Java, apakah saya melewatkan sesuatu, atau saya salah menafsirkannya?
sumber
interface
di Jawa tidak harus mendefinisikan metode konkret. Jadi saya terkejut bahwaInterfaceType
kode telah terkompilasi.default
metode .Jawaban:
Ini adalah masalah yang sangat menarik!
Sepertinya JLS bagian 12.4.1 harus membahas ini secara definitif. Namun, perilaku Oracle JDK dan OpenJDK (javac dan HotSpot) berbeda dari yang ditentukan di sini. Secara khusus, Contoh 12.4.1-3 dari bagian ini mencakup inisialisasi antarmuka. Contohnya sebagai berikut:
Output yang diharapkan adalah:
dan memang saya mendapatkan hasil yang diharapkan. Namun, jika metode default ditambahkan ke antarmuka
I
,output berubah menjadi:
yang dengan jelas menunjukkan antarmuka itu
I
sedang diinisialisasi di tempat yang tidak sebelumnya! Kehadiran metode default saja sudah cukup untuk memicu inisialisasi. Metode default tidak harus dipanggil atau diganti atau bahkan disebutkan, juga tidak adanya metode abstrak yang memicu inisialisasi.Spekulasi saya adalah bahwa implementasi HotSpot ingin menghindari penambahan kelas / antarmuka inisialisasi memeriksa ke jalur kritis
invokevirtual
panggilan. Sebelum Java 8 dan metode default,invokevirtual
tidak akan pernah bisa mengeksekusi kode di antarmuka, jadi ini tidak muncul. Orang mungkin mengira ini adalah bagian dari tahap persiapan kelas / antarmuka ( JLS 12.3.2 ) yang menginisialisasi hal-hal seperti tabel metode. Tapi mungkin ini berjalan terlalu jauh dan secara tidak sengaja melakukan inisialisasi penuh.Saya telah mengangkat pertanyaan ini di milis compiler-dev OpenJDK. Ada balasan dari Alex Buckley (editor JLS) di mana dia mengajukan lebih banyak pertanyaan yang ditujukan pada tim implementasi JVM dan lambda. Dia juga mencatat bahwa ada bug dalam spesifikasi di sini di mana dikatakan "T adalah kelas dan metode statis yang dideklarasikan oleh T dipanggil" juga harus diterapkan jika T adalah antarmuka. Jadi, mungkin saja ada bug spesifikasi dan HotSpot di sini.
Pengungkapan : Saya bekerja untuk Oracle di OpenJDK. Jika orang berpikir ini memberi saya keuntungan yang tidak adil karena mendapatkan hadiah yang melekat pada pertanyaan ini, saya bersedia bersikap fleksibel tentang itu.
sumber
Antarmuka tidak diinisialisasi karena bidang konstanta
InterfaceType.init
, yang diinisialisasi oleh nilai non konstan (pemanggilan metode), tidak digunakan di mana pun.Diketahui pada waktu kompilasi bahwa bidang antarmuka konstan tidak digunakan di mana pun, dan antarmuka tidak berisi metode default apa pun (Dalam java-8) sehingga tidak perlu menginisialisasi atau memuat antarmuka.
Antarmuka akan diinisialisasi dalam kasus berikut,
Dalam kasus Metode Default , Anda sedang menerapkan
InterfaceType
. Jadi, JikaInterfaceType
akan berisi metode default apa pun, Ini akan DIBERI (digunakan) dalam mengimplementasikan kelas. Dan Inisialisasi akan menjadi gambar.Tetapi, Jika Anda mengakses bidang antarmuka yang konstan (yang diinisialisasi dengan cara biasa), Inisialisasi antarmuka tidak diperlukan.
Pertimbangkan kode berikut.
Dalam kasus di atas, Antarmuka akan diinisialisasi dan dimuat karena Anda menggunakan bidang
InterfaceType.init
.Saya tidak memberikan contoh metode default seperti yang sudah Anda berikan dalam pertanyaan Anda.
Spesifikasi dan contoh bahasa Java diberikan di JLS 12.4.1 (Contoh tidak berisi metode default.)
Saya tidak dapat menemukan JLS untuk metode Default, mungkin ada dua kemungkinan
sumber
default
metode dan kelas yang mengimplementasikan antarmuka diinisialisasi.File instanceKlass.cpp dari OpenJDK berisi metode inisialisasi
InstanceKlass::initialize_impl
yang sesuai dengan Prosedur Inisialisasi Terperinci di JLS, yang secara analog ditemukan di Inisialisasi di Spesifikasi JVM.Ini berisi langkah baru yang tidak disebutkan di JLS dan bukan di buku JVM yang dirujuk dalam kode:
Jadi inisialisasi ini telah diterapkan secara eksplisit sebagai Langkah 7.5 baru . Ini menunjukkan bahwa implementasi ini mengikuti beberapa spesifikasi, tetapi tampaknya spesifikasi tertulis di situs web belum diperbarui sebagaimana mestinya.
EDIT: Sebagai referensi, komit (mulai Oktober 2012!) Di mana langkah terkait telah disertakan dalam implementasi: http://hg.openjdk.java.net/jdk8/build/hotspot/rev/4735d2c84362
EDIT2: Secara kebetulan, saya menemukan Dokumen ini tentang metode default di hotspot yang berisi catatan tambahan yang menarik di bagian akhir:
sumber
Saya akan mencoba untuk membuat kasus bahwa inisialisasi antarmuka tidak boleh menyebabkan efek samping saluran samping yang bergantung pada subtipe, oleh karena itu, apakah ini bug atau bukan, atau dengan cara apa pun Java memperbaikinya, itu tidak masalah. aplikasi tempat antarmuka pesanan diinisialisasi.
Dalam kasus a
class
, diterima dengan baik bahwa hal itu dapat menyebabkan efek samping yang bergantung pada subkelas. Sebagai contohSetiap subclass dari
Foo
akan berharap bahwa mereka akan melihat $ 1000 di bank, dimanapun dalam kode subclass. Oleh karena itu superclass diinisialisasi sebelum subclass tersebut.Bukankah kita harus melakukan hal yang sama untuk superintefaces juga? Sayangnya, urutan superinterfaces tidak dianggap signifikan, oleh karena itu tidak ada urutan yang jelas untuk menginisialisasi mereka.
Jadi sebaiknya kita tidak menetapkan efek samping semacam ini dalam inisialisasi antarmuka. Bagaimanapun,
interface
ini tidak dimaksudkan untuk fitur-fitur ini (bidang / metode statis) yang kami kumpulkan untuk kenyamanan.Oleh karena itu, jika kita mengikuti prinsip itu, tidak akan menjadi masalah bagi kita di mana antarmuka pesanan diinisialisasi.
sumber