Enam peran antarmuka

Pendatang baru bahasa Java sering mengalami kebingungan. Kebingungan mereka sebagian besar disebabkan oleh palet fitur bahasa eksotis Java, seperti generik dan lambda. Namun, fitur yang lebih sederhana seperti antarmuka dapat membingungkan.

Baru-baru ini, saya mendapat pertanyaan tentang mengapa Java mendukung antarmuka (via interfacedan implementskata kunci). Ketika saya mulai belajar Java pada 1990-an, pertanyaan ini sering dijawab dengan menyatakan bahwa antarmuka mengatasi kurangnya dukungan Java untuk beberapa implementasi pewarisan (kelas anak yang mewarisi dari beberapa kelas induk). Namun, antarmuka berfungsi lebih dari sekadar kludge. Dalam posting ini, saya menyajikan enam peran yang dimainkan antarmuka dalam bahasa Java.

Tentang multiple inheritance

Istilah multiple inheritance umumnya digunakan untuk merujuk ke kelas anak yang mewarisi dari beberapa kelas induk. Di Jawa, istilah warisan implementasi berganda berarti hal yang sama. Java juga mendukung pewarisan beberapa antarmuka di mana antarmuka anak dapat mewarisi dari beberapa antarmuka induk. Untuk mempelajari lebih lanjut tentang beberapa warisan (termasuk masalah berlian terkenal), lihat entri Warisan berganda Wikipedia.

Peran 1: Mendeklarasikan jenis anotasi

Kata interfacekunci kelebihan beban untuk digunakan dalam mendeklarasikan jenis anotasi. Misalnya, Listing 1 menyajikan Stubjenis anotasi sederhana .

Daftar 1. Stub.java

