Java'daki arayüzler

Java arayüzleri sınıflardan farklıdır ve özel özelliklerini Java programlarınızda nasıl kullanacağınızı bilmek önemlidir. Bu eğitim, sınıflar ve arabirimler arasındaki farkı tanıtır, ardından Java arabirimlerinin nasıl bildirileceğini, uygulanacağını ve genişletileceğini gösteren örneklerde size yol gösterir.

Ayrıca, arayüzün varsayılan ve statik yöntemlerin eklenmesiyle Java 8'de ve yeni özel yöntemlerle Java 9'da nasıl geliştiğini de öğreneceksiniz. Bu eklemeler, arayüzleri deneyimli geliştiriciler için daha kullanışlı hale getirir. Ne yazık ki, sınıflar ve arayüzler arasındaki çizgileri de bulanıklaştırarak arayüz programlamayı Java'ya yeni başlayanlar için daha da kafa karıştırıcı hale getiriyorlar.

indir Kodu alın Bu eğitimdeki örnek uygulamalar için kaynak kodunu indirin. JavaWorld için Jeff Friesen tarafından düzenlendi.

Java arayüzü nedir?

Bir arabirim bir iki sistem karşılamak noktası ve etkileşim olduğunu. Örneğin, bir ürün seçmek, ücretini ödemek ve bir yiyecek veya içecek ürünü almak için bir otomat makinesi arayüzü kullanabilirsiniz. Programlama açısından bakıldığında, yazılım bileşenleri arasında bir arayüz bulunur. Bir yöntem üstbilgisi (yöntem adı, parametre listesi vb.) Arabiriminin, yöntemi çağıran harici kod ile çağrının bir sonucu olarak yürütülecek yöntem içindeki kod arasında oturduğunu düşünün. İşte bir örnek:

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

Java'ya yeni başlayanlar için genellikle kafa karıştıran şey, sınıfların da arayüzlere sahip olmasıdır. Java 101'de açıkladığım gibi: Java'daki sınıflar ve nesneler, arayüz, sınıfın dışında bulunan koda erişilebilen bölümüdür. Bir sınıfın arabirimi, yöntemlerin, alanların, yapıcıların ve diğer varlıkların bazı kombinasyonlarından oluşur. Liste 1'i düşünün.

Liste 1. Hesap sınıfı ve arayüzü

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)Yapıcı ve void deposit(long amount), String getName(), long getAmount(), ve void setAmount(long amount)yöntemler oluşturur Accountsınıfının arayüzü: bunlar harici koda erişilebilir. private String name;Ve private long amount;alanlar erişilemez.

Java arayüzleri hakkında daha fazla bilgi

Java programlarınızdaki arayüzlerle ne yapabilirsiniz? Jeff'in Java arayüzünün altı rolüne genel bir bakış.

Yöntemin arabirimini destekleyen bir yöntemin kodu ve sınıfın arabirimini (özel alanlar gibi) destekleyen bir sınıfın parçası, yöntemin veya sınıfın uygulaması olarak bilinir . Bir uygulama, gelişen gereksinimleri karşılayacak şekilde değiştirilebilmesi için harici koddan gizlenmelidir.

Uygulamalar açığa çıktığında, yazılım bileşenleri arasında karşılıklı bağımlılıklar ortaya çıkabilir. Örneğin, yöntem kodu harici değişkenlere bağlı olabilir ve bir sınıfın kullanıcıları, gizlenmesi gereken alanlara bağımlı hale gelebilir. Bu bağlantı , uygulamaların gelişmesi gerektiğinde sorunlara yol açabilir (belki de açık alanların kaldırılması gerekir).

Java geliştiricileri, sınıf arayüzlerini soyutlamak için arayüz dili özelliğini kullanır ve böylece sınıfları kullanıcılarından ayırır . Sınıflar yerine Java arayüzlerine odaklanarak, kaynak kodunuzdaki sınıf adlarına yapılan başvuruların sayısını en aza indirebilirsiniz. Bu, yazılımınız olgunlaştıkça bir sınıftan diğerine geçişi (belki performansı artırmak için) kolaylaştırır. İşte bir örnek:

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

Bu örnek names, dize adlarının bir listesini depolayan bir alanı bildirir ve başlatır . Örnek ayrıca print()bir dizge listesinin içeriğini yazdırmak için bir yöntem de bildirir , belki satır başına bir dize. Kısaca, yöntemin uygulanmasını atladım.

Listsıralı bir nesne koleksiyonunu tanımlayan bir Java arayüzüdür. Java arayüzünün ArrayListdizi tabanlı uygulamasını tanımlayan bir sınıftır List. ArrayListSınıfın yeni bir örneği elde edilir ve Listdeğişkene atanır names. ( Listve ArrayListstandart sınıf kitaplığının java.utilpaketinde saklanır .)

