Bir arayüzde doğru yöntemlere sahip olmaktan daha fazlası var mı


159

Diyelim ki bu arayüze sahibim:

public interface IBox
{
   public void setSize(int size);
   public int getSize();
   public int getArea();
  //...and so on
}

Ve onu uygulayan bir sınıfım var:

public class Rectangle implements IBox
{
   private int size;
   //Methods here
}

IBox arayüzünü kullanmak istedim, aslında bunun bir örneğini oluşturamıyorum:

public static void main(String args[])
{
    Ibox myBox=new Ibox();
}

sağ? Aslında bunu yapmak zorundayım:

public static void main(String args[])
{
    Rectangle myBox=new Rectangle();
}

Bu doğruysa, arabirimlerin tek amacı, bir arabirimi uygulayan sınıfın bir arabirim tarafından açıklandığı gibi içinde doğru yöntemlere sahip olduğundan emin olmaktır? Yoksa arayüzlerin başka bir kullanımı var mı?


2
Unutmayın, arayüzler Java'ya özgü değildir. Tüm OOP dilleri, her zaman açıkça Java olarak tanımlanmamış olsa da, bir şekilde veya başka bir şekilde bulunur.
Herms

2
Teknik olarak, kuvvetle yazılmış tüm OOP dilleri bir şekilde veya başka bir dilde vardır. Yazılmamış veya yazılan diller benzer bir kavrama sahip değildir.
Jared

1
@Jared Güçlü yazmayı statik yazmayla ve "yazılmamış" ifadesini dinamik yazmayla karıştırmıyor musunuz?
eljenso

Polimorfizm arayüzler vasıtasıyla da gerçekleştirilebilir. Bu sayfanın son bölümünü kontrol edin codenuggets.com/2014/06/20/java-interface
Jeff

Yanıtlar:


143

Arayüzler kodunuzu daha esnek hale getirmenin bir yoludur. Yaptığınız şey şudur:

Ibox myBox=new Rectangle();

Daha sonra, farklı bir kutu türü kullanmaya karar verirseniz (belki daha iyi bir kutuya sahip başka bir kütüphane vardır), kodunuzu şu şekilde değiştirirsiniz:

Ibox myBox=new OtherKindOfBox();

Bir kere alıştıktan sonra, bunun harika (aslında gerekli) bir yol olduğunu göreceksiniz.

Başka bir neden, örneğin, bir kutu listesi oluşturmak ve her biri üzerinde işlem yapmak istiyorsanız, ancak listenin farklı türde kutular içermesini istiyorsanız. Her kutuda şunları yapabilirsiniz:

myBox.close()

myBox'ın gerçek sınıfı yinelemede olduğunuz kutuya bağlı olarak değişse bile (IBox'ın bir close () yöntemi olduğunu varsayarsak).


54
Bu yanıtta Java arayüzlerine özgü hiçbir şey yoktur . Aynı şey soyut sınıflar ve hatta somut sınıflar için de geçerlidir. Birden fazla arayüz uygulama yeteneğinden ve bunun ne zaman / neden faydalı olabileceğinden bahsetmek için iyi bir cevap bekliyorum .
Rogério

16
Bu cevap nasıl seçildi? Bu, polimorfizmin neden faydalı olduğuna dair kısa bir açıklamadır, ancak yukarıdaki afişin de belirttiği gibi, çoklu arayüzlerin daha iyi bir açıklamasını ve daha da önemlisi, soyut bir sınıfa karşı bir arayüz kullanmanın uygun olduğu durumlarda daha da açıklamasını beklerim.
trevorkavanaugh

2
Bunun arayüzleri ve polimorfizmin temelleri ile ilgili her şeyi açıklamakla çok az ilgisi var
A-Developer-Has-No-Name

123

Arayüzleri kullanışlı kılan şey , "fikrinizi değiştirebilir ve daha sonra farklı bir uygulama kullanabilirsiniz ve yalnızca nesnenin oluşturulduğu tek yeri değiştirmek zorunda olduğunuz" gerçeği değildir . Bu bir sorun değil.

Gerçek nokta zaten adda: herkesin o arayüzde çalışan tüm kodu kullanmak için uygulayabileceği bir arayüz tanımlar . Bunun en iyi örneği, java.util.Collectionsyalnızca sort()veya gibi arabirimlerde çalışan her türlü kullanışlı yöntemi reverse()sağlar List. Buradaki nokta, bu kodun artık arayüzleri uygulayan herhangi bir sınıfı sıralamak veya tersine çevirmek için kullanılabileceğidir List- sadece ArrayListve LinkedListaynı zamanda kendi yazdığınız sınıflar, ki bu java.util.Collectionsyazmayan insanlar asla hayal bile edilmemiş bir şekilde uygulanabilir .

