JavaRush /Java Blog /Random-ID /Fitur Java 8 – Panduan Utama (Bagian 1)
0xFF
Level 9
Донецк

Fitur Java 8 – Panduan Utama (Bagian 1)

Dipublikasikan di grup Random-ID
Bagian pertama terjemahan artikel Fitur Java 8 – Panduan ULTIMATE . Bagian kedua ada di sini (link dapat berubah). Fitur Java 8 – Panduan Utama (Bagian 1) - 1 Catatan Editor : Artikel ini diterbitkan ketika Java 8 tersedia untuk umum dan semua indikasi menunjukkan bahwa ini memang versi mayor. Di sini kami telah menyediakan banyak panduan Java Code Geeks seperti Bermain dengan Java 8 – Lambdas dan Konkurensi , Panduan API Tanggal dan Waktu Java 8: LocalDateTime dan Kelas Abstrak vs. Antarmuka di Era Java 8 . Kami juga menautkan ke 15 tutorial Java 8 yang harus dibaca dari sumber lain . Tentu saja kita melihat beberapa kekurangannya, seperti Sisi Gelap Java 8 . Jadi, inilah saatnya mengumpulkan semua fitur utama Java 8 di satu tempat demi kenyamanan Anda. Menikmati!

1. Perkenalan

Tidak diragukan lagi, rilis Java 8 adalah peristiwa terbesar sejak Java 5 (dirilis cukup lama, pada tahun 2004). Ini membawa banyak fitur baru ke Java baik dalam bahasa, compiler, perpustakaan, alat dan JVM (Java Virtual Machine). Dalam tutorial ini, kita akan melihat perubahan ini dan mendemonstrasikan berbagai kasus penggunaan dengan contoh nyata. Panduan ini terdiri dari beberapa bagian, yang masing-masing membahas aspek spesifik platform:
  • Bahasa
  • Penyusun
  • Perpustakaan
  • Peralatan
  • Lingkungan Waktu Proses (JVM)

2. Fitur baru di Java 8

Bagaimanapun, Java 8 adalah rilis besar. Bisa dibilang butuh waktu lama karena implementasi fitur-fitur yang dicari oleh setiap pengembang Java. Di bagian ini kita akan membahas sebagian besarnya.

2.1. Lambdas dan Antarmuka Fungsional

Lambdas (juga dikenal sebagai metode privat atau anonim) adalah perubahan bahasa terbesar dan paling diantisipasi di seluruh rilis Java 8. Lambdas memungkinkan kita menentukan fungsionalitas sebagai argumen metode (dengan mendeklarasikan fungsi di sekitarnya), atau menentukan kode sebagai data : konsep yang familiar bagi setiap pengembang fungsional.pemrograman _ Banyak bahasa pada platform JVM (Groovy, Scala , ...) memiliki lambda sejak hari pertama, tetapi pengembang Java tidak punya pilihan selain merepresentasikan lambda melalui kelas anonim. Perdebatan desain lambda menyita banyak waktu dan tenaga publik. Namun akhirnya ditemukan kompromi, yang berujung pada munculnya desain baru yang ringkas. Dalam bentuknya yang paling sederhana, lambda dapat direpresentasikan sebagai daftar parameter yang dipisahkan koma, simbol –> , dan isi. Misalnya:
Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) )
Perhatikan bahwa tipe argumen e ditentukan oleh kompiler. Selain itu, Anda dapat secara eksplisit menentukan tipe parameter dengan membungkus parameter dalam tanda kurung. Misalnya:
Arrays.asList( "a", "b", "d" ).forEach( ( String e ) -> System.out.println( e ) );
Jika badan lambda lebih kompleks, maka dapat dibungkus dengan kurung kurawal, mirip dengan definisi fungsi reguler di Java. Misalnya:
Arrays.asList( "a", "b", "d" ).forEach( e -< {
    System.out.print( e );
    System.out.print( e );
} );
Lambda dapat merujuk ke anggota kelas dan variabel lokal (secara implisit membuat panggilan menjadi efektif terlepas dari apakah finalbidang tersebut diakses atau tidak). Misalnya, 2 cuplikan ini setara:
String separator = ",";
Arrays.asList( "a", "b", "d" ).forEach(
    ( String e ) -> System.out.print( e + separator ) );
