Sučelja u Javi

Java sučelja razlikuju se od klasa i važno je znati koristiti njihova posebna svojstva u svojim Java programima. Ovaj vodič uvodi razliku između klasa i sučelja, a zatim vas vodi kroz primjere koji pokazuju kako deklarirati, implementirati i proširiti Java sučelja.

Također ćete naučiti kako se sučelje razvilo u Javi 8, uz dodavanje zadanih i statičkih metoda, te u Javi 9 s novim privatnim metodama. Ovi dodaci čine sučelja korisnijim za iskusne programere. Nažalost, oni također brišu linije između klasa i sučelja, čineći programiranje sučelja još zbunjujućim za početnike u Javi.

preuzimanje Preuzmite kod Preuzmite izvorni kod za primjere aplikacija u ovom vodiču. Stvorio Jeff Friesen za JavaWorld.

Što je Java sučelje?

Sučelje je točka gdje se dva sustava u susret i interakciju. Na primjer, možete koristiti sučelje automata za odabir predmeta, plaćanje i primanje predmeta s hranom ili pićem. Iz programske perspektive, sučelje se nalazi između softverskih komponenti. Uzmite u obzir da se zaglavlje metode (naziv metode, popis parametara i tako dalje) nalazi između vanjskog koda koji poziva metodu i koda unutar metode koja će se izvršiti kao rezultat poziva. Evo primjera:

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; }

Ono što početnike Java zbunjuje jest da klase također imaju sučelja. Kao što sam objasnio u Javi 101: Klase i objekti u Javi, sučelje je dio klase koji je dostupan kodu koji se nalazi izvan njega. Sučelje klase sastoji se od neke kombinacije metoda, polja, konstruktora i drugih entiteta. Razmislite o popisu 1.

Popis 1. Klasa Account i njezino sučelje

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; } }

Account(String name, long amount)Konstruktor i void deposit(long amount), String getName(), long getAmount(), i void setAmount(long amount)metode čine u Accountklasi sučelje za: oni su dostupni na vanjske koda. private String name;I private long amount;polja su nedostupni.

Više o Java sučeljima

Što možete učiniti sa sučeljima u svojim Java programima? Pogledajte pregled Jeffovih šest uloga Java sučelja.

Kôd metode koji podržava sučelje metode i onaj dio klase koji podržava sučelje klase (poput privatnih polja) poznat je kao implementacija metode ili klase . Implementacija treba biti skrivena od vanjskog koda kako bi se mogla mijenjati u skladu s novim zahtjevima.

Kada su implementacije izložene, mogu nastati međuovisnosti između softverskih komponenata. Na primjer, kod metode može se oslanjati na vanjske varijable, a korisnici klase mogu postati ovisni o poljima koja su trebala biti skrivena. Ova sprega može dovesti do problema kada se implementacije moraju razvijati (možda izložena polja moraju biti uklonjena).

Java programeri koriste jezik sučelja značajku za apstraktne klase sučelja, tako odvajajući nastavu iz svojih korisnika. Fokusiranjem na Java sučelja umjesto na klase, možete smanjiti broj referenci na nazive klasa u izvornom kodu. To olakšava prelazak iz jedne klase u drugu (možda radi poboljšanja performansi) kako vaš softver sazrijeva. Evo primjera:

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

Ovaj primjer deklarira i inicijalizira namespolje koje pohranjuje popis imena nizova. Primjer također deklarira print()metodu za ispis sadržaja popisa nizova, možda jedan niz u retku. Radi kratkoće izostavio sam provedbu metode.

Listje Java sučelje koje opisuje sekvencijalnu zbirku objekata. ArrayListje klasa koja opisuje implementaciju ListJava sučelja temeljenu na nizu . ArrayListDobiva se nova instanca klase i dodjeljuje Listvarijabli names. ( Listi ArrayListpohranjeni su u java.utilpaketu standardne knjižnice razreda .)

Kutne zagrade i generički lijekovi

