Tutorial Java 9 untuk mereka yang harus bekerja dengan kode lawas

Selamat sore, kolega. Tepat sebulan yang lalu, kami menerima kontrak untuk terjemahan Jawa Modern dari Manning, yang seharusnya menjadi salah satu produk baru kami yang paling menonjol tahun depan. Masalah "Modern" dan "Legacy" di Jawa begitu akut sehingga kebutuhan akan buku semacam itu sudah cukup matang. Skala bencana dan cara mengatasi masalah di Jawa 9 dijelaskan secara singkat dalam sebuah artikel oleh Wayne Citrin, terjemahan yang ingin kami tawarkan kepada Anda hari ini.

Setiap beberapa tahun, dengan merilis versi baru Java, penutur di JavaOne mulai menikmati konstruksi bahasa dan API baru, dan memuji kebajikan mereka. Dan pengembang yang bersemangat, sementara itu, bersemangat untuk memperkenalkan fitur-fitur baru. Gambaran seperti itu jauh dari kenyataan - sama sekali tidak memperhitungkan bahwa sebagian besar programmer sibuk mendukung dan menyelesaikan aplikasi yang sudah ada , dan tidak menulis aplikasi baru dari awal.

Sebagian besar aplikasi - terutama yang komersial - harus kompatibel dengan versi Java sebelumnya yang tidak mendukung semua fitur super-duper baru ini. Akhirnya, sebagian besar pelanggan dan pengguna akhir, terutama di segmen perusahaan besar, waspada terhadap peningkatan radikal ke platform Java, lebih memilih untuk menunggu sampai semakin kuat.

Karena itu, begitu pengembang akan mencoba peluang baru, ia dihadapkan dengan masalah. Apakah Anda menggunakan metode antarmuka default dalam kode Anda? Mungkin - jika Anda beruntung dan aplikasi Anda tidak perlu berinteraksi dengan Java 7 atau lebih rendah. Ingin menggunakan kelas java.util.concurrent.ThreadLocalRandom untuk menghasilkan angka pseudo-acak dalam aplikasi multi-utas? Ini tidak akan berfungsi jika aplikasi Anda harus dijalankan pada Java 6, 7, 8 atau 9 secara bersamaan.

Dengan rilis rilis baru, pengembang yang mendukung kode warisan merasa seperti anak-anak dipaksa untuk menatap jendela toko kue. Mereka tidak diizinkan masuk, jadi nasib mereka adalah kekecewaan dan frustrasi.

Jadi, apakah ada sesuatu dalam rilis baru Java 9 untuk programmer yang terlibat dalam dukungan kode lama? Sesuatu yang bisa membuat hidup mereka lebih mudah? Untungnya ya.

Apa yang harus dilakukan dengan dukungan kode legacy adalah penampilan Java 9

Tentu saja, Anda dapat mendorong kemampuan platform baru ke dalam aplikasi lawas di mana Anda harus mematuhi kompatibilitas ke belakang. Secara khusus, selalu ada peluang untuk memanfaatkan API baru. Namun, itu mungkin menjadi sedikit jelek.