DAN:
final String separator = ",";
Arrays.asList( "a", "b", "d" ).forEach(
    ( String e ) -> System.out.print( e + separator ) );
Lambdas dapat mengembalikan nilai. Tipe pengembalian akan ditentukan oleh kompiler. Deklarasi returntidak diperlukan jika badan lambda terdiri dari satu baris. Dua cuplikan kode di bawah ini setara:
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) );
DAN:
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> {
    int result = e1.compareTo( e2 );
    return result;
} );
Pengembang bahasa sudah lama berpikir tentang bagaimana membuat fungsi yang ada ramah lambda. Hasilnya, muncullah konsep antarmuka fungsional. Antarmuka fungsional adalah antarmuka yang hanya memiliki satu metode. Hasilnya, secara implisit dapat diubah menjadi ekspresi lambda. java.lang.Runnabledan java.util.concurrent.Callabledua contoh bagus antarmuka fungsional. Dalam praktiknya, antarmuka fungsional sangat rapuh: jika seseorang menambahkan satu metode lain ke dalam definisi antarmuka, metode tersebut tidak akan berfungsi lagi dan proses kompilasi tidak akan selesai. Untuk menghindari kerapuhan ini dan secara eksplisit mendefinisikan maksud antarmuka sebagai fungsional, anotasi khusus ditambahkan di Java 8 @FunctionalInterface(semua antarmuka yang ada di perpustakaan Java menerima anotasi @FunctionalInterface). Mari kita lihat definisi sederhana dari antarmuka fungsional:
@FunctionalInterface
public interface Functional {
    void method();
}
Ada satu hal yang perlu diingat: metode default dan metode statis tidak melanggar prinsip antarmuka fungsional dan dapat dideklarasikan:
@FunctionalInterface
public interface FunctionalDefaultMethods {
    void method();

    default void defaultMethod() {
    }
}
Lambdas adalah fitur paling populer di Java 8. Lambdas memiliki potensi untuk menarik lebih banyak pengembang ke platform luar biasa ini dan memberikan dukungan cerdas untuk fitur-fitur di Java murni. Untuk informasi lebih detail, silakan merujuk ke dokumentasi resmi .

2.2. Antarmuka Default dan Metode Statis

Java 8 memperluas definisi antarmuka dengan dua konsep baru: metode default dan metode statis. Metode default membuat antarmuka agak mirip dengan ciri, namun memiliki tujuan yang sedikit berbeda. Mereka memungkinkan Anda untuk menambahkan metode baru ke antarmuka yang ada tanpa merusak kompatibilitas versi antarmuka yang ditulis sebelumnya. Perbedaan antara metode default dan metode abstrak adalah metode abstrak harus diimplementasikan sedangkan metode default tidak. Sebaliknya, setiap antarmuka harus menyediakan apa yang disebut implementasi default, dan semua turunan akan menerimanya secara default (dengan kemampuan untuk mengesampingkan implementasi default ini jika perlu). Mari kita lihat contoh di bawah ini.
private interface Defaulable {
    // Интерфейсы теперь разрешают методы по умолчанию,
    // клиент может реализовывать  (переопределять)
    // or не реализовывать его
    default String notRequired() {
        return "Default implementation";
    }
}

private static class DefaultableImpl implements Defaulable {
}