Kutne zagrade ( <i >) dio su Java-ovog generičkog skupa značajki. Označavaju da namesopisuje popis nizova (na popisu se mogu pohraniti samo nizovi). Upoznat ću generike u budućem članku o Javi 101.

Kada klijentski kod stupi u interakciju names, on će pozvati one metode koje su deklarirane Listi koje implementira ArrayList. Klijentski kod neće izravno komunicirati ArrayList. Kao rezultat toga, klijentski kôd se neće slomiti kada je potrebna druga klasa implementacije, kao što LinkedListje:

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

Budući da je print()tip parametra Listmetode, implementacija ove metode ne mora se mijenjati. Međutim, da je vrsta bila ArrayList, tip bi se morao promijeniti u LinkedList. Ako bi obje klase trebale deklarirati vlastite jedinstvene metode, možda ćete trebati značajno promijeniti print()implementaciju.

Razdvajanje Listod ArrayListte LinkedListomogućuje pisanje koda koji je imun na klasi implementacije promjena. Korištenjem Java sučelja možete izbjeći probleme koji bi mogli nastati oslanjanjem na klase implementacije. Ovo razdvajanje glavni je razlog korištenja Java sučelja.

Izjava Java sučelja

Sučelje deklarirate pridržavajući se sintakse nalik klasi koja se sastoji od zaglavlja i tijela. Zaglavlje se najmanje sastoji od ključne riječi interfacenakon koje slijedi ime koje identificira sučelje. Tijelo započinje likom otvorene zagrade, a završava bliskom zagradom. Između ovih graničnika nalaze se konstante i deklaracije zaglavlja metode:

interface identifier { // interface body }

Prema dogovoru, prvo slovo imena sučelja veliko je, a sljedeća mala slova (na primjer, Drawable). Ako se ime sastoji od više riječi, veliko slovo svake riječi je veliko (npr DrawableAndFillable.). Ova konvencija imenovanja poznata je pod nazivom CamelCasing.

Popis 2 deklarira imenovano sučelje Drawable.

Popis 2. Primjer Java sučelja

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

Sučelja u Javinoj knjižnici standardnih klasa

Kao konvencija imenovanja, mnoga sučelja u Javinoj knjižnici standardnih klasa završavaju se sposobnim sufiksom. Primjeri uključuju Callable, Cloneable, Comparable, Formattable, Iterable, Runnable, Serializable, i Transferable. Sufiks, međutim, nije obvezan; standardni klase knjižnica uključuje sučelja CharSequence, ClipboardOwner, Collection, Executor, Future, Iterator, List, Mapi mnogi drugi.

Drawabledeklarira pet polja koja identificiraju konstante boje. Ovo sučelje također deklarira zaglavlje draw()metode koja se mora pozvati s jednom od ovih konstanti da bi se odredila boja koja se koristi za crtanje obrisa. (Upotreba cjelobrojnih konstanti nije dobra ideja jer bi se mogla prenijeti bilo koja cjelobrojna vrijednost draw(). Međutim, oni su dovoljni u jednostavnom primjeru.)

Zadane vrijednosti zaglavlja polja i metode

Polja koja su deklarirana u sučelju su implicitno public final static. Zaglavlja metoda sučelja su implicitno public abstract.

Drawableidentificira referentni tip koji određuje što učiniti (nacrtati nešto), ali ne i kako to učiniti. Pojedinosti o implementaciji dostavljaju se razredima koji implementiraju ovo sučelje. Primjeri takvih klasa poznati su kao crtači jer znaju kako se crtati.

Označavanje i označavanje sučelja

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); }

Kao što vidite, dosadno je ponavljati pozivanje draw()metode svakog objekta . Nadalje, time dodajete dodatni bytecode u Drawdatoteku klase. Razmišljajući o Circlei Rectanglekao Drawables, možete iskoristiti niz i jednostavnu petlju za pojednostavljivanje koda. To je dodatna prednost dizajniranja koda za preferiranje sučelja od klasa.

Oprez!