Aynı şekilde, iyi bilinen arabirimlerde veya tanımladığınız arabirimlerde çalışan kodlar yazabilirsiniz ve diğer insanlar sizden sınıflarını desteklemenizi istemeden kodunuzu kullanabilir.

Arayüzlerin bir diğer yaygın kullanımı Geri Aramalar içindir. Örneğin, bir Salınım tablosunun belirli bir sütundaki verileri nasıl görüntülediğini etkilemenizi sağlayan java.swing.table.TableCellRenderer . Bu arabirimi uygularsınız JTable, tablonun oluşturulması sırasında bir örnek geçirirsiniz ve tablonun oluşturulması sırasında, kodunuz işlerini yapmaya çağırılır.


9
Bu oldukça iyi bir cevap, java paketi sınıflarından örnekler verdiğinizde hoşuma gidiyor ...
Owais Qureshi

1
Ben sevdimyou can write code that operates on well-known interfaces, or interfaces you define
Manish Kumar

Bir dakika ... Arayüzleri kullanışlı kılan şey [istediğiniz herhangi bir uygulamayı kullanma yeteneği] değil, [istediğiniz herhangi bir uygulamayı kullanma becerisi]? Yine de tam tersi durumdan bahsetmek oldukça iyi bir nokta.
Powerslave

4
@Powerslave: Daha çok benzetmek Arabirimleri faydalı kılan şey [öğeyi değiştirirken yalnızca bir satırı değiştirmeniz gereken kodu yazma yeteneği] değil, daha ziyade [bir uygulama bile belirtmediğiniz yerde kod yazma yeteneği herşey].
Michael Borgwardt

@MichaelBorgwardt Kulağa daha iyi geliyor. :) Açıkladığınız için teşekkürler!
Powerslave

119

Okuduğum birçok kullanımdan biri, Java'da çoklu kalıtım kullanma arabirimleri olmadan zor olduğu yer:

class Animal
{
void walk() { } 
....
.... //other methods and finally
void chew() { } //concentrate on this
} 

Şimdi bir durum düşünün:

class Reptile extends Animal 
{ 
//reptile specific code here
} //not a problem here

fakat,

class Bird extends Animal
{
...... //other Bird specific code
} //now Birds cannot chew so this would a problem in the sense Bird classes can also call chew() method which is unwanted

Daha iyi tasarım:

class Animal
{
void walk() { } 
....
.... //other methods 
} 

Hayvan chew () yöntemine sahip değildir ve bunun yerine aşağıdaki gibi bir arayüze konur:

interface Chewable {
void chew();
}

ve Sürüngen sınıfının Kuşları değil bunu uygulamasını sağlayın (Kuşlar çiğnemediği için):

class Reptile extends Animal implements Chewable { } 

ve basit bir şekilde Kuşlar:

class Bird extends Animal { }

6
@CHEBURASHKA Ve kötü adlandırma. Eğer Reptile"çiğnenebilir" değil kendisinden daha "çiğniyor". (Bazen) adlandırma arayüzlerinin konvansiyonu Whateverable sadece mükemmel mantıklı yerlerde uygulanmalıdır. Arayüzün adlandırılması Predatorburada daha uygun olacaktır.
Powerslave

6
@Powerslave Doğru imho, bir sürüngen "çiğneyebilir" / "çiğnenebilir". Bir şahin bir avcıdır ama hala çiğneyemez ... sadece nitpicking ama "çiğnenebilir" arayüzün dokümantasyonunda daha iyi tanımlanabilir.
Madmenyo

Süper .. Çok iyi açıkladı. Teşekkür ederim..!
Gurusinghe

1
Anlamıyorum. Görünüşe bakılırsa, arayüzler, onu uygulayan her sınıfta aynı yöntemleri uyguladığınızdan emin olmanın iyi bir yolu olduğu gibi (örn. runn () ile - hepsi aynı). Ama dikkat ederek ve derslerimin aynı yöntem biçimlerine / yapılarına sahip olmasını sağlayarak, aynı şeyi başaramadım mı? Gerçekten arayüzler programcının unutulmadığından emin gibi görünüyor. Ayrıca, arayüzler bana zaman kazandırıyor gibi görünmüyor; Hala onu uygulayan her sınıfta yöntemi tanımlamak gerekiyor.
Alex G