private static class OverridableImpl implements Defaulable {
    @Override
    public String notRequired() {
        return "Overridden implementation";
    }
}
Antarmuka Defaulablemendeklarasikan metode default notRequired()menggunakan kata kunci defaultsebagai bagian dari definisi metode. Salah satu kelas, DefaultableImpl, mengimplementasikan antarmuka ini dengan membiarkan metode default apa adanya. Kelas lain, OverridableImpl, mengesampingkan implementasi default dan menyediakan implementasinya sendiri. Fitur menarik lainnya yang diperkenalkan di Java 8 adalah antarmuka dapat mendeklarasikan (dan menawarkan implementasi) metode statis. Berikut ini contohnya:
private interface DefaulableFactory {
    // Interfaces now allow static methods
    static Defaulable create( Supplier<Defaulable> supplier ) {
        return supplier.get();
    }
}
Sepotong kecil kode menggabungkan metode default dan metode statis dari contoh di atas:
public static void main( String[] args ) {
    Defaulable defaulable = DefaulableFactory.create( DefaultableImpl::new );
    System.out.println( defaulable.notRequired() );

    defaulable = DefaulableFactory.create( OverridableImpl::new );
    System.out.println( defaulable.notRequired() );
}
Output konsol dari program ini terlihat seperti ini:
Default implementation
Overridden implementation
Implementasi metode default di JVM sangat efisien dan pemanggilan metode didukung oleh instruksi bytecode. Metode default memungkinkan antarmuka Java yang ada berkembang tanpa mengganggu proses kompilasi. Contoh yang baik adalah banyaknya metode yang ditambahkan ke antarmuka java.util.Collection: stream(), parallelStream(), forEach(), removeIf(), ... Meskipun kuat, metode default harus digunakan dengan hati-hati: sebelum mendeklarasikan metode default, Anda harus berpikir dua kali apakah ini benar-benar diperlukan karena ini dapat menyebabkan untuk mengkompilasi ambiguitas dan kesalahan dalam hierarki yang kompleks. Untuk informasi lebih detail, silakan merujuk ke dokumentasi .

2.3. Metode Referensi

Metode referensi menerapkan sintaksis yang berguna untuk mereferensikan metode atau konstruktor kelas atau objek (instance) Java yang ada. Bersama dengan ekspresi lambda, metode referensi membuat konstruksi bahasa menjadi ringkas dan ringkas, menjadikannya berbasis templat. Di bawah ini adalah kelas Carsebagai contoh definisi metode yang berbeda, mari soroti empat jenis metode referensi yang didukung:
public static class Car {
    public static Car create( final Supplier<Car> supplier ) {
        return supplier.get();
    }

    public static void collide( final Car car ) {
        System.out.println( "Collided " + car.toString() );
    }

    public void follow( final Car another ) {
        System.out.println( "Following the " + another.toString() );
    }

    public void repair() {
        System.out.println( "Repaired " + this.toString() );
    }
}
Metode referensi pertama adalah referensi ke konstruktor dengan sintaksis Class::newatau alternatif untuk obat generik Class< T >::new. Perhatikan bahwa konstruktor tidak memiliki argumen.
final Car car = Car.create( Car::new );
final List<Car> cars = Arrays.asList( car );
Opsi kedua adalah referensi ke metode statis dengan sintaksis Class::static_method. Perhatikan bahwa metode ini mengambil tepat satu parameter bertipe Car.
cars.forEach( Car::collide );
Tipe ketiga adalah referensi ke metode turunan dari objek arbitrer dari tipe tertentu dengan sintaksis Class::method. Perhatikan bahwa tidak ada argumen yang diterima oleh metode ini.
cars.forEach( Car::repair );
Dan yang terakhir, tipe keempat adalah referensi ke metode turunan dari kelas tertentu dengan sintaksis instance::method. Harap dicatat bahwa metode ini hanya menerima satu parameter bertipe Car.
final Car police = Car.create( Car::new );
cars.forEach( police::follow );
Menjalankan semua contoh ini sebagai program Java menghasilkan keluaran konsol berikut (referensi kelas Carmungkin berbeda):
Collided com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
Repaired com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
Following the com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
Untuk informasi lebih rinci dan rincian metode referensi, silakan merujuk ke dokumentasi resmi .

2.4. Anotasi duplikat

Sejak Java 5 memperkenalkan dukungan untuk anotasi , fitur ini menjadi sangat populer dan banyak digunakan. Namun, salah satu keterbatasan penggunaan anotasi adalah kenyataan bahwa anotasi yang sama tidak dapat dideklarasikan lebih dari satu kali di tempat yang sama. Java 8 melanggar aturan ini dan memperkenalkan anotasi duplikat. Hal ini memungkinkan anotasi yang sama diulang beberapa kali di lokasi deklarasinya. Anotasi duplikat harus diberi anotasi sendiri menggunakan annotation @Repeatable. Faktanya, ini bukanlah perubahan bahasa melainkan trik kompiler, sedangkan tekniknya tetap sama. Mari kita lihat contoh sederhana:
package com.javacodegeeks.java8.repeatable.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