Açılı ayraçlar ve jenerikler

Açılı ayraçlar ( <ve >), Java'nın jenerik özellik kümesinin bir parçasıdır. namesBir dizi listesini açıkladığını belirtirler (listede yalnızca dizeler saklanabilir). Gelecekteki bir Java 101 makalesinde jenerikleri tanıtacağım.

İstemci kodu ile etkileşime girdiğinde names, tarafından bildirilen Listve tarafından uygulanan yöntemleri çağırır ArrayList. Müşteri kodu ile doğrudan etkileşime girmeyecektir ArrayList. Sonuç olarak LinkedList, aşağıdaki gibi farklı bir uygulama sınıfı gerektiğinde istemci kodu bozulmayacaktır :

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

Çünkü print()yöntem parametresi türüdür List, bu yöntemin uygulanması değişikliğine sahip değil. Ancak, tür olsaydı ArrayList, türün değiştirilmesi gerekirdi LinkedList. Her iki sınıf da kendi benzersiz yöntemlerini bildireceklerse, print()uygulamasının önemli ölçüde değiştirilmesi gerekebilir .

Ayırma Listgelen ArrayListve LinkedListsınıf-uygulama değişikliklerine karşı bağışıklık var kod yazmak sağlar. Java arayüzlerini kullanarak, uygulama sınıflarına güvenmekten doğabilecek sorunları önleyebilirsiniz. Bu ayrıştırma, Java arayüzlerini kullanmanın ana nedenidir.

Java arayüzlerini bildirme

Bir başlık ve ardından bir gövdeden oluşan sınıf benzeri bir sözdizimine bağlı kalarak bir arayüz bildirirsiniz. Başlık, en azından bir anahtar sözcük ve interfaceardından arabirimi tanımlayan bir addan oluşur . Gövde açık küme ayracı karakteriyle başlar ve yakın küme ayracı ile biter. Bu sınırlayıcılar arasında sabit ve yöntem başlığı bildirimleri bulunur:

interface identifier { // interface body }

Kural olarak, bir arabirim adının ilk harfi büyük ve sonraki harfler küçük harfle yazılır (örneğin, Drawable). Bir isim birden çok kelimeden oluşuyorsa, her kelimenin ilk harfi büyük harfle yazılır (örneğin DrawableAndFillable). Bu adlandırma kuralı CamelCasing olarak bilinir.

Liste 2, adlı bir arayüz bildirir Drawable.

Liste 2. Bir Java arayüzü örneği

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

Java'nın standart sınıf kitaplığındaki arabirimler

Bir adlandırma kuralı olarak, Java'nın standart sınıf kitaplığındaki birçok arabirim, mümkün sonekiyle sona erer . Örnekler arasında Callable, Cloneable, Comparable, Formattable, Iterable, Runnable, Serializable, ve Transferable. Ancak sonek zorunlu değildir; standart sınıf kütüphanesi arayüzleri içerir CharSequence, ClipboardOwner, Collection, Executor, Future, Iterator, List, Mapve diğerleri.

Drawablerenk sabitlerini tanımlayan beş alan bildirir. Bu arabirim ayrıca, draw()bir anahat çizmek için kullanılan rengi belirtmek için bu sabitlerden biriyle çağrılması gereken bir yöntemin başlığını da bildirir . (Tamsayı sabitlerini kullanmak iyi bir fikir değildir çünkü herhangi bir tamsayı değeri aktarılabilir draw(). Ancak, basit bir örnekte bunlar yeterlidir.)

Alan ve yöntem üstbilgisi varsayılanları

Bir arayüzde bildirilen alanlar örtülüdür public final static. Bir arabirimin yöntem başlıkları örtüktür public abstract.

Drawablene yapılacağını (bir şey çizileceğini) ancak nasıl yapılacağını belirtmeyen bir referans türünü tanımlar. Uygulama ayrıntıları, bu arayüzü uygulayan sınıflara gönderilir. Bu tür sınıfların örnekleri, kendilerini nasıl çizeceklerini bildikleri için çekilebilir öğeler olarak bilinir.

İşaretçi ve etiketleme arayüzleri

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

Gördüğünüz gibi, her nesnenin draw()yöntemini tekrar tekrar çağırmak sıkıcı . Ayrıca, böyle yapmak, Drawsınıf dosyasına fazladan bayt kodu ekler . Düşünerek Circleve Rectangleyanı Drawables, bir dizi ve kodu basitleştirmek için basit döngü yararlanabilir. Bu, sınıflar yerine arabirimleri tercih etmek için kod tasarlamanın ek bir faydasıdır.

Dikkat!