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

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

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


person Nicola Ambrosetti    schedule 22.04.2013    source sumber
comment
kemungkinan duplikat Apakah ada contoh spesifik dari kemunduran ketidakcocokan antar versi Java?   -  person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 04.05.2015


Jawaban (5)


Tidak, penggunaan fitur 1.8 dalam kode sumber mengharuskan Anda menargetkan VM 1.8. Saya baru saja mencoba rilis Java 8 yang baru dan mencoba kompilasi dengan -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
person JesperE    schedule 18.03.2014
comment
Tidak, menurutku itu tidak akan terjadi. Java memiliki pangsa pasar desktop yang kecil, namun tetap menguasai pangsa kecil tersebut. Namun hal ini menghambat penerapan versi dan fitur baru. Saya tidak akan dapat menggunakan fitur Java 8 dalam kode yang saya tulis selama beberapa waktu, karena saya ingin menghindari orang harus memutakhirkan instalasi Java lokal mereka. - person JesperE; 15.08.2014
comment
Mengapa? Ya berarti Java 8 dapat dikompilasi untuk dijalankan pada VM Java 7, yang salah menurut kompiler Java 8. - person JesperE; 28.10.2014
comment
Sekarang saya mengerti: TIDAK Anda menjawab judul pertanyaan, bukan isi pertanyaan. - person Abdull; 28.10.2014

Metode default memerlukan perubahan pada bytecode dan JVM sehingga tidak mungkin dilakukan di Java 7. Pemverifikasi bytecode Java 7 dan di bawahnya akan menolak antarmuka dengan badan metode (kecuali untuk metode penginisialisasi statis). Mencoba meniru metode default dengan metode statis di sisi pemanggil tidak akan memberikan hasil yang sama, karena metode default dapat ditimpa di subkelas. Retrolambda memiliki dukungan terbatas untuk melakukan backporting metode default, namun tidak pernah dapat dibackport sepenuhnya karena benar-benar memerlukan fitur JVM baru.

Lambdas dapat berjalan di Java 7 apa adanya, jika kelas API yang diperlukan ada di sana. Instruksi invokedynamic ada di Java 7, tetapi lambda dapat diimplementasikan sehingga menghasilkan kelas lambda pada waktu kompilasi (pembangunan JDK 8 awal melakukannya seperti itu) dalam hal ini instruksi tersebut akan berfungsi pada versi Java apa pun. (Oracle memutuskan untuk menggunakan invokedynamic untuk lambda untuk pemeriksaan di masa mendatang; mungkin suatu hari nanti JVM akan memiliki fungsi kelas satu, sehingga invokedynamic dapat diubah untuk menggunakannya alih-alih membuat kelas untuk setiap lambda, sehingga meningkatkan kinerja.) Apa yang dilakukan Retrolambda adalah bahwa ia memproses semua instruksi invokedynamic dan menggantinya dengan kelas anonim; sama seperti apa yang dilakukan Java 8 saat runtime ketika lamdba invokedynamic dipanggil pertama kali.

Anotasi Berulang hanyalah gula sintaksis. Mereka adalah bytecode yang kompatibel dengan versi sebelumnya. Di Java 7 Anda hanya perlu mengimplementasikan sendiri metode pembantu (mis. getAnnotationsByType) yang menyembunyikan detail implementasi anotasi container yang berisi anotasi berulang.

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

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

Fitur baru lainnya sebagian besar berupa API baru, peningkatan pada HotSpot, dan peralatan . Beberapa API baru tersedia sebagai perpustakaan pihak ketiga (misalnya ThreeTen-Backport dan dukungan streaming).

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

person Esko Luontola    schedule 20.05.2014
comment
Sebenarnya anotasi tipe dapat terlihat saat runtime. stackoverflow.com/questions/22374612/ - person Antimony; 19.02.2016

Sejauh yang saya tahu, tidak ada perubahan di JDK 8 yang memerlukan penambahan bytecode baru. Bagian dari instrumentasi lambda dilakukan menggunakan invokeDynamic (yang sudah ada di JDK 7). Jadi, dari sudut pandang set instruksi JVM, tidak ada yang membuat basis kode tidak kompatibel. Namun, ada banyak perbaikan terkait API dan kompiler yang dapat membuat kode dari JDK 8 sulit untuk dikompilasi/dijalankan di bawah JDK sebelumnya (tetapi saya belum mencobanya).

Mungkin materi referensi berikut dapat membantu memperkaya pemahaman tentang bagaimana perubahan yang terkait dengan lambda diinstrumentasikan.

Ini menjelaskan secara rinci bagaimana segala sesuatunya diinstrumentasikan di bawah tenda. Mungkin Anda dapat menemukan jawaban atas pertanyaan Anda di sana.