public class RepeatingAnnotations {
    @Target( ElementType.TYPE )
    @Retention( RetentionPolicy.RUNTIME )
    public @interface Filters {
        Filter[] value();
    }

    @Target( ElementType.TYPE )
    @Retention( RetentionPolicy.RUNTIME )
    @Repeatable( Filters.class )
    public @interface Filter {
        String value();
    };

    @Filter( "filter1" )
    @Filter( "filter2" )
    public interface Filterable {
    }

    public static void main(String[] args) {
        for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) {
            System.out.println( filter.value() );
        }
    }
}
Seperti yang bisa kita lihat, kelas Filterdianotasi dengan @Repeatable( Filters. class). Filtershanyalah pemilik anotasi Filter, tetapi kompiler Java mencoba menyembunyikan kehadirannya dari pengembang. Jadi, antarmuka Filterableberisi anotasi Filteryang dideklarasikan dua kali (tanpa menyebutkan Filters). Reflection API juga menyediakan metode baru getAnnotationsByType()untuk mengembalikan anotasi duplikat dari beberapa jenis (ingat bahwa Filterable. class.getAnnotation( Filters. class) akan mengembalikan instance yang diinjeksi kompiler Filters). Output programnya akan terlihat seperti ini:
filter1
filter2
Untuk informasi lebih detail, silakan merujuk ke dokumentasi resmi .

2.5. Inferensi tipe yang ditingkatkan

Kompiler Java 8 telah menerima banyak perbaikan inferensi tipe. Dalam banyak kasus, parameter tipe eksplisit dapat ditentukan oleh kompiler, sehingga membuat kode lebih bersih. Mari kita lihat salah satu contohnya:
package com.javacodegeeks.java8.type.inference;

public class Value<T> {
    public static<T> T defaultValue() {
        return null;
    }

    public T getOrDefault( T value, T defaultValue ) {
        return ( value != null ) ? value : defaultValue;
    }
}
Dan berikut kegunaannya dengan type Value<String>:
package com.javacodegeeks.java8.type.inference;

public class TypeInference {
    public static void main(String[] args) {
        final Value<String> value = new Value<>();
        value.getOrDefault( "22", Value.defaultValue() );
    }
}
Parameter tipe Value.defaultValue()ditentukan secara otomatis dan tidak perlu diberikan secara eksplisit. Di Java 7, contoh yang sama tidak akan dikompilasi dan harus ditulis ulang sebagai <NOBR>Value.<String>defaultValue()</NOBR>.

2.6. Dukungan anotasi yang diperluas

Java 8 memperluas konteks di mana anotasi dapat digunakan. Saat ini, hampir semua hal dapat memiliki anotasi: variabel lokal, tipe generik, superkelas dan antarmuka yang diimplementasikan, bahkan pengecualian metode. Beberapa contoh disajikan di bawah ini:
package com.javacodegeeks.java8.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Collection;

public class Annotations {
    @Retention( RetentionPolicy.RUNTIME )
    @Target( { ElementType.TYPE_USE, ElementType.TYPE_PARAMETER } )
    public @interface NonEmpty {
    }

    public static class Holder<@NonEmpty T> extends @NonEmpty Object {
        public void method() throws @NonEmpty Exception {
        }
    }

    @SuppressWarnings( "unused" )
    public static void main(String[] args) {
        final Holder<String> holder = new @NonEmpty Holder<String>();
        @NonEmpty Collection<@NonEmpty String> strings = new ArrayList<>();
    }
}
ElementType.TYPE_USEdan ElementType.TYPE_PARAMETERdua tipe elemen baru untuk mendeskripsikan konteks anotasi yang relevan. Annotation Processing APIjuga telah mengalami sedikit perubahan untuk mengenali jenis anotasi baru di Java.