Misalnya, Anda dapat menerapkan pengikatan yang terlambat jika Anda ingin mengakses API baru ketika aplikasi Anda juga perlu bekerja dengan versi Java yang lebih lama yang tidak mendukung API ini. Misalkan Anda ingin menggunakan kelas java.util.stream.LongStream , diperkenalkan di Java 8, dan Anda ingin menggunakan metode anyMatch(LongPredicate) dari kelas ini, tetapi aplikasi tersebut harus kompatibel dengan Java 7. Anda dapat membuat kelas pembantu, seperti ini:

 public classLongStreamHelper { private static Class longStreamClass; private static Class longPredicateClass; private static Method anyMatchMethod; static { try { longStreamClass = Class.forName("java.util.stream.LongStream"); longPredicateClass = Class.forName("java.util.function.LongPredicate"); anyMatchMethod = longStreamClass.getMethod("anyMatch", longPredicateClass): } catch (ClassNotFoundException e) { longStreamClass = null; longPredicateClass = null; anyMatchMethod = null } catch (NoSuchMethodException e) { longStreamClass = null; longPredicateClass = null; anyMatchMethod = null; } public static boolean anyMatch(Object theLongStream, Object thePredicate) throws NotImplementedException { if (longStreamClass == null) throw new NotImplementedException(); try { Boolean result = (Boolean) anyMatchMethod.invoke(theLongStream, thePredicate); return result.booleanValue(); } catch (Throwable e) { // lots of potential exceptions to handle. Let's simplify. throw new NotImplementedException(); } } } 

Ada cara untuk menyederhanakan operasi ini, atau membuatnya lebih umum, atau lebih efektif - Anda dapat ide.

Alih-alih memanggil theLongStream.anyMatch(thePredicate) , seperti yang Anda lakukan di Java 8, Anda bisa memanggil LongStreamHelper.anyMatch(theLongStream, thePredicate) di semua versi Java. Jika Anda berurusan dengan Java 8, ini akan berhasil, tetapi jika dengan Java 7, program akan melempar NotImplementedException .

Kenapa ini jelek? Karena kode dapat menjadi terlalu rumit jika Anda perlu mengakses banyak API (pada kenyataannya, bahkan sekarang, dengan satu API, ini sudah merepotkan). Selain itu, praktik ini bukan tipe-aman, karena kode tidak dapat secara langsung menyebutkan LongStream atau LongPredicate . Akhirnya, praktik ini jauh kurang efisien, karena overhead refleksi, serta tambahan blok try-catch . Karena itu, walaupun bisa dilakukan dengan cara ini, itu tidak terlalu menarik, dan penuh dengan kesalahan karena kecerobohan.

Ya, Anda dapat mengakses API baru, dan kode Anda pada saat yang sama menjaga kompatibilitas, tetapi Anda tidak akan berhasil dengan konstruksi bahasa baru. Sebagai contoh, misalkan kita perlu menggunakan ekspresi lambda dalam kode yang harus tetap kompatibel dan bekerja di Jawa 7. Anda kurang beruntung. Kompiler Java tidak akan memungkinkan Anda menentukan versi kode sumber di atas target. Jadi, jika Anda mengatur tingkat kepatuhan kode sumber ke 1,8 (mis., Java 8), dan tingkat kepatuhan target adalah 1,7 (Java 7), maka kompiler tidak akan mengizinkan Anda.

File JAR multi-versi akan membantu Anda

Baru-baru ini, peluang besar lain muncul untuk menggunakan fitur Java terbaru, sementara memungkinkan aplikasi untuk bekerja dengan versi Java yang lebih lama, di mana aplikasi tersebut tidak didukung. Di Java 9, fitur ini disediakan untuk API baru dan konstruksi bahasa Java baru: kita berbicara tentang file JAR multi-versi .

File JAR multi-versi hampir tidak berbeda dari file JAR lama yang baik, tetapi dengan satu peringatan penting: "ceruk" baru telah muncul di file JAR baru, di mana Anda dapat menulis kelas yang menggunakan fitur Java 9 terbaru. Jika Anda bekerja dengan Java 9, maka JVM akan menemukan "ceruk" ini, akan menggunakan kelas-kelas dari itu dan mengabaikan kelas-kelas dengan nama yang sama dari bagian utama dari file JAR.

Namun, ketika bekerja dengan Java 8 atau lebih rendah, JVM tidak menyadari keberadaan "ceruk" ini. Dia mengabaikannya dan menggunakan kelas dari bagian utama file JAR. Dengan dirilisnya Java 10, β€œceruk” serupa yang baru akan muncul untuk kelas yang menggunakan fitur Java 10 yang paling relevan dan seterusnya.

Dalam JEP 238 , proposal untuk pengembangan Java, yang menjelaskan file JAR yang ravenous, contoh sederhana diberikan. Katakanlah kita memiliki file JAR dengan empat kelas yang berjalan di Java 8 atau lebih rendah:

 JAR root - A.class - B.class - C.class - D.class 

Sekarang bayangkan bahwa setelah rilis Java 9, kami menulis ulang kelas A dan B sehingga mereka dapat menggunakan fitur-fitur baru khusus untuk Java 9. Kemudian Java 10 keluar, dan kami menulis ulang kelas A sehingga dapat menggunakan fitur-fitur baru Java 10. , aplikasi harus tetap berfungsi baik dengan Java 8. File JAR multi-versi yang baru terlihat seperti ini:

 JAR root - A.class - B.class - C.class - D.class - META-INF Versions - 9 - A.class - B.class - 10 - A.class 

File JAR tidak hanya memperoleh struktur baru; sekarang dalam manifesnya ditunjukkan bahwa file ini multi-versi.

Ketika Anda menjalankan file JAR ini di Java 8 JVM, ia mengabaikan bagian \META-INF\Versions karena ia bahkan tidak mencurigainya atau mencarinya. Hanya kelas asli A, B, C, dan D yang digunakan.

Saat berjalan di bawah Java 9, kelas-kelas yang terletak di \META-INF\Versions\9 digunakan, apalagi, mereka digunakan sebagai pengganti kelas A dan B asli, tetapi kelas-kelas di \META-INF\Versions\10 diabaikan.

Saat berjalan di bawah Java 10, kedua cabang \META-INF\Versions ; khususnya, versi A dari Java 10, versi B dari Java 9, dan versi default C dan D.

Jadi, jika dalam aplikasi Anda, Anda memerlukan API ProcessBuilder baru dari Java 9, tetapi Anda perlu memastikan bahwa aplikasi terus bekerja di bawah Java 8, cukup tulis versi baru dari kelas Anda menggunakan ProcessBuilder di bagian \META-INF\Versions\9 dari file JAR , dan meninggalkan kelas lama di bagian utama arsip, digunakan secara default. Ini adalah cara termudah untuk menggunakan fitur-fitur baru Java 9 tanpa mengorbankan kompatibilitas ke belakang.

Java 9 JDK memiliki versi alat jar.exe yang mendukung pembuatan file JAR multi-versi. Alat non-JDK lainnya juga menyediakan dukungan ini.

Java 9: ​​modul, modul di mana-mana

Sistem modul Java 9 (juga dikenal sebagai Project Jigsaw) tidak diragukan lagi merupakan perubahan terbesar di Jawa 9. Salah satu tujuan modularisasi adalah untuk memperkuat mekanisme enkapsulasi Jawa sehingga pengembang dapat menentukan API mana yang disediakan untuk komponen lain dan dapat menghitung, bahwa JVM akan memberlakukan enkapsulasi. Enkapsulasi lebih kuat dengan modularisasi daripada dengan pengubah akses public/protected/private untuk kelas atau anggota kelas.

Tujuan kedua dari modularisasi adalah untuk menunjukkan modul mana yang memerlukan modul lain untuk bekerja, dan sebelum memulai aplikasi, pastikan bahwa semua modul yang diperlukan sudah ada. Dalam hal ini, modul lebih kuat daripada mekanisme classpath tradisional, karena jalur classpath tidak diperiksa terlebih dahulu, dan kesalahan mungkin terjadi karena kurangnya kelas yang diperlukan. Dengan demikian, classpath yang salah dapat dideteksi ketika aplikasi memiliki waktu untuk bekerja cukup lama, atau setelah diluncurkan berkali-kali.
Seluruh sistem modul besar dan kompleks, dan diskusi terperinci tentang hal itu berada di luar cakupan artikel ini (Berikut ini penjelasan yang bagus dan terperinci ). Di sini saya akan memperhatikan aspek-aspek modularisasi yang membantu pengembang dengan dukungan aplikasi warisan.

Modularisasi adalah hal yang baik, dan pengembang harus mencoba memecah kode baru ke dalam modul bila memungkinkan, bahkan jika aplikasi lainnya (belum) termodulasi. Untungnya, ini mudah dilakukan berkat spesifikasi untuk bekerja dengan modul.

Pertama, file JAR menjadi termodulasi (dan berubah menjadi modul) dengan tampilan file module-info.class (dikompilasi dari module-info.java) di akar file JAR. module-info.java berisi metadata, khususnya, nama modul yang paketnya diekspor (yaitu menjadi terlihat dari luar), yang memerlukan modul modul ini, dan beberapa informasi lainnya.

Informasi dalam module-info.class hanya terlihat ketika JVM mencarinya - yaitu, sistem memperlakukan file JAR yang termodulasi seperti biasa jika bekerja dengan versi Java yang lebih lama (diasumsikan bahwa kode tersebut dikompilasi untuk bekerja dengan versi Java yang lebih lama Sebenarnya, dibutuhkan sedikit chemistry, dan masih Java 9 yang ditetapkan sebagai versi target module-info.class, tetapi ini nyata).

Dengan demikian, Anda harus dapat menjalankan file JAR termodulasi dengan Java 8 dan di bawah, asalkan dalam hal lain mereka juga kompatibel dengan versi Java sebelumnya. Juga perhatikan bahwa module-info.class dapat, dengan reservasi, ditempatkan di area berversi dari file JAR multi-versi .

Di Java 9, ada classpath dan path modul. dan jalur modul. Classpath berfungsi seperti biasa. Jika Anda meletakkan file JAR termodulasi dalam classpath, itu terbuang seperti file JAR lainnya. Artinya, jika Anda memodulasi file JAR, dan aplikasi Anda belum siap untuk memperlakukannya sebagai modul, Anda dapat meletakkannya di classpath, itu akan berfungsi seperti biasa. Kode lawas Anda harus menanganinya dengan cukup sukses.

Juga perhatikan bahwa koleksi semua file JAR di classpath dianggap sebagai bagian dari modul tunggal yang tidak disebutkan namanya. Modul semacam itu dianggap yang paling umum, namun, ia mengekspor semua informasi ke modul lain, dan dapat merujuk ke modul lain mana pun. Jadi, jika Anda belum memiliki aplikasi Java termodulasi, tetapi ada beberapa pustaka lama yang juga tidak termodulasi (dan mungkin tidak akan pernah) - Anda cukup meletakkan semua pustaka ini di classpath, dan seluruh sistem akan berfungsi dengan baik.

Java 9 memiliki lintasan modul yang bekerja bersama dengan classpath. Saat menggunakan modul dari jalur ini, JVM dapat memeriksa (baik pada waktu kompilasi dan pada saat run time) apakah semua modul yang diperlukan sudah ada dan melaporkan kesalahan jika ada modul yang hilang. Semua file JAR di classpath, sebagai anggota modul yang tidak disebutkan namanya, dapat diakses oleh modul yang terdaftar di jalur modular - dan sebaliknya.

Tidaklah sulit untuk mentransfer file JAR dari classpath ke jalur modul - dan mengambil keuntungan penuh dari modularisasi. Pertama, Anda bisa menambahkan file module-info.class ke file JAR, dan kemudian meletakkan file JAR yang termodulasiasi di lintasan modul. Modul yang baru dibuat tersebut masih akan dapat mengakses semua file JAR yang tersisa di classpath JAR, karena mereka memasukkan modul yang tidak disebutkan namanya dan tetap dalam akses.

Mungkin juga Anda tidak ingin memodulasi file JAR, atau bahwa file JAR bukan milik Anda, tetapi milik orang lain, jadi Anda tidak dapat memodulasinya sendiri. Dalam hal ini, file JAR masih dapat diletakkan di jalur modul, itu akan menjadi modul otomatis.

Modul otomatis dianggap sebagai modul, meskipun tidak memiliki file module-info.class . Modul ini adalah nama yang sama dengan file JAR yang dikandungnya, dan modul lain dapat secara eksplisit memintanya. Secara otomatis mengekspor semua API yang tersedia untuk umum dan membaca (mis., Mensyaratkan) semua modul bernama lainnya serta modul tanpa nama.

Dengan demikian, file JAR yang tidak dimodifikasi dari classpath dapat diubah menjadi modul tanpa melakukan apa pun. File JAR yang diwarisi secara otomatis dikonversi ke modul, mereka hanya kekurangan beberapa informasi yang akan menentukan apakah semua modul yang diperlukan sudah ada, atau menentukan apa yang hilang.

Tidak semua file JAR yang tidak dimodifikasi dapat dipindahkan ke jalur modul dan diubah menjadi modul otomatis. Ada aturannya: paket bisa menjadi bagian dari hanya satu modul bernama . Artinya, jika sebuah paket terletak di lebih dari satu file JAR, maka hanya satu file JAR dengan paket ini dalam komposisi yang dapat diubah menjadi modul otomatis. Sisanya dapat tetap berada di classpath dan bergabung dengan modul yang tidak disebutkan namanya.

Sepintas, mekanisme yang dijelaskan di sini tampak rumit, tetapi dalam praktiknya sangat sederhana. Faktanya, dalam hal ini hanya Anda dapat meninggalkan file JAR lama di classpath atau memindahkannya ke jalur modul. Anda dapat memecahnya menjadi modul atau tidak. Dan ketika file JAR lama Anda termodulasi, Anda bisa membiarkannya di classpath atau memindahkannya ke jalur modul.

Dalam kebanyakan kasus, semuanya harus berfungsi, seperti sebelumnya. File JAR Anda yang diwarisi harus berakar di sistem modular baru. Semakin Anda memodulasi kode, semakin banyak informasi dependensi yang perlu Anda periksa, dan modul dan API yang hilang akan terdeteksi pada tahap pengembangan lebih awal dan mungkin akan menghemat banyak pekerjaan.

Java 9 "lakukan sendiri": Modular JDK dan Jlink

Salah satu masalah dengan aplikasi Java lawas adalah bahwa pengguna akhir mungkin tidak bekerja dengan lingkungan Java yang sesuai. Salah satu cara untuk menjamin kesehatan aplikasi Java adalah dengan menyediakan runtime dengan aplikasi tersebut. Java memungkinkan Anda membuat JRE pribadi (dapat didistribusikan) yang dapat didistribusikan dalam aplikasi. Inilah cara membuat JRE pribadi. Sebagai aturan, hierarki file JRE diambil, yang diinstal dengan JDK, file yang diperlukan disimpan, dan file opsional dengan fungsi yang mungkin diperlukan dalam aplikasi Anda disimpan.

Prosesnya agak merepotkan: Anda harus menjaga hierarki file instalasi, sambil berhati-hati, sehingga Anda tidak ketinggalan satu file, bukan direktori tunggal. Ini dengan sendirinya tidak ada salahnya, namun, Anda masih ingin menyingkirkan semua yang berlebihan, karena file-file ini memakan ruang. Ya, mudah untuk menyerah dan membuat kesalahan seperti itu.

Jadi mengapa tidak mendelegasikan pekerjaan ini ke JDK?

Di Java 9, Anda dapat membuat lingkungan mandiri yang ditambahkan ke aplikasi - dan di lingkungan ini akan ada semua yang diperlukan agar aplikasi berfungsi. Anda tidak perlu khawatir lagi bahwa komputer pengguna akan memiliki lingkungan yang salah untuk mengeksekusi Java, Anda tidak perlu khawatir bahwa Anda sendiri membangun JRE pribadi secara tidak tepat.

Sumber daya utama untuk membuat gambar yang dapat dieksekusi mandiri seperti itu adalah sistem modular. Sekarang Anda dapat memodulasi tidak hanya kode Anda sendiri, tetapi juga Java 9 JDK itu sendiri. Sekarang perpustakaan kelas Java adalah kumpulan modul, dan alat JDK juga terdiri dari modul. Sistem modul mengharuskan Anda untuk menentukan modul kelas dasar yang diperlukan dalam kode Anda, dan Anda menentukan elemen JDK yang diperlukan.

Untuk menyatukan semuanya, Java 9 menyediakan alat baru khusus yang disebut jlink . Dengan meluncurkan jlink, Anda mendapatkan hierarki file - persis yang Anda perlukan untuk menjalankan aplikasi Anda, tidak lebih, tidak kurang. Perangkat seperti itu akan jauh lebih kecil daripada JRE standar, apalagi, itu akan menjadi platform-spesifik (yaitu, dipilih untuk sistem operasi dan mesin tertentu). Oleh karena itu, jika Anda ingin membuat gambar yang dapat dieksekusi seperti itu untuk platform lain, Anda harus menjalankan jlink dalam konteks instalasi pada setiap platform tertentu yang Anda butuhkan gambar tersebut.

Perhatikan juga: jika Anda menjalankan jlink dengan aplikasi di mana tidak ada yang termodulasi, alat tersebut tidak memiliki informasi yang diperlukan untuk menekan JRE, jadi jlink tidak akan memiliki apa pun selain mengemas seluruh JRE. Bahkan dalam kasus ini, itu akan sedikit lebih nyaman bagi Anda: jlink akan mengemas JRE untuk Anda, jadi Anda tidak bisa khawatir tentang cara menyalin hierarki file dengan benar.

Dengan jlink, menjadi mudah untuk mengemas aplikasi dan semua yang Anda butuhkan untuk menjalankannya - dan Anda tidak perlu khawatir bahwa Anda akan melakukan sesuatu yang salah. Alat hanya akan mengemas bagian runtime yang diperlukan agar aplikasi berfungsi. Artinya, aplikasi Java warisan dijamin untuk menerima lingkungan di mana ia akan beroperasi.

Pertemuan yang lama dan yang baru

, Java- , , . Java 9, , API , ( ) , , Java.

Java 9: -, , , Java.

JAR- Java 9 JAR-, Java . , Java 9, Java 8 – .

Java, , JAR- , . , , Β« Β» .

JDK jlink, , . , Java – .

Java, Java 9 , – , , Java.

Source: https://habr.com/ru/post/id417733/


All Articles