person Edwin Dalorzo    schedule 29.04.2013
comment
Tidak ada bytecode baru, tetapi struktur baru. Verifikator akan muntah. - person Jonathan S. Fisher; 12.06.2014
comment
@exabrial, apa yang Anda maksud dengan struktur? - person Abdull; 28.10.2014
comment
Contoh yang bagus adalah Antarmuka. Mereka sekarang dapat berisi metode. Pemverifikasi Java7 tidak dilengkapi untuk menangani hal ini. Semua bytecode lama digunakan, tetapi dengan cara baru. - person Jonathan S. Fisher; 29.10.2014
comment
Saya bertanya-tanya bagaimana kompiler scala dengan begitu banyak fitur bahasa dapat mencapai target rilis jvm bahkan jdk5. - person Marinos An; 05.04.2016
comment
@MarinosAn Apa sebenarnya maksud Anda? MI dengan ciri-ciri yang mengandung metode konkrit, misal: class C extends A with B, diimplementasikan dengan antarmuka normal A dan B serta kelas pendamping A$class dan B$class. kelas C cukup meneruskan metode ke kelas pendamping statis. Tipe mandiri tidak diterapkan sama sekali, lambda ditranspilasi pada waktu kompilasi ke kelas dalam abstrak, begitu pula ekspresi new D with A with B. Pencocokan pola adalah sekumpulan struktur if-else. Pengembalian non-lokal? mekanisme coba-tangkap dari lambda. Ada yang tersisa? (Menariknya, scalac saya mengatakan 1.6 adalah defaultnya) - person Adowrath; 08.04.2017
comment
Tentu saja, tipe mandiri, dll. dikodekan dalam atribut dan anotasi kelas khusus sehingga scalac dapat menggunakan dan menegakkan aturan saat menggunakan kelas yang sudah dikompilasi. - person Adowrath; 08.04.2017
comment
@Adowrath lalu bagaimana Scala memiliki ciri-ciri dengan implementasi default? Ini terlihat seperti metode default di antarmuka Java 8. - person Franklin Yu; 06.09.2017
comment
@FranklinYu Jika targetnya 1,8 atau lebih tinggi: metode default, ya. 1.7 ke bawah? Jika trait A { def foo: Int = 12 } dan class C extends A, secara kasar Anda mendapatkan: interface A { int foo(); }, class A$class { public static int foo(A $this) { return 12; } } dan class C implements A { public int foo() { return A$class.foo(this); } }. Jadi implementasi metode default dipindahkan ke kelas X$class, dan di setiap kelas implementasi sifat tersebut, kompiler menghasilkan metode rintisan yang hanya meneruskan ke metode di X$class, memberikan this sebagai argumen (jika X.foo memanggil X.bar) . - person Adowrath; 07.09.2017
comment
Jadi kode di Java 7 tidak dapat memanggil implementasi default antarmuka Scala saat dijalankan di JRE 7? Jika hal tersebut dapat diterima oleh pengguna Scala, mengapa kita tidak dapat melakukan hal yang sama dengan Java 8? - person Franklin Yu; 07.09.2017
comment
@FranklinYu Oh, maaf. Ya tidak bisa, JRE 7 bahkan tidak bisa memuat Sifat Scala baru dengan metode default. Tapi itu tidak bisa memuat antarmuka Java 8 dengan metode default. Scala 2.12 dan seterusnya memiliki ketergantungan eksplisit pada Java 8 tidak hanya di bagian ini, tetapi juga di tipe SAM (yaitu java.util.Function dan scala.Function1 keduanya ditangani dengan cara yang sama, dan a => a + 1 dapat dikompilasi ke keduanya) kompilasi, menggunakan java.lang.invoke.LambdaMetaFactory. - person Adowrath; 08.10.2017
comment
@Adowrath Jadi Scala tidak benar-benar mencapai target rilis jvm bahkan jdk5, bukan? - person Franklin Yu; 09.10.2017
comment
@FranklinYu 2.12 ke atas tidak lagi, tidak (kecuali dapat menangani -target:jvm-1.7, tapi saya harus mencobanya). 2.11 ke bawah? Ya. (Meskipun menurut saya ini Java 6 dan lebih tinggi sejak beberapa waktu, tidak tahu persis apakah itu akurat) - person Adowrath; 09.10.2017

Jika Anda ingin menggunakan "retrotranslator" cobalah Retrolambda Esko Luontola yang luar biasa: https://github.com/orfjackal/retrolambda

person Stefan Zobel    schedule 17.05.2014

Anda dapat melakukan -source 1.7 -target 1.7 lalu akan dikompilasi. Tapi itu tidak akan dikompilasi jika Anda memiliki fitur khusus Java 8 seperti lambda

person kalgecin    schedule 20.12.2014
comment
Pertanyaannya secara eksplisit mengemukakan penggunaan fitur bahasa baru, jadi -source 1.7 tidak akan berfungsi. - person toolforger; 28.01.2019