import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface Stub { int id(); // A semicolon terminates an element declaration. String dueDate(); String developer() default "unassigned"; }

Stubmenjelaskan kategori anotasi (contoh jenis anotasi) yang menunjukkan jenis dan metode yang belum selesai. Deklarasinya dimulai dengan header yang terdiri dari @diikuti oleh interfacekata kunci, diikuti dengan namanya.

Jenis anotasi ini mendeklarasikan tiga elemen , yang dapat Anda anggap sebagai header metode:

  • id() mengembalikan pengenal berbasis integer untuk stub
  • dueDate() mengidentifikasi tanggal di mana rintisan harus diisi dengan kode
  • developer() mengidentifikasi pengembang yang bertanggung jawab untuk mengisi stub

Sebuah elemen mengembalikan nilai apa pun yang ditetapkan padanya dengan anotasi. Jika elemen tidak ditentukan, nilai default-nya (mengikuti defaultkata kunci dalam deklarasi) dikembalikan.

Kode 2 menunjukkan Stubdalam konteks ContactMgrkelas yang belum selesai ; kelas dan metode soliternya telah diberi @Stubanotasi.

Daftar 2. ContactMgr.java

@Stub ( id = 1, dueDate = "12/31/2016" ) public class ContactMgr { @Stub ( id = 2, dueDate = "06/31/2016", developer = "Marty" ) public void addContact(String contactID) { } }

Contoh jenis anotasi dimulai dengan @, yang diikuti dengan nama jenis anotasi. Di sini, @Stubanotasi pertama mengidentifikasikan dirinya sebagai nomor 1 dengan batas waktu 31 Desember 2016. Pengembang yang bertanggung jawab untuk mengisi stub belum ditugaskan. Sebaliknya, @Stubanotasi kedua mengidentifikasikan dirinya sebagai nomor 2 dengan tenggat waktu 31 Juni 2016. Pengembang yang bertanggung jawab untuk mengisi stub diidentifikasi sebagai Marty.

Anotasi harus diproses agar bisa digunakan. ( Stubdiberi anotasi @Retention(RetentionPolicy.RUNTIME)sehingga dapat diproses.) Kode 3 menyajikan StubFinderaplikasi yang melaporkan @Stubpenjelasan kelas .

Daftar 3. StubFinder.java

import java.lang.reflect.Method; public class StubFinder { public static void main(String[] args) throws Exception { if (args.length != 1) { System.err.println("usage: java StubFinder classfile"); return; } Class clazz = Class.forName(args[0]); if (clazz.isAnnotationPresent(Stub.class)) { Stub stub = clazz.getAnnotation(Stub.class); System.out.println("Stub ID = " + stub.id()); System.out.println("Stub Date = " + stub.dueDate()); System.out.println("Stub Developer = " + stub.developer()); System.out.println(); } Method[] methods = clazz.getMethods(); for (int i = 0; i < methods.length; i++) if (methods[i].isAnnotationPresent(Stub.class)) { Stub stub = methods[i].getAnnotation(Stub.class); System.out.println("Stub ID = " + stub.id()); System.out.println("Stub Date = " + stub.dueDate()); System.out.println("Stub Developer = " + stub.developer()); System.out.println(); } } }

main()Metode Listing 3 menggunakan API Refleksi Java untuk mengambil semua @Stubpenjelasan yang mengawali deklarasi kelas serta deklarasi metodenya.

Susun Daftar 1 sampai 3, sebagai berikut:

javac *.java

Jalankan aplikasi yang dihasilkan, sebagai berikut:

java StubFinder ContactMgr

Anda harus mengamati keluaran berikut:

Stub ID = 1 Stub Date = 12/31/2016 Stub Developer = unassigned Stub ID = 2 Stub Date = 06/31/2016 Stub Developer = Marty

Anda mungkin berpendapat bahwa jenis anotasi dan anotasinya tidak ada hubungannya dengan antarmuka. Bagaimanapun, deklarasi kelas dan implementskata kunci tidak ada. Namun, saya tidak setuju dengan kesimpulan ini.

@interfacemirip dengan classyang memperkenalkan tipe. Elemen-elemennya adalah metode yang diimplementasikan (di belakang layar) untuk mengembalikan nilai. Elemen dengan defaultnilai mengembalikan nilai meskipun tidak ada dalam anotasi, yang mirip dengan objek. Elemen non-default harus selalu ada dalam anotasi dan harus dideklarasikan untuk mengembalikan nilai. Oleh karena itu, seolah-olah sebuah kelas telah dideklarasikan dan bahwa kelas tersebut mengimplementasikan metode antarmuka.

Peran 2: Menjelaskan kapabilitas implementasi-independen

Kelas yang berbeda mungkin menawarkan kemampuan yang sama. Sebagai contoh, java.nio.CharBuffer, javax.swing.text.Segment, java.lang.String, java.lang.StringBuffer, dan java.lang.StringBuilderkelas menyediakan akses ke urutan dibaca dari charnilai-nilai.

Ketika kelas menawarkan kemampuan umum, antarmuka untuk kemampuan ini dapat diekstraksi untuk digunakan kembali. Misalnya, antarmuka ke kemampuan "urutan charnilai yang dapat dibaca " telah diekstraksi ke java.lang.CharSequenceantarmuka. CharSequencemenyediakan akses yang seragam dan hanya-baca ke berbagai jenis charurutan.

Misalkan Anda diminta untuk menulis sebuah aplikasi kecil yang jumlah jumlah kejadian dari masing-masing jenis huruf kecil di CharBuffer, String, dan StringBufferbenda-benda. Setelah beberapa pemikiran, Anda mungkin akan menemukan Listing 4. (Saya biasanya menghindari ekspresi bias budaya seperti ch - 'a', tapi saya ingin membuat contohnya tetap sederhana.)

Daftar 4. Freq.java(versi 1)

import java.nio.CharBuffer; public class Freq { public static void main(String[] args) { if (args.length != 1) { System.err.println("usage: java Freq text"); return; } analyzeS(args[0]); analyzeSB(new StringBuffer(args[0])); analyzeCB(CharBuffer.wrap(args[0])); } static void analyzeCB(CharBuffer cb) { int counts[] = new int[26]; while (cb.hasRemaining()) { char ch = cb.get(); if (ch >= 'a' && ch <= 'z') counts[ch - 'a']++; } for (int i = 0; i < counts.length; i++) System.out.printf("Count of %c is %d%n", (i + 'a'), counts[i]); System.out.println(); } static void analyzeS(String s) { int counts[] = new int[26]; for (int i = 0; i = 'a' && ch <= 'z') counts[ch - 'a']++; } for (int i = 0; i < counts.length; i++) System.out.printf("Count of %c is %d%n", (i + 'a'), counts[i]); System.out.println(); } static void analyzeSB(StringBuffer sb) { int counts[] = new int[26]; for (int i = 0; i = 'a' && ch <= 'z') counts[ch - 'a']++; } for (int i = 0; i < counts.length; i++) System.out.printf("Count of %c is %d%n", (i + 'a'), counts[i]); System.out.println(); } }

Daftar 4 menyajikan tiga analyzemetode berbeda untuk mencatat jumlah kejadian huruf kecil dan mengeluarkan statistik ini. Meskipun varian Stringdan StringBufferpraktis identik (dan Anda mungkin tergoda untuk membuat satu metode untuk keduanya), CharBuffervariannya jauh lebih berbeda secara signifikan.

Kode 4 mengungkapkan banyak kode duplikat, yang mengarah ke file kelas yang lebih besar dari yang diperlukan. Anda dapat mencapai tujuan statistik yang sama dengan menggunakan CharSequenceantarmuka. Kode 5 menyajikan versi alternatif dari aplikasi frekuensi yang didasarkan pada CharSequence.

Daftar 5. Freq.java(versi 2)

import java.nio.CharBuffer; public class Freq { public static void main(String[] args) { if (args.length != 1) { System.err.println("usage: java Freq text"); return; } analyze(args[0]); analyze(new StringBuffer(args[0])); analyze(CharBuffer.wrap(args[0])); } static void analyze(CharSequence cs) { int counts[] = new int[26]; for (int i = 0; i = 'a' && ch <= 'z') counts[ch - 'a']++; } for (int i = 0; i < counts.length; i++) System.out.printf("Count of %c is %d%n", (i + 'a'), counts[i]); System.out.println(); } }

Kode 5 mengungkapkan aplikasi yang jauh lebih sederhana, yang karena kodifikasi analyze()untuk menerima CharSequenceargumen. Karena masing-masing String, StringBufferdan CharBufferalat CharSequence, itu hukum untuk lulus contoh dari jenis ini untuk analyze().

Contoh lain

Ekspresi CharBuffer.wrap(args[0])adalah contoh lain untuk meneruskan Stringobjek ke parameter tipe CharSequence.

Singkatnya, peran kedua dari sebuah antarmuka adalah untuk menggambarkan kemampuan implementasi-independen. Dengan coding untuk sebuah antarmuka (seperti CharSequence) bukan ke kelas (seperti String, StringBuffer, atau CharBuffer), Anda menghindari duplikasi kode dan menghasilkan classfiles lebih kecil. Dalam hal ini, saya mencapai pengurangan lebih dari 50%.

Peran 3: Memfasilitasi evolusi perpustakaan

Java 8 memperkenalkan kami pada fitur bahasa lambda yang sangat berguna dan Streams API (dengan fokus pada komputasi apa yang harus dilakukan, bukan pada bagaimana cara melakukannya). Lambdas dan Streams mempermudah pengembang untuk memperkenalkan paralelisme ke dalam aplikasi mereka. Sayangnya, Java Collections Framework tidak dapat memanfaatkan kemampuan ini tanpa perlu penulisan ulang yang ekstensif.

Untuk meningkatkan koleksi dengan cepat untuk digunakan sebagai sumber dan tujuan streaming, dukungan untuk metode default (juga dikenal sebagai metode ekstensi ), yang merupakan metode non-statis yang headernya diawali dengan defaultkata kunci dan yang menyediakan badan kode, telah ditambahkan ke fitur antarmuka Java. Metode default milik antarmuka; mereka tidak diimplementasikan (tapi bisa diganti) oleh kelas yang mengimplementasikan antarmuka. Juga, mereka bisa dipanggil melalui referensi objek.

Setelah metode default menjadi bagian dari bahasa, metode berikut ditambahkan ke java.util.Collectionantarmuka, untuk menjembatani antara koleksi dan aliran:

  • default Stream parallelStream(): Kembalikan java.util.stream.Streamobjek paralel (mungkin) dengan koleksi ini sebagai sumbernya.
  • default Stream stream(): Kembalikan Streamobjek berurutan dengan koleksi ini sebagai sumbernya.

Misalkan Anda telah mendeklarasikan java.util.Listvariabel dan ekspresi tugas berikut :

List innerPlanets = Arrays.asList("Mercury", "Venus", "Earth", "Mars");

Anda secara tradisional akan mengulang koleksi ini, sebagai berikut:

for (String innerPlanet: innerPlanets) System.out.println(innerPlanet);

Anda dapat mengganti iterasi eksternal ini, yang berfokus pada cara melakukan komputasi, dengan iterasi internal berbasis Streams, yang berfokus pada komputasi apa yang harus dilakukan, sebagai berikut:

innerPlanets.stream().forEach(System.out::println); innerPlanets.parallelStream().forEach(System.out::println);

Di sini, innerPlanets.stream()dan innerPlanets.parallelStream()kembalikan aliran sekuensial dan paralel ke Listsumber yang dibuat sebelumnya . Rangkaian ke Streamreferensi yang dikembalikan adalah forEach(System.out::println), yang melakukan iterasi pada objek aliran dan memanggil System.out.println()(diidentifikasi oleh System.out::printlnreferensi metode) untuk setiap objek untuk mengeluarkan representasi stringnya ke aliran keluaran standar.

Metode default dapat membuat kode lebih mudah dibaca. Misalnya, java.util.Collectionskelas mendeklarasikan void sort(List list, Comparator c)metode statis untuk menyortir konten daftar yang tunduk pada pembanding yang ditentukan. Java 8 menambahkan default void sort(Comparator c)metode ke Listantarmuka sehingga Anda dapat menulis lebih mudah dibaca myList.sort(comparator);daripada Collections.sort(myList, comparator);.

Peran metode default yang ditawarkan oleh antarmuka telah memberikan kehidupan baru pada Java Collections Framework. Anda dapat mempertimbangkan peran ini untuk perpustakaan berbasis antarmuka lama Anda sendiri.