Antarmuka di Java

Antarmuka Java berbeda dengan kelas, dan penting untuk mengetahui cara menggunakan properti khususnya di program Java Anda. Tutorial ini memperkenalkan perbedaan antara kelas dan antarmuka, kemudian memandu Anda melalui contoh-contoh yang menunjukkan cara mendeklarasikan, mengimplementasikan, dan memperluas antarmuka Java.

Anda juga akan mempelajari bagaimana antarmuka telah berevolusi di Java 8, dengan penambahan metode default dan statis, dan di Java 9 dengan metode privat baru. Penambahan ini membuat antarmuka lebih berguna bagi pengembang berpengalaman. Sayangnya, mereka juga mengaburkan garis antara kelas dan antarmuka, membuat pemrograman antarmuka menjadi lebih membingungkan bagi pemula Java.

unduh Dapatkan kodenya Unduh kode sumber untuk aplikasi contoh dalam tutorial ini. Dibuat oleh Jeff Friesen untuk JavaWorld.

Apa itu antarmuka Java?

Sebuah antarmuka adalah titik di mana dua sistem bertemu dan berinteraksi. Misalnya, Anda dapat menggunakan antarmuka mesin penjual otomatis untuk memilih item, membayarnya, dan menerima item makanan atau minuman. Dari perspektif pemrograman, sebuah antarmuka berada di antara komponen perangkat lunak. Pertimbangkan bahwa header metode (nama metode, daftar parameter, dan seterusnya) berada di antara kode eksternal yang memanggil metode dan kode di dalam metode yang akan dijalankan sebagai hasil dari panggilan tersebut. Berikut contohnya:

System.out.println(average(10, 15)); double average(double x, double y) // interface between average(10, 15) call and return (x + y) / 2; { return (x + y) / 2; }

Yang sering membingungkan pemula Java adalah bahwa kelas juga memiliki antarmuka. Seperti yang saya jelaskan di Java 101: Kelas dan objek di Java, antarmuka adalah bagian dari kelas yang dapat diakses oleh kode yang terletak di luarnya. Antarmuka kelas terdiri dari beberapa kombinasi metode, bidang, konstruktor, dan entitas lainnya. Pertimbangkan Daftar 1.

Daftar 1. Kelas Akun dan antarmukanya

class Account { private String name; private long amount; Account(String name, long amount) { this.name = name; setAmount(amount); } void deposit(long amount) { this.amount += amount; } String getName() { return name; } long getAmount() { return amount; } void setAmount(long amount) { this.amount = amount; } }

The Account(String name, long amount)konstruktor dan void deposit(long amount), String getName(), long getAmount(), dan void setAmount(long amount)metode membentuk Accountantarmuka kelas ini: mereka dapat diakses oleh kode eksternal. Bidang private String name;dan private long amount;tidak dapat diakses.

Lebih lanjut tentang antarmuka Java

Apa yang dapat Anda lakukan dengan antarmuka di program Java Anda? Dapatkan gambaran umum tentang peran Jeff's Six dari antarmuka Java.

Kode metode, yang mendukung antarmuka metode, dan bagian kelas yang mendukung antarmuka kelas (seperti bidang privat) dikenal sebagai implementasi metode atau kelas . Implementasi harus disembunyikan dari kode eksternal sehingga dapat diubah untuk memenuhi persyaratan yang terus berkembang.

Ketika implementasi terekspos, saling ketergantungan antara komponen perangkat lunak dapat muncul. Misalnya, kode metode mungkin bergantung pada variabel eksternal dan pengguna kelas mungkin menjadi bergantung pada bidang yang seharusnya disembunyikan. Penggabungan ini dapat menyebabkan masalah saat implementasi harus berkembang (mungkin bidang yang terbuka harus dihapus).

Pengembang Java menggunakan fitur bahasa antarmuka untuk mengabstraksikan antarmuka kelas, sehingga memisahkan kelas dari penggunanya. Dengan berfokus pada antarmuka Java, bukan kelas, Anda bisa meminimalkan jumlah referensi ke nama kelas di kode sumber Anda. Ini memfasilitasi perubahan dari satu kelas ke kelas lain (mungkin untuk meningkatkan kinerja) saat perangkat lunak Anda matang. Berikut ini contohnya:

List names = new ArrayList() void print(List names) { // ... }

Contoh ini mendeklarasikan dan menginisialisasi namesbidang yang menyimpan daftar nama string. Contoh ini juga mendeklarasikan print()metode untuk mencetak konten daftar string, mungkin satu string per baris. Singkatnya, saya telah menghilangkan implementasi metode ini.

Listadalah antarmuka Java yang mendeskripsikan kumpulan objek berurutan. ArrayListadalah kelas yang mendeskripsikan implementasi berbasis larik dari Listantarmuka Java. Sebuah contoh baru dari ArrayListkelas diperoleh dan ditugaskan ke Listvariabel names. ( Listdan ArrayListdisimpan dalam java.utilpaket perpustakaan kelas standar .)

Tanda kurung sudut dan obat generik

Tanda kurung sudut ( <dan >) adalah bagian dari set fitur umum Java. Mereka menunjukkan bahwa namesmenjelaskan daftar string (hanya string yang dapat disimpan dalam daftar). Saya akan memperkenalkan obat generik di artikel Java 101 mendatang.

Ketika kode klien berinteraksi dengannya names, itu akan memanggil metode yang dideklarasikan oleh List, dan yang diimplementasikan oleh ArrayList. Kode klien tidak akan berinteraksi langsung dengan ArrayList. Akibatnya, kode klien tidak akan rusak ketika kelas implementasi yang berbeda, seperti LinkedList, diperlukan:

List names = new LinkedList() // ... void print(List names) { // ... }

Karena print()tipe parameter metode adalah List, implementasi metode ini tidak harus berubah. Namun, jika tipenya adalah ArrayList, tipe itu harus diubah menjadi LinkedList. Jika kedua kelas akan mendeklarasikan metode uniknya sendiri, Anda mungkin perlu mengubah print()implementasi secara signifikan .

Memisahkan Listdari ArrayListdan LinkedListmemungkinkan Anda menulis kode yang kebal terhadap perubahan implementasi kelas. Dengan menggunakan antarmuka Java, Anda dapat menghindari masalah yang mungkin timbul karena mengandalkan kelas implementasi. Pemisahan ini adalah alasan utama untuk menggunakan antarmuka Java.

Mendeklarasikan antarmuka Java

Anda mendeklarasikan antarmuka dengan mengikuti sintaks mirip kelas yang terdiri dari tajuk diikuti dengan isi. Minimal, header terdiri dari kata kunci interfacediikuti dengan nama yang mengidentifikasi antarmuka. Body dimulai dengan karakter open-brace dan diakhiri dengan close brace. Di antara pembatas ini terdapat konstanta dan deklarasi header metode:

interface identifier { // interface body }

By convention, the first letter of an interface's name is uppercased and subsequent letters are lowercased (for example, Drawable). If a name consists of multiple words, the first letter of each word is uppercased (such as DrawableAndFillable). This naming convention is known as CamelCasing.

Listing 2 declares an interface named Drawable.

Listing 2. A Java interface example

interface Drawable { int RED = 1; int GREEN = 2; int BLUE = 3; int BLACK = 4; int WHITE = 5; void draw(int color); }

Interfaces in Java's standard class library

As a naming convention, many interfaces in Java's standard class library end with the able suffix. Examples include Callable, Cloneable, Comparable, Formattable, Iterable, Runnable, Serializable, and Transferable. The suffix isn't mandatory, however; the standard class library includes the interfaces CharSequence, ClipboardOwner, Collection, Executor, Future, Iterator, List, Map and many others.

Drawable declares five fields that identify color constants. This interface also declares the header for a draw() method that must be called with one of these constants to specify the color used to draw an outline. (Using integer constants isn't a good idea because any integer value could be passed to draw(). However, they suffice in a simple example.)

Field and method header defaults

Fields that are declared in an interface are implicitly public final static. An interface's method headers are implicitly public abstract.

Drawable identifies a reference type that specifies what to do (draw something) but not how to do it. Implementation details are consigned to classes that implement this interface. Instances of such classes are known as drawables because they know how to draw themselves.

Marker and tagging interfaces

An interface with an empty body is known as a marker interface or a tagging interface. The interface exists only to associate metadata with a class. For example, Cloneable (see Inheritance in Java, Part 2) implies that instances of its implementing class can be shallowly cloned. When Object's clone() method detects (via runtime type identification) that the calling instance's class implements Cloneable, it shallowly clones the object.

Implementing Java interfaces

A class implements an interface by appending Java's implements keyword followed by a comma-separated list of interface names to the class header, and by coding each interface method in the class. Listing 3 presents a class that implements Listing 2's Drawable interface.

Listing 3. Circle implementing the Drawable interface

class Circle implements Drawable { private double x, y, radius; Circle(double x, double y, double radius) { this.x = x; this.y = y; this.radius = radius; } @Override public void draw(int color) { System.out.println("Circle drawn at (" + x + ", " + y + "), with radius " + radius + ", and color " + color); } double getRadius() { return radius; } double getX() { return x; } double getY() { return y; } }

Listing 3's Circle class describes a circle as a center point and a radius. As well as providing a constructor and suitable getter methods, Circle implements the Drawable interface by appending implements Drawable to the Circle header, and by overriding (as indicated by the @Override annotation) Drawable's draw() method header.

Listing 4 presents a second example: a Rectangle class that also implements Drawable.

Listing 4. Implementing the Drawable interface in a Rectangle context

class Rectangle implements Drawable { private double x1, y1, x2, y2; Rectangle(double x1, double y1, double x2, double y2) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; } @Override public void draw(int color) { System.out.println("Rectangle drawn with upper-left corner at (" + x1 + ", " + y1 + ") and lower-right corner at (" + x2 + ", " + y2 + "), and color " + color); } double getX1() { return x1; } double getX2() { return x2; } double getY1() { return y1; } double getY2() { return y2; } }

Listing 4's Rectangle class describes a rectangle as a pair of points denoting the upper-left and lower-right corners of this shape. As with Circle, Rectangle provides a constructor and suitable getter methods, and also implements the Drawable interface.

Overriding interface method headers

The compiler reports an error when you attempt to compile a non-abstract class that includes an implements interface clause but doesn't override all of the interface's method headers.

An interface type's data values are the objects whose classes implement the interface and whose behaviors are those specified by the interface's method headers. This fact implies that you can assign an object's reference to a variable of the interface type, provided that the object's class implements the interface. Listing 5 demonstrates.

Listing 5. Aliasing Circle and Rectangle objects as Drawables

class Draw { public static void main(String[] args) { Drawable[] drawables = new Drawable[] { new Circle(10, 20, 15), new Circle(30, 20, 10), new Rectangle(5, 8, 8, 9) }; for (int i = 0; i < drawables.length; i++) drawables[i].draw(Drawable.RED); } }

Because Circle and Rectangle implement Drawable, Circle and Rectangle objects have Drawable type in addition to their class types. Therefore, it's legal to store each object's reference in an array of Drawables. A loop iterates over this array, invoking each Drawable object's draw() method to draw a circle or a rectangle.

Assuming that Listing 2 is stored in a Drawable.java source file, which is in the same directory as the Circle.java, Rectangle.java, and Draw.java source files (which respectively store Listing 3, Listing 4, and Listing 5), compile these source files via either of the following command lines:

javac Draw.java javac *.java

Run the Draw application as follows:

java Draw

You should observe the following output:

Circle drawn at (10.0, 20.0), with radius 15.0, and color 1 Circle drawn at (30.0, 20.0), with radius 10.0, and color 1 Rectangle drawn with upper-left corner at (5.0, 8.0) and lower-right corner at (8.0, 9.0), and color 1

Note that you could also generate the same output by specifying the following main() method:

public static void main(String[] args) { Circle c = new Circle(10, 20, 15); c.draw(Drawable.RED); c = new Circle(30, 20, 10); c.draw(Drawable.RED); Rectangle r = new Rectangle(5, 8, 8, 9); r.draw(Drawable.RED); }

Seperti yang Anda lihat, membosankan untuk memanggil draw()metode setiap objek berulang kali . Selanjutnya, melakukan hal itu menambahkan bytecode ekstra ke Drawfile kelas. Dengan memikirkan Circledan Rectanglesebagai Drawables, Anda dapat memanfaatkan array dan loop sederhana untuk menyederhanakan kode. Ini adalah keuntungan tambahan dari mendesain kode untuk lebih memilih antarmuka daripada kelas.

Peringatan!