@AlexG - Ben çok kullanır dostum anlattı :) Dahası var, biz basit bir şekilde soruyu cevaplamak için yüzey zar zor çizik vardı!
peevesy

47

Arayüzlerin amacı polimorfizm , yani tip ikamesidir . Örneğin, aşağıdaki yöntem göz önüne alındığında:

public void scale(IBox b, int i) {
   b.setSize(b.getSize() * i);
}

scaleYöntemi çağırırken , IBoxarabirimi uygulayan türde herhangi bir değer sağlayabilirsiniz . Diğer bir deyişle, Rectangleve Squareher ikisi de uygulamak IBox, ya bir sağlayabilir Rectangleveya Squarebir yerde IBoxbekleniyor.


8
Java'da alt sınıflama ve yöntem geçersiz kılma ile bunu başarabilirsem neden arabirim polimorfizmi?
eljenso

1
Arayüzlerin herhangi bir uygulamayı atlaması gerekmediği aynı şeydir. Bu nedenle sınıflar birden fazla arabirim uygulayabilir.
Apocalisp

4
Hey, Java'nın herhangi bir kavramsal bütünlüğü olduğunu söylemedim. Tip ikamesi tüm alt tiplerin amacıdır. Java'nın, hiçbiri özellikle iyi olmayan birden fazla alt tip mekanizması vardır.
Apocalisp

1
Kavramsal bütünlük hakkında da hiçbir şey söylemedim. Ama devam edelim. Her IBox'ı yönteminizle ölçeklendirebiliyorsanız, IBox: IBox.scale (int) 'de bildirilen bir işlem olmamalı mı?
eljenso

1
Integer'ı IBox ile birleştirmek istemiyoruz, bu yüzden Integer'de bir yöntem yapmıyoruz. Ve bir arayüzdeki yöntemlerin sayısı, onu uygulamanın ne kadar hantal olacağını değil, ifade ettiği soyutlamanın tutarlılığı ve uyumu ile belirlenir. Her neyse, cevaplarınız için teşekkürler Apo.
eljenso

33

Arabirimler statik olarak yazılan dillerin polimorfizmi desteklemesine izin verir. Nesneye Dayalı bir saf, bir dilin tam özellikli bir Nesne Odaklı dil olabilmesi için kalıtım, kapsülleme, modülerlik ve polimorfizm sağlamasında ısrar eder. Dinamik tipte - veya ördek tipinde - dillerde (Smalltalk gibi) polimorfizm önemsizdir; ancak, statik olarak yazılan dillerde (Java veya C # gibi) polimorfizm önemsiz olmaktan uzaktır (aslında, yüzeyde güçlü yazım kavramıyla çelişiyor gibi görünmektedir).

Göstereyim:

Dinamik olarak yazılan (veya ördekle yazılan) bir dilde (Smalltalk gibi), tüm değişkenler nesnelere referanstır (daha az ve daha fazlası değil). Yani, Smalltalk'ta bunu yapabilirim:

|anAnimal|    
anAnimal := Pig new.
anAnimal makeNoise.

anAnimal := Cow new.
anAnimal makeNoise.

Bu kod:

  1. AnAnimal adlı bir yerel değişkeni bildirir (değişkenin TÜRÜ'Ü BELİRTMEZ olduğumuzu unutmayın - tüm değişkenler, bir nesneye daha fazla ve daha az değil, bir referanstır.)
  2. "Domuz" adlı sınıfın yeni bir örneğini oluşturur
  3. Bu yeni Pig örneğini anAnimal değişkenine atar.
  4. Mesajı makeNoisedomuza gönderir .
  5. Her şeyi bir inek kullanarak tekrarlar, ancak Domuz ile aynı değişkene atar.

Aynı Java kodu şöyle görünecektir (Duck ve Cow'in Animal'in alt sınıfları olduğu varsayımı:

Animal anAnimal = new Pig();
duck.makeNoise();

anAnimal = new Cow();
cow.makeNoise();

Sebze sınıfını tanıtana kadar hepsi iyi ve güzel. Sebzeler, Hayvan ile aynı davranışlara sahiptir, ancak hepsi değil. Örneğin, hem Hayvan hem de Sebze büyüyebilir, ancak açıkça sebzeler ses çıkarmaz ve hayvanlar hasat edilemez.

Smalltalk'ta şunu yazabiliriz:

|aFarmObject|
aFarmObject := Cow new.
aFarmObject grow.
aFarmObject makeNoise.

aFarmObject := Corn new.
aFarmObject grow.
aFarmObject harvest.

Bu, Smalltalk'ta mükemmel bir şekilde çalışır çünkü ördek türünde (bir ördek gibi yürür ve ördek gibi quack yaparsa - bir ördek.) Bu durumda, bir nesneye mesaj gönderildiğinde, bir arama yapılır. alıcının yöntem listesi ve eşleşen bir yöntem bulunursa çağrılır. Değilse, bir çeşit NoSuchMethodError istisnası atılır - ancak hepsi çalışma zamanında yapılır.

Ancak statik olarak yazılmış bir dil olan Java'da, değişkenimize ne tür atayabiliriz? Mısırın büyümeyi desteklemek için Sebzeden miras alması gerekir, ancak Gürültüden dolayı hayvandan miras alamaz. İnek, makeNoise'ı desteklemek için Hayvandan miras almalıdır, ancak hasat uygulamaması gerektiğinden Sebzeden miras alamaz. Birden fazla mirasa ihtiyacımız var gibi görünüyor - birden fazla sınıftan miras alma yeteneği. Ancak, ortaya çıkan tüm uç durumlar nedeniyle oldukça zor bir dil özelliği ortaya çıkıyor (birden fazla paralel üst sınıf aynı yöntemi uyguladığında ne olur? Vb.)

Gelen arayüzler boyunca ...

Hayvan ve Sebze dersleri yaparsak, her biri Growable uygulayarak İnekimizin Hayvan ve Mısırımızın Sebze olduğunu söyleyebiliriz. Ayrıca hem Hayvansal hem de Sebze Yetiştirilebilir olduğunu söyleyebiliriz. Bu, her şeyi büyütmek için bunu yazmamızı sağlar:

List<Growable> list = new ArrayList<Growable>();
list.add(new Cow());
list.add(new Corn());
list.add(new Pig());

for(Growable g : list) {
   g.grow();
}

Ve hayvan seslerini yapmak için bunu yapmamıza izin veriyor:

List<Animal> list = new ArrayList<Animal>();
list.add(new Cow());
list.add(new Pig());
for(Animal a : list) {
  a.makeNoise();
}

Ördek tipi dilin avantajı, gerçekten güzel bir polimorfizm elde etmenizdir: bir sınıfın davranış sağlamak için tek yapması gereken yöntemi sağlamaktır. Herkes iyi oynadığı ve yalnızca tanımlı yöntemlerle eşleşen mesajlar gönderdiği sürece her şey iyidir. Dezavantajı, aşağıdaki hata türünün çalışma zamanına kadar yakalanmamasıdır:

|aFarmObject|
aFarmObject := Corn new.
aFarmObject makeNoise. // No compiler error - not checked until runtime.

Statik olarak yazılan diller, derleme zamanında aşağıdaki iki tür hatayı yakalayacaklarından çok daha iyi "sözleşme ile programlama" sağlar:

// Compiler error: Corn cannot be cast to Animal.
Animal farmObject = new Corn();  
farmObject makeNoise();

-

// Compiler error: Animal doesn't have the harvest message.
Animal farmObject = new Cow();
farmObject.harvest(); 

Yani .... özetlemek gerekirse:

  1. Arayüz uygulaması, nesnelerin ne tür şeyler yapabileceğini (etkileşim) belirlemenize izin verir ve Sınıf mirası, şeylerin nasıl yapılması gerektiğini belirtmenize olanak tanır (uygulama).

  2. Arayüzler derleyici türü kontrolünden ödün vermeden "gerçek" polimorfizmin birçok faydasını sunar.


2
Bu, başka bir soruya verdiğim cevabın metni: stackoverflow.com/questions/379282/… . Ama bunlar ilgili cevaplar.
Jared

2
Öyleyse sorabilir miyim, ördek tipi bir dil, su bitkileri için kullandığı Animal.water () (bu, ihanet eden çiftçi sızıntı olduğunu söylerdi) ve Plant.water () arasında nasıl ayrım yapar. Belirsizlik düşmandır. Belirsizliğin üstesinden gelmek için gerekli her türlü ayrıntı kabul edilebilir IMO'dur.
Bill K

1
Evet .. büyüklük ördeği yazılan dillerle oyunun adıdır. Ördekle yazılmış bir dilde profesyonelce çalışırken, 50-100 karakter uzunluğunda adlara sahip üyeleri (yöntemler ve değişkenler) görmek nadir değildir.
Jared

1
Ördekle yazılan dillerin bir diğer büyük dezavantajı, statik analize dayalı programlı yeniden düzenleme yapamamaktır - printString yönteminizin tüm arayanlarının listesi için bir Smalltalk görüntüsü istemeyi deneyin ... TÜM printString yöntemlerinin tüm arayanlarının listesini alacaksınız. ...
Jared

... çünkü Automobile # printString çağrısı, NearEarthOrbit # printString çağrısından programlı olarak ayırt edilemez.
Jared

9

Normalde Arayüzler kullanmanız gereken arayüzü tanımlar (adından da anlaşılacağı gibi ;-)). Örneklem


public void foo(List l) {
   ... do something
}

Şimdi fonksiyonunuz s'yi fookabul ediyor ArrayList,LinkedList sadece bir türü değil, s, ... .

Java'daki en önemli şey, birden fazla arabirim uygulayabilmenizdir, ancak yalnızca bir sınıfı genişletebilirsiniz! Örneklem:


class Test extends Foo implements Comparable, Serializable, Formattable {
...
}
mümkün ama

class Test extends Foo, Bar, Buz {
...
}
değil!

Kodunuz yukarıda da olabilir: IBox myBox = new Rectangle();. Önemli olan şimdi, myBox SADECE diğer yöntemlerden (muhtemelen mevcut) değil IBox yöntemlerini / alanlarını içermesidir Rectangle.


1
'Liste'nin bir arayüz üyesi olması gerekiyor mu?
Click Olumlu oy

1
Liste, java koleksiyon kütüphanesindeki bir arayüzdür.
rmeador

Liste, standart Java kitaplığındaki bir arabirimdir ( java.sun.com/javase/6/docs/api/java/util/List.html ). Sadece amacını göstermek için kullanıyor.
Michael Myers

6

Arayüzlerin yaptığı her şeyi anladığınızı düşünüyorum, ancak henüz bir Arayüzün yararlı olduğu durumları hayal etmiyorsunuz.

Bir nesneyi dar bir kapsam dahilinde (örneğin, bir yöntem çağrısında) başlatır, kullanır ve serbest bırakırsanız, Arayüz gerçekten hiçbir şey eklemez. Belirttiğiniz gibi, somut sınıf biliniyor.

Arayüzlerin faydalı olduğu yerlerde, bir nesnenin tek bir yerde oluşturulması ve uygulama ayrıntılarını umursamayan bir arayana geri dönmesi gerekir. IBox örneğinizi bir Shape olarak değiştirelim. Artık Dikdörtgen, Daire, Üçgen vb. Gibi Şekil uygulamalarına sahip olabiliriz. GetArea () ve getSize () yöntemlerinin uygulamaları her beton sınıfı için tamamen farklı olacaktır.

Şimdi, geçilen parametrelere bağlı olarak uygun bir Şekil döndürecek çeşitli createShape (params) yöntemlerine sahip bir fabrika kullanabilirsiniz. bir daire mi, bir kare mi, vs.

Şimdi, şekilleriniz üzerinde gerçekleştirmeniz gereken çeşitli işlemlerin olduğunu hayal edin. Belki bunları alana göre sıralamanız, hepsini yeni bir boyuta ayarlamanız ve ardından bir kullanıcı arayüzünde görüntülemeniz gerekir. Şekillerin tümü fabrika tarafından oluşturulur ve Sıralayıcı, Sizer ve Display sınıflarına kolayca aktarılabilir. Gelecekte bir altıgen sınıfı eklemeniz gerekiyorsa, fabrikadan başka bir şeyi değiştirmeniz gerekmez. Arayüz olmadan, başka bir şekil eklemek çok dağınık bir süreç haline gelir.


6

yapabilirsin

Ibox myBox = new Rectangle();

bu şekilde bu nesneyi Ibox olarak kullanıyorsunuz ve bunun gerçekten umrunda değil Rectangle.


Yani böyle yazabilir miyiz ?! > Rectangle inst = yeni Rectangle ();
Dr.jacky

@ Mr.Hyde Daha sonra eklemek Squareisterseniz bir probleminiz olacak ... arayüzler olmadan yapmaya çalışırsanız, bunu garanti edemezsiniz Squareve Rectangleaynı yöntemlere sahip olursunuz ... bu, bir kabusa neden olabilir daha geniş bir kod tabanı ... Unutmayın, arayüzler bir şablon tanımlar.
Kolob Kanyonu

6

NEDEN ARAYÜZ ??????

Bir köpekle başlar. Özellikle, bir boksör .

Pugın çeşitli davranışları vardır:

public class Pug { 
private String name;
public Pug(String n) { name = n; } 
public String getName() { return name; }  
public String bark() { return  "Arf!"; } 
public boolean hasCurlyTail() { return true; } }

Ve ayrıca bir takım davranışları olan bir Labrador'unuz var.

public class Lab { 
private String name; 
public Lab(String n) { name = n; } 
public String getName() { return name; } 
public String bark() { return "Woof!"; } 
public boolean hasCurlyTail() { return false; } }

Bazı puglar ve laboratuarlar yapabiliriz:

Pug pug = new Pug("Spot"); 
Lab lab = new Lab("Fido");

Ve onların davranışlarını çağırabiliriz:

pug.bark() -> "Arf!" 
lab.bark() -> "Woof!" 
pug.hasCurlyTail() -> true 
lab.hasCurlyTail() -> false 
pug.getName() -> "Spot"

Diyelim ki bir köpek kulübesi işletiyorum ve barındırdığım tüm köpekleri takip etmem gerekiyor. Ben ayrı dizide benim pugs ve Labrador saklamak gerekir :

public class Kennel { 
Pug[] pugs = new Pug[10]; 
Lab[] labs = new Lab[10];  
public void addPug(Pug p) { ... } 
public void addLab(Lab l) { ... } 
public void printDogs() { // Display names of all the dogs } }

Ancak bu açıkça optimal değildir. Eğer bazı kaniş ev istiyorum de, bunu Poodle bir dizi eklemek için benim Kennel tanımını değiştirmek zorunda. Aslında, her köpek türü için ayrı bir diziye ihtiyacım var.

İçgörü: Hem puglar hem de labradors (ve kanişler) köpek türleridir ve aynı davranışlara sahiptirler. Yani, (bu örneğin amaçları için) tüm köpeklerin havlayabileceğini, bir isme sahip olabileceğini ve kıvırcık bir kuyruğa sahip olabileceğini veya olmayabilir. Tüm köpeklerin neler yapabileceğini tanımlamak için bir arayüz kullanabiliriz, ancak bu belirli davranışları uygulamak için belirli köpek türlerine bırakabiliriz. Arayüz "tüm köpeklerin yapabileceği şeyler" diyor, ancak her davranışın nasıl yapıldığını söylemiyor.

public interface Dog 
{
public String bark(); 
public String getName(); 
public boolean hasCurlyTail(); }

Sonra Köpek davranışlarını uygulamak için Pug ve Lab sınıflarını biraz değiştiriyorum. Bir Pug'ın bir Köpek ve bir Lab'ın bir köpek olduğunu söyleyebiliriz.

public class Pug implements Dog {
// the rest is the same as before } 

public class Lab implements Dog { 
// the rest is the same as before 
}

Daha önce yaptığım gibi hala Pugs ve Labs'ı başlatabilirim, ancak şimdi de bunu yapmanın yeni bir yolunu elde ediyorum:

Dog d1 = new Pug("Spot"); 
Dog d2 = new Lab("Fido");

Bu d1'in sadece bir köpek olmadığını, özellikle bir Pug olduğunu söylüyor. Ve d2 aynı zamanda bir Köpek, özellikle de bir Lab. Davranışları çağırabiliriz ve daha önce olduğu gibi çalışırlar:

d1.bark() -> "Arf!" 
d2.bark() -> "Woof!" 
d1.hasCurlyTail() -> true 
d2.hasCurlyTail() -> false 
d1.getName() -> "Spot"

İşte tüm ekstra işlerin işe yaradığı yer. Kennel sınıfı çok daha basit hale geldi. Sadece bir dizi ve bir addDog yöntemine ihtiyacım var. Her ikisi de köpek olan herhangi bir nesne ile çalışacaktır; yani, Köpek arayüzünü uygulayan nesneler.

public class Kennel {
Dog[] dogs = new Dog[20]; 
public void addDog(Dog d) { ... } 
public void printDogs() {
// Display names of all the dogs } }

Bunu nasıl kullanacağınız aşağıda açıklanmıştır:

Kennel k = new Kennel(); 
Dog d1 = new Pug("Spot"); 
Dog d2 = new Lab("Fido"); 
k.addDog(d1); 
k.addDog(d2); 
k.printDogs();

Son ifade şunu gösterir: Spot Fido

Bir arabirim, arabirimi uygulayan tüm sınıfların ortak paylaşacağı bir dizi davranış belirleme olanağı sağlar. Sonuç olarak, ne tür belirli bir nesneyi tutacağını, yalnızca arabirimi uygulayan nesneleri tutacaklarını önceden bilmek zorunda olmayan değişkenleri ve koleksiyonları (diziler gibi) tanımlayabiliriz.


@niranjan kurambhatti i köpek genişletmek için tüm sınıflar yapabilirsiniz ama yine de neden arayüzü ??
Jeeva

3

Arayüzlerin nasıl kullanıldığına harika bir örnek Koleksiyonlar çerçevesindedir. A alan bir işlev yazarsanız List, kullanıcının bir Vectorveya bir ArrayListveya bir HashListveya bir veya başka bir şekilde geçmesi önemli değildir . Ve bunu Listbir CollectionveyaIterable arayüz .

Bu Collections.sort(List list), nasıl Listuygulandığından bağımsız olarak işlevleri mümkün kılar .


3

Fabrika Kalıpları ve diğer yaratıcı kalıpların Java'da bu kadar popüler olmasının nedeni budur . Bunlar olmadan Java'nın, örneklemenin kolay soyutlanması için kutudan çıkmış bir mekanizma sağlamadığı konusunda haklısınız. Yine de, olmayan her yerde soyutlama elde edersiniz kodunuzun en olmalıdır sizin yönteminde bir nesne oluşturmak.

Bir kenara, genel olarak insanları arayüzleri adlandırmak için "IRealname" mekanizmasını takip etmemeye teşvik ediyorum. Bu, bir ayağını Macar notasyonunun mezarına koyan bir Windows / COM şeyidir ve gerçekten gerekli değildir (Java zaten güçlü bir şekilde yazılmıştır ve arayüzlere sahip olmanın tüm amacı, onları sınıf türlerinden mümkün olduğunca ayırt edilemez hale getirmektir).


1
Güçlü yazmayı statik yazmayla karıştırıyorsunuz.
eljenso

3

Daha sonraki bir tarihte mevcut bir sınıfı alıp uygulayabileceğinizi IBoxve daha sonra kutunun farkında olan tüm kodunuz tarafından kullanılabileceğini unutmayın.

Arayüzler -able olarak adlandırılırsa, bu biraz daha açık hale gelir . Örneğin

public interface Saveable {
....

public interface Printable {
....

(Adlandırma düzenleri her zaman çalışmaz, örneğin Boxableburada uygun olduğundan emin değilim )


3

arabirimlerin tek amacı, bir arabirimi uygulayan sınıfın bir arabirim tarafından açıklandığı gibi içinde doğru yöntemlere sahip olduğundan emin olmaktır? Yoksa arayüzlerin başka bir kullanımı var mı?

Java 8 sürümü ile tanıtılan arayüzün yeni özellikleri ile cevabı güncelliyoruz .

Arayüz özetinde oracle dokümantasyon sayfasından :

Arabirim bildirimi şunları içerebilir

  1. yöntem imzalar
  2. varsayılan yöntemler
  3. statik yöntemler
  4. değişmez tanımlamalar.

Uygulamaları olan tek yöntem varsayılan ve statik yöntemlerdir.

Arayüz kullanım alanları :

  1. Bir sözleşme tanımlamak için
  2. İlişkisiz sınıfları bir yeteneği olan (örn.Serializable Arabirimi , bu arabirimi uygulamak dışında aralarında herhangi bir ilişki olabilir veya olmayabilir)
  3. Değiştirilebilir uygulama sağlamak, örneğin strateji modeli
  4. Varsayılan yöntemler , kitaplıklarınızın arabirimlerine yeni işlevler eklemenizi ve bu arabirimlerin eski sürümleri için yazılan kodla ikili uyumluluk sağlamanızı sağlar
  5. Kütüphanenizdeki yardımcı yöntemleri statik yöntemlerle düzenleyin (bir arabirime özgü statik yöntemleri ayrı bir sınıf yerine aynı arabirimde tutabilirsiniz)

Soyut sınıf ve arayüz arasındaki farka ilişkin bazı SE soruları ve çalışma örnekleriyle kullanım örnekleri:

Bir arayüz ile soyut sınıf arasındaki fark nedir?

Bir Interface ve Abstract sınıfı arasındaki farkı nasıl açıklamalıydım?

Java 8'de eklenen yeni özellikleri anlamak için belgeler sayfasına göz atın : varsayılan yöntemler ve statik yöntemler .


Soru java-8 hakkında bir şey sormadığı için java-8 etiketini kaldırdım (ve java-8'den çok önce soruldu). Etiketler sorular içindir, cevaplar için değildir.
Tagir Valeev

2

Arayüzlerin amacı soyutlama veya uygulamadan ayrıştırmaktır.

Programınıza bir soyutlama getirirseniz, olası uygulamaları umursamazsınız. İlgilendiğiniz neyi yapabilirim değil nasıl ve bir kullanmak interfaceJava bu ifade etmek.


Tüm yapılandırılmış programlamanın amacı soyutlamadır. Arayüzlerin amacının soyutlama olduğunu neden söylüyorsunuz, çünkü aynı şeyi jenerik ve sınıf kompozisyonunu kullanarak da başarabiliyorum?
Apocalisp

1
Tüm yapılandırılmış programlama soyutlama (iddianız) ise, arayüzler bu soyutlamadaki soyutlamalardır.
eljenso

1

CardboardBox ve HtmlBox'ınız varsa (ikisi de IBox'ı uygular), ikisini de bir IBox'ı kabul eden herhangi bir yönteme geçirebilirsiniz. Her ikisi de çok farklı ve tamamen değiştirilemez olsa da, "açık" veya "yeniden boyutlandırma" umurumda olmayan yöntemler yine de sınıflarınızı kullanabilir (belki de ekranda bir şey görüntülemek için kaç piksel gerektiğini önemsiyorlar).


1

Çoklu kalıtım sağlamak için java'ya bir fetaranın eklendiği arayüzler. Java geliştiricileri, birden fazla mirasa sahip olmanın "tehlikeli" bir özellik olduğunu fark ettiler / bu yüzden bir arayüz fikri ortaya çıktı.

çoklu kalıtım tehlikelidir, çünkü aşağıdaki gibi bir sınıfınız olabilir:


class Box{
    public int getSize(){
       return 0;
    }
    public int getArea(){
       return 1;
    }

}

class Triangle{
    public int getSize(){
       return 1;
    }
    public int getArea(){
       return 0;
    }

}

class FunckyFigure extends Box, Triable{
   // we do not implement the methods we will used the inherited ones
}

Kullandığımızda çağrılması gereken yöntem hangisidir?


   FunckyFigure.GetArea(); 

Tüm problemler arayüzlerle çözülür, çünkü arayüzleri uzatabileceğinizi ve sınıflandırma yöntemlerine sahip olmayacaklarını biliyorsunuz ... tabii derleyici güzeldir ve bir yöntem uygulamadıysanız size söyler, ancak bunun daha ilginç bir fikrin bir yan etkisi.


Cevabınızda çoklu uygulama devralma ve çoklu arayüz devralma arasında bir fark yaratmak isteyebilirsiniz, aksi takdirde kafa karıştırıcı olur.
eljenso

0

İşte arayüz avantajı anlayışım. Eğer Yanlışsam beni düzelt. İşletim sistemi geliştirdiğimizi ve diğer ekibin bazı cihazların sürücülerini geliştirdiğini hayal edin. StorageDevice bir arayüz geliştirdik. Diğer geliştiriciler ekibi tarafından sağlanan iki uygulamamız (FDD ve HDD) var.

Daha sonra sadece StorageDevice arabirimini uygulanan sınıfın bir örneğini ileterek saveData gibi arabirim yöntemlerini çağırabilen bir OperatingSystem sınıfımız var.

Buradaki avantaj, arayüzün uygulanmasını önemsemememizdir. Diğer ekip işi StorageDevice arabirimini uygulayarak yapar.

package mypack;

interface StorageDevice {
    void saveData (String data);
}


class FDD implements StorageDevice {
    public void saveData (String data) {
        System.out.println("Save to floppy drive! Data: "+data);
    }
}

class HDD implements StorageDevice {
    public void saveData (String data) {
        System.out.println("Save to hard disk drive! Data: "+data);
    }
}

class OperatingSystem {
    public String name;
    StorageDevice[] devices;
    public OperatingSystem(String name, StorageDevice[] devices) {

        this.name = name;
        this.devices = devices.clone();

        System.out.println("Running OS " + this.name);
        System.out.println("List with storage devices available:");
        for (StorageDevice s: devices) {
            System.out.println(s);
        }

    }

    public void saveSomeDataToStorageDevice (StorageDevice storage, String data) {
        storage.saveData(data);
    }
}

public class Main {

    public static void main(String[] args) {

        StorageDevice fdd0 = new FDD();
        StorageDevice hdd0 = new HDD();     
        StorageDevice[] devs = {fdd0, hdd0};        
        OperatingSystem os = new OperatingSystem("Linux", devs);
        os.saveSomeDataToStorageDevice(fdd0, "blah, blah, blah...");    
    }
}

Aynı şey StorageDevice sınıfını genişleten soyut sınıf StorageDevice ve FDD ve HDD sınıflarıyla da yapılabilir. ama soyut sınıfı kullanırsak çoklu kalıtımdan yararlanamayız.
Vladimir Georgiev
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.