3. Fitur baru pada kompiler Java

3.1. Nama parameter

Sepanjang waktu, pengembang Java telah menemukan berbagai cara untuk menyimpan nama parameter metode dalam bytecode Java agar tersedia saat runtime (misalnya, perpustakaan Paranamer ). Terakhir, Java 8 membuat fungsi sulit ini dalam bahasa (menggunakan API dan metode Refleksi Parameter.getName()) dan bytecode (menggunakan argumen kompiler baru javac:) –parameters.
package com.javacodegeeks.java8.parameter.names;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class ParameterNames {
    public static void main(String[] args) throws Exception {
        Method method = ParameterNames.class.getMethod( "main", String[].class );
        for( final Parameter parameter: method.getParameters() ) {
            System.out.println( "Parameter: " + parameter.getName() );
        }
    }
}
Jika Anda mengkompilasi kelas ini tanpa menggunakan argumen –parametersdan kemudian menjalankan programnya, Anda akan melihat sesuatu seperti ini:
Parameter: arg0
Dengan parameter –parametersyang diteruskan ke kompiler, keluaran program akan berbeda (nama sebenarnya dari parameter akan ditampilkan):
Parameter: args
Untuk pengguna Maven tingkat lanjut, argumen –parameters dapat ditambahkan ke kompilasi menggunakan bagian maven-compiler-plugin:
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.1</version>
    <configuration>
    <compilerArgument>-parameters</compilerArgument>
    <source>1.8</source>
    <target>1.8</target>
    </configuration>
</plugin>
Untuk memeriksa ketersediaan nama parameter ada isNamePresent()metode mudah yang disediakan oleh kelas Parameter.

4. Alat Java Baru

Java 8 hadir dengan seperangkat alat baris perintah baru. Di bagian ini kita akan melihat yang paling menarik.

4.1. Mesin Nashorn: jjs

jjs adalah mesin Nashorn mandiri yang berbasis baris perintah. Dibutuhkan daftar file kode sumber JavaScript dan menjalankannya. Misalnya, mari buat file func.js dengan konten berikut:
function f() {
     return 1;
};

print( f() + 1 );
Untuk menjalankan file ini, mari kita berikan sebagai argumen ke jjs :
jjs func.js
Output konsolnya akan seperti ini:
2
Untuk lebih jelasnya lihat dokumentasi .

4.2. Penganalisis Ketergantungan Kelas: jdeps

jdeps adalah alat baris perintah yang sangat hebat. Ini menunjukkan dependensi tingkat paket atau kelas untuk kelas Java. Ia menerima file .class , folder atau file JAR sebagai input. Secara default , jdeps mengeluarkan dependensi ke keluaran standar (konsol). Sebagai contoh, mari kita lihat laporan ketergantungan perpustakaan Spring Framework yang populer . Untuk mempersingkat contoh, mari kita lihat dependensi untuk file JAR saja org.springframework.core-3.0.5.RELEASE.jar.
jdeps org.springframework.core-3.0.5.RELEASE.jar
Output perintah ini cukup banyak, jadi kami hanya akan menganalisis sebagian dari outputnya. Dependensi dikelompokkan berdasarkan paket. Jika tidak ada ketergantungan, tidak ditemukan akan ditampilkan .
org.springframework.core-3.0.5.RELEASE.jar -> C:\Program Files\Java\jdk1.8.0\jre\lib\rt.jar
   org.springframework.core (org.springframework.core-3.0.5.RELEASE.jar)
      -> java.io
      -> java.lang
      -> java.lang.annotation
      -> java.lang.ref
      -> java.lang.reflect
      -> java.util
      -> java.util.concurrent
      -> org.apache.commons.logging                         not found
      -> org.springframework.asm                            not found
      -> org.springframework.asm.commons                    not found
   org.springframework.core.annotation (org.springframework.core-3.0.5.RELEASE.jar)
      -> java.lang
      -> java.lang.annotation
      -> java.lang.reflect
      -> java.util
Untuk informasi lebih detail, silakan merujuk ke dokumentasi resmi .
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION