Antara muka di Java

Antaramuka Java berbeza dari kelas, dan penting untuk mengetahui bagaimana menggunakan sifat khas mereka dalam program Java anda. Tutorial ini memperkenalkan perbezaan antara kelas dan antara muka, kemudian membimbing anda melalui contoh yang menunjukkan cara menyatakan, melaksanakan, dan memperluas antara muka Java.

Anda juga akan mengetahui bagaimana antarmuka berkembang di Java 8, dengan penambahan kaedah lalai dan statik, dan di Java 9 dengan kaedah persendirian yang baru. Penambahan ini menjadikan antara muka lebih berguna untuk pembangun yang berpengalaman. Sayangnya, mereka juga mengaburkan garis antara kelas dan antara muka, menjadikan pengaturcaraan antara muka menjadi lebih membingungkan bagi pemula Java.

muat turun Dapatkan kod Muat turun kod sumber misalnya aplikasi dalam tutorial ini. Dicipta oleh Jeff Friesen untuk JavaWorld.

Apakah antara muka Java?

Satu antara muka satu titik di mana dua sistem bertemu dan berinteraksi. Sebagai contoh, anda mungkin menggunakan antara muka mesin layan diri untuk memilih item, membayarnya, dan menerima barang makanan atau minuman. Dari perspektif pengaturcaraan, antara muka terletak di antara komponen perisian. Pertimbangkan bahawa antara muka kaedah (nama kaedah, senarai parameter, dan sebagainya) berada di antara kod luaran yang memanggil kaedah dan kod dalam kaedah yang akan dijalankan sebagai hasil daripada panggilan tersebut. Inilah 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 ialah kelas juga mempunyai antara muka. Seperti yang saya jelaskan di Java 101: Kelas dan objek di Java, antara muka adalah bahagian kelas yang dapat diakses oleh kod yang terletak di luarnya. Antaramuka kelas terdiri daripada beberapa gabungan kaedah, bidang, pembina, dan entiti lain. Pertimbangkan Penyenaraian 1.

Penyenaraian 1. Kelas Akaun dan antara muka

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)pembina dan void deposit(long amount), String getName(), long getAmount(), dan void setAmount(long amount)kaedah membentuk Accountantara muka kelas: mereka boleh diakses kepada kod luar. The private String name;dan private long amount;ladang-ladang yang tidak boleh diakses.

Lebih banyak mengenai antara muka Java

Apa yang dapat anda lakukan dengan antaramuka dalam program Java anda? Dapatkan gambaran keseluruhan dengan Jeff's Six peranan antara muka Java.

Kod A method, yang menyokong antara muka method, dan bahawa sebahagian daripada kelas yang menyokong antara muka kelas (seperti bidang swasta) dikenali sebagai method atau kelas ini pelaksanaan . Pelaksanaan harus disembunyikan dari kod luaran sehingga dapat diubah untuk memenuhi keperluan yang terus berkembang.

Apabila implementasi didedahkan, saling ketergantungan antara komponen perisian dapat timbul. Sebagai contoh, kod kaedah mungkin bergantung pada pemboleh ubah luaran dan pengguna kelas mungkin bergantung pada bidang yang semestinya disembunyikan. Ini gandingan boleh membawa kepada masalah apabila pelaksanaan mesti berubah (medan mungkin terdedah mesti dikeluarkan).

Pembangun Java menggunakan ciri bahasa antara muka untuk abstrak antara muka kelas, sehingga memutuskan kelas dari pengguna mereka. Dengan memfokuskan pada antara muka Java dan bukannya kelas, anda dapat meminimumkan jumlah rujukan nama kelas dalam kod sumber anda. Ini memudahkan perubahan dari satu kelas ke kelas yang lain (mungkin untuk meningkatkan prestasi) apabila perisian anda semakin matang. Berikut adalah contoh:

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

Contoh ini menyatakan dan menginisialisasi namesmedan yang menyimpan senarai nama rentetan. Contohnya juga menyatakan print()kaedah untuk mencetak kandungan senarai rentetan, mungkin satu rentetan setiap baris. Untuk kesimpulan, saya telah menghilangkan pelaksanaan kaedah ini.

Listadalah antara muka Java yang menerangkan koleksi objek yang berurutan. ArrayListadalah kelas yang menggambarkan pelaksanaan berasaskan array Listantara muka Java. Contoh baru ArrayListkelas diperoleh dan diberikan kepada Listpemboleh ubah names. ( Listdan ArrayListdisimpan dalam java.utilpakej perpustakaan kelas standard .)

Kurungan sudut dan generik

Kurungan sudut ( <dan >) adalah sebahagian daripada set ciri generik Java. Mereka menunjukkan bahawa namesmenerangkan senarai rentetan (hanya rentetan yang dapat disimpan dalam senarai). Saya akan memperkenalkan generik dalam artikel Java 101 yang akan datang.

Apabila kod pelanggan berinteraksi names, ia akan menggunakan kaedah yang dinyatakan oleh List, dan yang dilaksanakan oleh ArrayList. Kod pelanggan tidak akan berinteraksi secara langsung dengan ArrayList. Akibatnya, kod pelanggan tidak akan hancur apabila kelas pelaksanaan yang berbeza, seperti LinkedList, diperlukan:

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

Kerana print()jenis parameter metode adalah List, pelaksanaan metode ini tidak harus berubah. Namun, jika jenisnya ArrayList, jenisnya harus diubah menjadi LinkedList. Sekiranya kedua-dua kelas menyatakan kaedah unik mereka sendiri, anda mungkin perlu mengubah print()pelaksanaannya dengan ketara .

Memisahkan Listdari ArrayListdan LinkedListmembolehkan anda menulis kod yang kebal terhadap perubahan pelaksanaan kelas. Dengan menggunakan antaramuka Java, Anda dapat menghindari masalah yang mungkin timbul dari bergantung pada kelas pelaksanaan. Pemutusan ini adalah sebab utama untuk menggunakan antara muka Java.

Menyatakan antara muka Java

Anda menyatakan antara muka dengan mematuhi sintaks seperti kelas yang terdiri daripada tajuk diikuti oleh badan. Paling minimum, tajuk terdiri daripada kata kunci interfacediikuti dengan nama yang mengenal pasti antara muka. Badan dimulakan dengan watak pendakap terbuka dan diakhiri dengan pendakap rapat. Antara pembatas ini adalah pernyataan tetap dan kaedah:

interface identifier { // interface body }

Secara konvensional, huruf pertama nama antara muka adalah huruf besar dan huruf berikutnya huruf kecil (contohnya Drawable). Sekiranya nama terdiri daripada beberapa perkataan, huruf pertama setiap perkataan adalah huruf besar (seperti DrawableAndFillable). Konvensyen penamaan ini dikenali sebagai CamelCasing.

Penyenaraian 2 menyatakan antara muka dinamakan Drawable.

Penyenaraian 2. Contoh antara muka Java

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

Antara muka di perpustakaan kelas standard Java

Sebagai konvensyen penamaan, banyak antara muka di perpustakaan kelas standard Java diakhiri dengan akhiran yang mampu . Contohnya termasuk Callable, Cloneable, Comparable, Formattable, Iterable, Runnable, Serializable, dan Transferable. Akhiran tidak wajib, bagaimanapun; perpustakaan kelas standard termasuk antara muka CharSequence, ClipboardOwner, Collection, Executor, Future, Iterator, List, Mapdan lain-lain lagi.

Drawablemenyatakan lima medan yang mengenal pasti pemalar warna. Antaramuka ini juga menyatakan tajuk untuk draw()kaedah yang mesti dipanggil dengan salah satu pemalar ini untuk menentukan warna yang digunakan untuk melukis garis besar. (Menggunakan pemalar bilangan bulat bukanlah idea yang baik kerana nilai integer dapat diteruskan draw(). Namun, itu cukup dalam contoh sederhana.)

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 berulang kali menggunakan draw()kaedah setiap objek . Selanjutnya, berbuat demikian akan menambahkan kod bytek tambahan ke Drawfail kelas. Dengan memikirkan Circledan Rectanglesebagai Drawables, anda dapat memanfaatkan susunan dan gelung ringkas untuk mempermudah kodnya. Ini adalah faedah tambahan dari merancang kod untuk memilih antara muka daripada kelas.

Awas!