Bisakah kode Java 8 dikompilasi untuk dijalankan di Java 7 JVM?

163

Java 8 memperkenalkan fitur-fitur bahasa baru yang penting seperti ekspresi lambda.

Apakah perubahan dalam bahasa ini disertai dengan perubahan signifikan dalam bytecode yang dikompilasi yang akan mencegahnya dijalankan pada mesin virtual Java 7 tanpa menggunakan retrotranslator?

Nicola Ambrosetti
sumber
duplikat yang mungkin dari Apakah ada contoh spesifik ketidaksesuaian mundur antara versi Java?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Jawaban:

146

Tidak, menggunakan 1,8 fitur dalam kode sumber Anda mengharuskan Anda menargetkan 1,8 VM. Saya baru saja mencoba rilis Java 8 baru dan mencoba kompilasi -target 1.7 -source 1.8, dan kompiler menolak:

$ javac Test -source 1.8 -target 1.7
javac: source release 1.8 requires target release 1.8
JesperE
sumber
4
Tidak, saya pikir itu tidak akan terjadi. Java memiliki pangsa kecil dari pasar desktop, tetapi menjaga pangsa kecil itu dalam genggaman yang cukup ketat. Tapi itu menghambat adopsi versi dan fitur baru. Saya tidak akan dapat menggunakan fitur Java 8 dalam kode yang saya tulis untuk beberapa waktu, karena saya ingin menghindari orang harus memperbarui instalasi Java lokal mereka.
JesperE
Mengapa? "Ya" akan menyiratkan Java 8 dapat dikompilasi untuk dijalankan pada Java 7 VM, yang tidak benar menurut kompiler Java 8.
JesperE
5
Sekarang saya mengerti: "Tidak" Anda menjawab judul pertanyaan, bukan badan pertanyaan.
Abdull
58

Metode default memerlukan perubahan pada bytecode dan JVM yang tidak mungkin dilakukan di Java 7. Verifikasi bytecode Java 7 dan di bawahnya akan menolak antarmuka dengan badan metode (kecuali untuk metode initializer statis). Mencoba meniru metode default dengan metode statis di sisi penelepon tidak akan menghasilkan hasil yang sama, karena metode default dapat ditimpa dalam subkelas. Retrolambda memiliki dukungan terbatas untuk backporting metode default, tetapi tidak pernah dapat sepenuhnya didukung karena benar-benar membutuhkan fitur JVM baru.

Lambdas dapat berjalan di Java 7 apa adanya, jika kelas API yang diperlukan hanya akan ada di sana. Instruksi invokedynamic ada di Java 7, tetapi akan mungkin untuk mengimplementasikan lambdas sehingga menghasilkan kelas lambda pada waktu kompilasi (build JDK 8 awal melakukannya seperti itu) dalam hal ini akan bekerja pada versi Java apa pun. (Oracle memutuskan untuk menggunakan invokedynamic untuk lambdas untuk pembuktian di masa depan; mungkin suatu hari JVM akan memiliki fungsi kelas satu, jadi invokedynamic dapat diubah untuk menggunakannya daripada menghasilkan kelas untuk setiap lambda, sehingga meningkatkan kinerja.) Yang dilakukan Retrolambda adalah bahwa itu memproses semua instruksi invokedynamic dan menggantinya dengan kelas anonim; sama seperti apa yang dilakukan Java 8 saat runtime ketika lamdba invokedynamic disebut pertama kali.

Pengulangan Anotasi hanyalah gula sintaksis. Mereka bytecode kompatibel dengan versi sebelumnya. Di Java 7 Anda hanya perlu menerapkan sendiri metode pembantu (mis. GetAnnotationsByType ) yang menyembunyikan detail implementasi anotasi wadah yang berisi anotasi berulang.

AFAIK, Jenis Anotasi hanya ada pada waktu kompilasi, jadi mereka seharusnya tidak memerlukan perubahan bytecode, jadi hanya mengubah nomor versi bytecode dari kelas Java 8 yang dikompilasi harus cukup untuk membuatnya bekerja di Java 7.

Nama parameter metode ada dalam bytecode dengan Java 7, jadi itu juga kompatibel. Anda dapat mengaksesnya dengan membaca bytecode metode dan melihat nama variabel lokal di informasi debug metode. Sebagai contoh, Spring Framework melakukan hal itu untuk mengimplementasikan @PathVariable , jadi mungkin ada metode pustaka yang bisa Anda panggil. Karena metode antarmuka abstrak tidak memiliki tubuh metode, informasi debug itu tidak ada untuk metode antarmuka di Java 7, dan AFAIK tidak di Java 8.

Fitur-fitur baru lainnya kebanyakan adalah API baru, peningkatan pada HotSpot dan tooling. Beberapa API baru tersedia sebagai perpustakaan pihak ketiga (mis. ThreeTen-Backport dan streamsupport ).

Summa summarum, metode standar membutuhkan fitur JVM baru tetapi fitur bahasa lainnya tidak. Jika Anda ingin menggunakannya, Anda harus mengkompilasi kode di Java 8 dan kemudian mengubah bytecode dengan format Retrolambda ke Java 5/6/7. Minimal versi bytecode perlu diubah, dan javac tidak diizinkan -source 1.8 -target 1.7sehingga diperlukan retrotranslator.

Esko Luontola
sumber
3
Sebenarnya jenis anotasi dapat terlihat runtime. stackoverflow.com/questions/22374612/…
Antimony
33

Sejauh yang saya tahu tidak ada perubahan di JDK 8 yang mengharuskan penambahan bytecodes baru. Bagian dari instrumentasi lambda sedang dilakukan dengan menggunakaninvokeDynamic (yang sudah ada di JDK 7). Jadi, dari sudut pandang set instruksi JVM, tidak ada yang harus membuat basis kode tidak kompatibel. Ada, meskipun, banyak API yang terkait dan peningkatan kompiler yang akan membuat kode dari JDK 8 sulit dikompilasi / dijalankan di bawah JDK sebelumnya (tapi saya belum mencoba ini).

Mungkin bahan referensi berikut dapat membantu memperkaya pemahaman tentang bagaimana perubahan yang terkait dengan lambda sedang diinstrumentasi.

Ini menjelaskan secara rinci bagaimana hal-hal diinstruksikan di bawah tenda. Mungkin Anda dapat menemukan jawaban untuk pertanyaan Anda di sana.

Edwin Dalorzo
sumber
7
Tidak ada bytecode baru, tetapi struktur baru. Verifikasi akan muntah.
Jonathan S. Fisher
12
Contoh yang baik adalah Antarmuka. Mereka sekarang dapat berisi metode. Verifikasi Java7 tidak dilengkapi untuk menangani ini. Semua bytecode lama digunakan, tetapi dengan cara baru.
Jonathan S. Fisher
1
Saya bertanya-tanya bagaimana scala kompiler dengan begitu banyak fitur bahasa dapat mencapai rilis target jvm bahkan jdk5.
Marinos An
1
@MarinosAn Apa maksud Anda tepatnya? MI dengan sifat-sifat yang mengandung metode konkret, misalnya class C extends A with B, diimplementasikan dengan antarmuka normal Adan Bdan kelas pendamping A$classdan B$class. kelas Chanya meneruskan metode ke kelas pendamping statis. Tipe-diri tidak dipaksakan sama sekali, lambda ditransformasikan pada waktu kompilasi ke dalam kelas-kelas dalam abstrak, demikian juga new D with A with Bungkapan. Pencocokan pola adalah sekelompok struktur if-else. Pengembalian non-lokal? mekanisme coba-tangkap dari lambda. Ada yang tersisa? (Menariknya, scalac saya mengatakan 1,6 adalah default)
Adowrath
1
Tentu saja, tipe diri dll dikodekan dalam atribut kelas khusus dan anotasi sehingga scalac dapat menggunakan dan menegakkan aturan ketika menggunakan kelas yang sudah dikompilasi.
Adowrath
-5

Anda dapat melakukannya -source 1.7 -target 1.7kemudian akan dikompilasi. Tapi itu tidak akan dikompilasi jika Anda memiliki fitur khusus java 8 seperti lambdas

kalgecin
sumber
Pertanyaannya secara eksplisit menyatakan penggunaan fitur bahasa baru, jadi -source 1.7tidak akan terbang.
toolforger