Apakah ada lebih banyak antarmuka daripada memiliki metode yang benar

159

Jadi katakanlah saya memiliki antarmuka ini:

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

Dan saya memiliki kelas yang mengimplementasikannya:

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

Jika saya ingin menggunakan antarmuka IBox, saya tidak bisa membuat instance darinya, dengan cara:

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

Baik? Jadi sebenarnya saya harus melakukan ini:

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

Jika itu benar, maka satu-satunya tujuan antarmuka adalah untuk memastikan bahwa kelas yang mengimplementasikan antarmuka telah mendapatkan metode yang benar di dalamnya seperti yang dijelaskan oleh antarmuka? Atau apakah ada penggunaan antarmuka lainnya?

Klik Upvote
sumber
2
Ingat, antarmuka tidak khusus untuk Java. Semua bahasa OOP memilikinya dalam beberapa bentuk atau lainnya, meskipun tidak selalu secara eksplisit didefinisikan sebagai Java.
Herms
2
Secara teknis, semua bahasa OOP yang sangat diketik memilikinya dalam beberapa bentuk. Bahasa yang tidak diketik, atau diketik bebek, tidak memiliki konsep serupa.
Jared
1
@ Jared Apakah Anda tidak bingung mengetik kuat dengan mengetik statis, dan "tidak mengetik" dengan mengetik secara dinamis?
eljenso
Polimorfisme dapat dicapai melalui antarmuka juga. Periksa bagian terakhir dari halaman ini codenuggets.com/2014/06/20/java-interface
Jeff

Jawaban:

143

Antarmuka adalah cara untuk membuat kode Anda lebih fleksibel. Apa yang Anda lakukan adalah ini:

Ibox myBox=new Rectangle();

Kemudian, nanti, jika Anda memutuskan ingin menggunakan jenis kotak yang berbeda (mungkin ada perpustakaan lain, dengan jenis kotak yang lebih baik), Anda beralih kode Anda ke:

Ibox myBox=new OtherKindOfBox();

Setelah terbiasa, Anda akan menemukan cara yang bagus (sebenarnya penting) untuk bekerja.

Alasan lain adalah, misalnya, jika Anda ingin membuat daftar kotak dan melakukan beberapa operasi pada masing-masing, tetapi Anda ingin daftar berisi berbagai jenis kotak. Pada setiap kotak yang dapat Anda lakukan:

myBox.close()

(dengan asumsi IBox memiliki metode close ()) meskipun kelas sebenarnya dari myBox berubah tergantung pada kotak yang Anda gunakan dalam iterasi.

kode morgancodes
sumber
54
Tidak ada dalam jawaban ini yang eksklusif untuk antarmuka Java . Hal yang sama berlaku juga untuk kelas abstrak, atau bahkan yang konkret. Saya akan mengharapkan jawaban yang baik untuk menyebutkan kemampuan untuk mengimplementasikan beberapa antarmuka, dan kapan / mengapa itu akan berguna.
Rogério
16
Bagaimana ini bisa dipilih sebagai jawabannya? Ini adalah deskripsi singkat tentang mengapa polimorfisme berguna, tetapi seperti yang dikatakan oleh poster di atas, saya akan mengharapkan penjelasan yang lebih baik dari banyak antarmuka dan bahkan yang lebih penting ketika layak untuk menggunakan antarmuka vs kelas abstrak.
trevorkavanaugh
2
Ini sangat sedikit hubungannya dengan menjelaskan antarmuka dan segala sesuatu yang berkaitan dengan dasar-dasar polimorfisme
A-Developer-Has-No-Name
123

Apa yang membuat antarmuka tidak bermanfaat fakta bahwa "Anda dapat mengubah pikiran Anda dan menggunakan implementasi yang berbeda di lain waktu dan hanya perlu mengubah satu tempat di mana objek dibuat". Itu bukan masalah.

Poin sebenarnya sudah ada dalam namanya: mereka mendefinisikan antarmuka yang dapat diterapkan oleh siapa saja untuk menggunakan semua kode yang beroperasi pada antarmuka itu. Contoh terbaik adalah java.util.Collectionsyang menyediakan semua jenis metode berguna yang beroperasi secara eksklusif pada antarmuka, seperti sort()atau reverse()untuk List. Intinya di sini adalah bahwa kode ini sekarang dapat digunakan untuk mengurutkan atau membalikkan setiap kelas yang mengimplementasikan Listantarmuka - tidak hanya ArrayListdan LinkedList, tetapi juga kelas yang Anda tulis sendiri, yang dapat diimplementasikan dengan cara yang orang-orang yang menulis java.util.Collectionstidak pernah bayangkan.

Dengan cara yang sama, Anda dapat menulis kode yang beroperasi pada antarmuka yang terkenal, atau antarmuka yang Anda tentukan, dan orang lain dapat menggunakan kode Anda tanpa harus meminta Anda untuk mendukung kelas mereka.

Penggunaan antarmuka lain yang umum adalah untuk Callback. Misalnya, java.swing.table.TableCellRenderer , yang memungkinkan Anda untuk memengaruhi bagaimana tabel Swing menampilkan data dalam kolom tertentu. Anda mengimplementasikan antarmuka itu, meneruskan contoh ke JTable, dan pada beberapa titik selama rendering tabel, kode Anda akan dipanggil untuk melakukan hal-hal tersebut.

Michael Borgwardt
sumber
9
Itu jawaban yang cukup bagus, saya suka ketika Anda memberi contoh dari kelas paket java ...
Owais Qureshi
1
Saya sukayou can write code that operates on well-known interfaces, or interfaces you define
Manish Kumar
Tunggu sebentar ... Apa yang membuat antarmuka berguna bukanlah [kemampuan untuk menggunakan implementasi apa pun yang Anda suka], tetapi [kemampuan untuk menggunakan implementasi apa pun yang Anda suka]? Menyebutkan kasus sebaliknya cukup banyak poin yang baik.
Powerslave
4
@ Powerlave: parafrase lebih seperti Apa yang membuat antarmuka berguna bukan [kemampuan untuk menulis kode di mana Anda harus mengubah hanya satu baris ketika mengubah implementasi] tetapi [kemampuan untuk menulis kode di mana Anda bahkan tidak menentukan implementasi di semua].
Michael Borgwardt
@MichaelBorgwardt Kedengarannya jauh lebih baik. :) Terima kasih telah mengklarifikasi!
Powerslave
119

Salah satu dari banyak kegunaan yang saya baca adalah di mana yang sulit tanpa multi-inheritance-using-interface di Jawa:

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

Sekarang, Bayangkan sebuah kasus di mana:

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

tapi,

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

Desain yang lebih baik adalah:

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

Animal tidak memiliki metode chew () dan sebagai gantinya diletakkan di antarmuka sebagai:

interface Chewable {
void chew();
}

dan minta kelas Reptil mengimplementasikan ini dan bukan Burung (karena Burung tidak bisa mengunyah):

class Reptile extends Animal implements Chewable { } 

dan memetikan burung hanya:

class Bird extends Animal { }
peevesy
sumber
6
@CHEBURASHKA Dan penamaan yang buruk. Jika Reptile"mengunyah", maka bukan itu sendiri adalah "kunyah". Konvensi (kadang-kadang) antarmuka penamaan Apapun yang seharusnya hanya diterapkan di mana masuk akal. Memberi nama antarmuka Predatorakan lebih sesuai di sini.
Powerslave
6
@Powerlave Benar imho, reptil adalah "mampu mengunyah" / "kunyah". Elang adalah predator tetapi masih tidak bisa mengunyah ... hanya nitpicking tetapi "kunyah" dapat didefinisikan lebih baik dalam dokumentasi antarmuka.
Madmenyo
Luar biasa .. Dijelaskan dengan sangat baik. Terima kasih..!
Gurusinghe
1
Saya tidak mengerti. Semua sepertinya adalah bahwa antarmuka adalah cara yang baik untuk memastikan Anda menerapkan metode yang sama di setiap kelas yang mengimplementasikannya, (mis. Sehingga mencegah saya membuat pilihan bodoh dari kelas Bird dengan run () dan kelas Dog dengan runn () - semuanya sama). Tetapi dengan memperhatikan dan membuat kelas saya memiliki format / struktur metode yang sama, tidak bisakah saya mencapai hal yang sama? Benar-benar tampak seperti antarmuka, pastikan pemrogram tidak pelupa. Selain itu, antarmuka sepertinya tidak menghemat waktu saya; Saya masih perlu mendefinisikan metode di setiap kelas yang mengimplementasikannya.
Alex G
@AlexG - saya katakan salah satu dari banyak kegunaan Bung :) Ada lagi, kami baru saja menggaruk permukaan untuk menjawab pertanyaan dengan cara yang sederhana!
peevesy
47

Tujuan dari antarmuka adalah polimorfisme , alias subtitusi jenis . Misalnya, diberikan metode berikut:

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

Saat memanggil scale metode, Anda bisa memberikan nilai apa pun dari tipe yang mengimplementasikan IBoxantarmuka. Dengan kata lain, jika Rectangledan Squarekeduanya diimplementasikan IBox, Anda dapat memberikan a Rectangleatau a Squaredimanapun IBoxdiharapkan.

Apocalisp
sumber
8
Mengapa tujuan dari polimorfisme antarmuka, jika saya sudah bisa mencapainya di Java dengan subclassing dan metode overriding?
eljenso
1
Itu hal yang sama, kecuali bahwa antarmuka harus mengabaikan implementasi apa pun. Kelas karenanya dapat mengimplementasikan lebih dari satu antarmuka.
Apocalisp
4
Hei, saya tidak pernah mengatakan Java memiliki integritas konseptual apa pun. Substitusi tipe adalah tujuan dari semua subtyping. Java kebetulan memiliki lebih dari satu mekanisme subtyping, tidak ada yang sangat bagus.
Apocalisp
1
Saya tidak pernah mengatakan apa pun tentang integritas konseptual juga. Tapi mari kita lanjutkan. Jika Anda dapat menskalakan setiap IBox dengan metode Anda, bukankah seharusnya itu merupakan operasi yang dinyatakan di IBox: IBox.scale (int)?
eljenso
1
Kami tidak ingin memasangkan Integer ke IBox, itu sebabnya kami tidak menjadikannya metode pada Integer. Dan jumlah metode pada antarmuka ditentukan oleh konsistensi dan kohesi dari abstraksi yang diungkapkannya, bukan seberapa rumitnya untuk mengimplementasikannya. Bagaimanapun, terima kasih atas jawaban Anda Apo.
eljenso
33

Antarmuka memungkinkan bahasa yang diketik secara statis untuk mendukung polimorfisme. Purist Berorientasi Objek akan bersikeras bahwa bahasa harus menyediakan warisan, enkapsulasi, modularitas dan polimorfisme agar menjadi bahasa Berorientasi Objek dengan fitur lengkap. Dalam bahasa yang diketik secara dinamis - atau diketik bebek - (seperti Smalltalk,) polimorfisme adalah sepele; Namun, dalam bahasa yang diketik secara statis (seperti Java atau C #,) polimorfisme jauh dari sepele (pada kenyataannya, di permukaan tampaknya bertentangan dengan gagasan mengetik kuat.)

Biarkan saya menunjukkan:

Dalam bahasa yang diketik secara dinamis (atau diketik bebek) (seperti Smalltalk), semua variabel adalah referensi ke objek (tidak kurang dan tidak lebih.) Jadi, di Smalltalk, saya bisa melakukan ini:

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

anAnimal := Cow new.
anAnimal makeNoise.

Kode itu:

  1. Deklarasikan variabel lokal yang disebut anAnimal (perhatikan bahwa kami TIDAK menentukan TYPE dari variabel - semua variabel adalah referensi ke objek, tidak lebih dan tidak kurang.)
  2. Membuat instance baru dari kelas bernama "Babi"
  3. Tetapkan instance baru Pig tersebut ke variabel anAnimal.
  4. Mengirim pesan makeNoiseke babi.
  5. Mengulangi semuanya menggunakan sapi, tetapi menugaskannya ke variabel yang sama persis seperti Babi.

Kode Java yang sama akan terlihat seperti ini (membuat asumsi bahwa Bebek dan Sapi adalah subkelas Hewan:

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

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

Itu semua baik dan bagus, sampai kami memperkenalkan kelas Sayuran. Sayuran memiliki beberapa perilaku yang sama dengan Hewan, tetapi tidak semua. Misalnya, Hewan dan Sayuran mungkin bisa tumbuh, tetapi jelas sayuran tidak membuat kebisingan dan hewan tidak bisa dipanen.

Di Smalltalk, kita bisa menulis ini:

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

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

Ini berfungsi dengan baik di Smalltalk karena itu diketik bebek (jika berjalan seperti bebek, dan dukun seperti bebek - itu adalah bebek.) Dalam hal ini, ketika pesan dikirim ke objek, pencarian dilakukan pada daftar metode penerima, dan jika metode yang cocok ditemukan, itu disebut. Jika tidak, beberapa jenis pengecualian NoSuchMethodError dilemparkan - tetapi semuanya dilakukan saat runtime.

Tetapi di Jawa, bahasa yang diketik secara statis, jenis apa yang bisa kita tetapkan untuk variabel kita? Jagung perlu diwariskan dari Sayuran, untuk mendukung tumbuh, tetapi tidak dapat mewarisi dari Hewan, karena tidak membuat kebisingan. Sapi perlu diwariskan dari Hewan untuk mendukung makeNoise, tetapi tidak dapat mewarisi dari Sayuran karena tidak boleh menerapkan panen. Sepertinya kita membutuhkan banyak warisan - kemampuan untuk mewarisi dari lebih dari satu kelas. Tapi itu ternyata menjadi fitur bahasa yang cukup sulit karena semua kasus tepi yang muncul (apa yang terjadi ketika lebih dari satu superclass paralel menerapkan metode yang sama ?, dll.)

Datanglah antarmuka ...

Jika kita membuat kelas Hewan dan Sayuran, dengan masing-masing menerapkan Growable, kita dapat mendeklarasikan bahwa Sapi kita adalah Hewan dan Jagung kita adalah Sayuran. Kami juga dapat menyatakan bahwa Hewan dan Sayuran adalah Growable. Itu memungkinkan kami menulis ini untuk menumbuhkan segalanya:

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

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

Dan ini memungkinkan kita melakukan ini, untuk membuat suara binatang:

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

Keuntungan dari bahasa yang diketik bebek adalah Anda mendapatkan polimorfisme yang benar-benar bagus: semua yang harus dilakukan kelas untuk memberikan perilaku adalah menyediakan metode. Selama semua orang bermain bagus, dan hanya mengirim pesan yang cocok dengan metode yang ditentukan, semuanya baik. Kelemahannya adalah jenis kesalahan di bawah ini tidak diketahui sampai runtime:

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

Bahasa yang diketik secara statis memberikan "pemrograman berdasarkan kontrak" yang jauh lebih baik, karena mereka akan menangkap dua jenis kesalahan di bawah ini pada waktu kompilasi:

// 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(); 

Jadi .... untuk meringkas:

  1. Implementasi antarmuka memungkinkan Anda menentukan jenis hal yang dapat dilakukan objek (interaksi) dan pewarisan Kelas memungkinkan Anda menentukan cara melakukan sesuatu (implementasi).

  2. Antarmuka memberi kita banyak manfaat polimorfisme "benar", tanpa harus mengorbankan pemeriksaan tipe kompiler.

Jared
sumber
2
Ini adalah teks jawaban saya untuk pertanyaan lain: stackoverflow.com/questions/379282/… . Tapi, itu jawaban terkait.
Jared
2
Jadi boleh saya bertanya, bagaimana bahasa yang diketik bebek membedakan antara Animal.water () (yang, petani prudish dulu mengatakan bahwa ia membutuhkan kebocoran) dan Plant.water () yang ia gunakan untuk menyirami tanaman. Ambiguitas adalah musuh. Berapapun jumlah verbositas yang diperlukan untuk mengatasi ambiguitas adalah IMO yang dapat diterima.
Bill K
1
Yap .. ambiguitas adalah nama permainan dengan bahasa yang diketik bebek. Saat bekerja secara profesional dalam bahasa yang diketik bebek, tidak jarang melihat anggota (metode dan variabel) dengan nama yang panjangnya 50-100 karakter.
Jared
1
Kelemahan besar lain dari bahasa yang diketik bebek adalah ketidakmampuan untuk melakukan refactoring terprogram berdasarkan analisis statis - coba minta gambar Smalltalk untuk daftar semua penelepon metode printString Anda ... Anda akan mendapatkan daftar semua penelepon SEMUA metode printString. ...
Jared
... karena penelepon Automobile # printString tidak dapat secara program dibedakan dari penelepon NearEarthOrbit # printString.
Jared
9

Biasanya Antarmuka menentukan antarmuka yang harus Anda gunakan (seperti namanya ;-)). Sampel


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

Sekarang fungsi Anda foomenerima ArrayLists, LinkedLists, ... tidak hanya satu jenis.

Yang paling penting di Java adalah Anda bisa mengimplementasikan banyak antarmuka tetapi Anda hanya bisa memperluas satu kelas! Sampel:


class Test extends Foo implements Comparable, Serializable, Formattable {
...
}
mungkin tapi

class Test extends Foo, Bar, Buz {
...
}
tidak!

Di atas kode Anda juga bisa menjadi: IBox myBox = new Rectangle();. Yang penting sekarang, bahwa myBox ONLY berisi metode / bidang dari IBox dan bukan (mungkin ada) metode lain dari Rectangle.

Johannes Weiss
sumber
1
Apakah 'Daftar' seharusnya menjadi anggota antarmuka?
Klik Suara pada
1
Daftar adalah antarmuka di perpustakaan koleksi java.
rmeador
Daftar adalah antarmuka di perpustakaan Java standar ( java.sun.com/javase/6/docs/api/java/util/List.html ). Dia hanya menggunakannya untuk menggambarkan maksudnya.
Michael Myers
6

Saya pikir Anda mengerti semua yang dilakukan Antarmuka, tetapi Anda belum membayangkan situasi di mana Antarmuka berguna.

Jika Anda membuat instance, menggunakan dan melepaskan objek semua dalam lingkup sempit (misalnya, dalam satu panggilan metode), sebuah Interface tidak benar-benar menambahkan apa pun. Seperti yang Anda catat, kelas konkret dikenal.

Di mana Antarmuka berguna adalah ketika suatu objek perlu dibuat satu tempat dan dikembalikan ke penelepon yang mungkin tidak peduli dengan detail implementasi. Mari kita ubah contoh IBox Anda menjadi Shape. Sekarang kita dapat memiliki implementasi Shape seperti Rectangle, Circle, Triangle, dll. Implementasi metode getArea () dan getSize () akan sangat berbeda untuk setiap kelas beton.

Sekarang Anda dapat menggunakan pabrik dengan berbagai metode createShape (params) yang akan mengembalikan Bentuk yang sesuai tergantung pada params yang dilewati. Jelas, pabrik akan tahu tentang jenis Bentuk apa yang sedang dibuat, tetapi penelepon tidak akan memiliki untuk peduli apakah itu lingkaran, atau kotak, atau sebagainya.

Sekarang, bayangkan Anda memiliki berbagai operasi yang harus Anda lakukan pada bentuk Anda. Mungkin Anda perlu mengurutkannya berdasarkan area, mengatur semuanya ke ukuran baru, dan kemudian menampilkannya di UI. Semua Bentuk dibuat oleh pabrik dan kemudian dapat diteruskan ke kelas Sorter, Sizer, dan Tampilan dengan sangat mudah. Jika Anda perlu menambahkan kelas segi enam beberapa waktu di masa depan, Anda tidak perlu mengubah apa pun kecuali pabrik. Tanpa Antarmuka, menambahkan bentuk lain menjadi proses yang sangat berantakan.

Ickster
sumber
6

Anda bisa melakukannya

Ibox myBox = new Rectangle();

dengan begitu Anda menggunakan objek ini sebagai Ibox dan Anda tidak peduli itu benar-benar Rectangle.

IAdapter
sumber
Itu artinya kita bisa menulis seperti ini ?! > Rectangle inst = Rectangle baru ();
Dr.jacky
@ Mr.Hyde Jika nanti Anda ingin menambahkan SquareAnda akan memiliki masalah .... jika Anda mencoba melakukannya tanpa antarmuka, Anda tidak dapat menjamin itu Squaredan Rectanglememiliki metode yang sama ... ini dapat mengakibatkan mimpi buruk ketika Anda memiliki basis kode yang lebih besar ... Ingat, antarmuka mendefinisikan templat.
Ngarai Kolob
6

MENGAPA INTERFACE ??????

Dimulai dengan seekor anjing. Secara khusus, pesek .

Pesek memiliki berbagai perilaku:

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

Dan Anda memiliki Labrador, yang juga memiliki serangkaian perilaku.

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

Kita bisa membuat beberapa pugs dan laboratorium:

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

Dan kita dapat memohon perilaku mereka:

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

Katakanlah saya menjalankan kandang anjing dan saya harus melacak semua anjing yang saya tempati. Saya perlu menyimpan pugs dan labrador saya di array terpisah :

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

Tapi ini jelas tidak optimal. Jika saya ingin menyimpan beberapa pudel juga, saya harus mengubah definisi Kennel saya untuk menambahkan array Pudel. Faktanya, saya membutuhkan array terpisah untuk setiap jenis anjing.

Wawasan: pugs dan labrador (dan pudel) adalah jenis anjing dan mereka memiliki perilaku yang sama. Yaitu, kita dapat mengatakan (untuk keperluan contoh ini) bahwa semua anjing dapat menggonggong, memiliki nama, dan mungkin memiliki ekor keriting atau tidak. Kita dapat menggunakan antarmuka untuk menentukan apa yang dapat dilakukan semua anjing, tetapi serahkan pada jenis anjing tertentu untuk menerapkan perilaku tertentu itu. Antarmuka mengatakan "ini adalah hal-hal yang dapat dilakukan semua anjing" tetapi tidak mengatakan bagaimana setiap perilaku dilakukan.

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

Kemudian saya sedikit mengubah kelas Pug dan Lab untuk menerapkan perilaku Dog. Kita dapat mengatakan bahwa Pug adalah Anjing dan Lab adalah seekor anjing.

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

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

Saya masih dapat membuat Instanate Pugs dan Labs seperti yang saya lakukan sebelumnya, tapi sekarang saya juga mendapatkan cara baru untuk melakukannya:

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

Ini mengatakan bahwa d1 bukan hanya seekor Anjing, tetapi secara khusus seekor Pug. Dan d2 juga adalah Anjing, khususnya Lab. Kita dapat memohon perilaku dan mereka bekerja seperti sebelumnya:

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

Di sinilah semua pekerjaan ekstra terbayar. Kelas Kennel menjadi lebih sederhana. Saya hanya perlu satu larik dan satu metode addDog. Keduanya akan bekerja dengan benda apa pun yang adalah anjing; yaitu objek yang mengimplementasikan antarmuka Anjing.

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

Berikut cara menggunakannya:

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

Pernyataan terakhir akan menampilkan: Spot Fido

Antarmuka memberi Anda kemampuan untuk menentukan serangkaian perilaku yang akan dimiliki oleh semua kelas yang mengimplementasikan antarmuka. Sebagai konsekuensinya, kita dapat mendefinisikan variabel dan koleksi (seperti array) yang tidak perlu tahu sebelumnya apa jenis objek spesifik yang akan mereka pegang, hanya saja mereka akan memegang objek yang mengimplementasikan antarmuka.

niranjan kurambhatti
sumber
@niranjan kurambhatti saya dapat membuat semua kelas untuk memperluas anjing tetapi masih mengapa antarmuka ??
Jeeva
3

Contoh yang bagus tentang bagaimana antarmuka digunakan dalam kerangka kerja Koleksi. Jika Anda menulis fungsi yang membutuhkan a List, maka tidak masalah jika pengguna memasukkan a Vectoratau a ArrayListatau a HashListatau apa pun. Dan Anda dapat meneruskannya Listke fungsi apa pun yang memerlukan Collectionatau Iterableantarmuka juga.

Ini membuat fungsi menjadi Collections.sort(List list)mungkin, terlepas dari bagaimana Listpenerapannya.

Tidur
sumber
3

Ini adalah alasan mengapa Pola Pabrik dan pola kreasi lainnya sangat populer di Jawa. Anda benar bahwa tanpa mereka Java tidak menyediakan mekanisme out of the box untuk memudahkan abstraksi instantiasi. Namun, Anda mendapatkan abstraksi di mana-mana di mana Anda tidak membuat objek dalam metode Anda, yang seharusnya menjadi sebagian besar kode Anda.

Sebagai tambahan, saya biasanya mendorong orang untuk tidak mengikuti mekanisme "IRealname" untuk penamaan antarmuka. Itu hal Windows / COM yang menempatkan satu kaki di kuburan notasi Hungaria dan benar-benar tidak perlu (Java sudah sangat diketik, dan seluruh titik memiliki antarmuka adalah untuk membuat mereka sebagian besar tidak dapat dibedakan dari tipe kelas mungkin).

Christopher Smith
sumber
1
Anda bingung mengetik kuat dengan mengetik statis.
eljenso
3

Jangan lupa bahwa di kemudian hari Anda dapat mengambil kelas yang ada , dan membuatnya diterapkanIBox , dan kemudian akan tersedia untuk semua kode kotak-sadar Anda.

Ini menjadi sedikit lebih jelas jika antarmuka diberi nama -able . misalnya

public interface Saveable {
....

public interface Printable {
....

dll. (Skema penamaan tidak selalu berfungsi misalnya saya tidak yakin Boxablecocok di sini)

Brian Agnew
sumber
3

satu-satunya tujuan antarmuka adalah untuk memastikan bahwa kelas yang mengimplementasikan antarmuka telah mendapat metode yang benar di dalamnya seperti yang dijelaskan oleh antarmuka? Atau apakah ada penggunaan antarmuka lainnya?

Saya memperbarui jawabannya dengan fitur antarmuka baru, yang telah diperkenalkan dengan versi java 8 .

Dari halaman dokumentasi oracle pada ringkasan antarmuka :

Deklarasi antarmuka dapat berisi

  1. tanda tangan metode
  2. metode standar
  3. metode statis
  4. definisi konstan.

Satu-satunya metode yang memiliki implementasi adalah metode default dan statis.

Penggunaan antarmuka :

  1. Untuk menentukan kontrak
  2. Untuk menautkan kelas yang tidak terkait dengan memiliki kemampuan (mis. Kelas yang mengimplementasikan Serializableantarmuka mungkin atau mungkin tidak memiliki hubungan di antara mereka kecuali menerapkan antarmuka itu
  3. Untuk memberikan implementasi yang dapat dipertukarkan misalnya pola strategi
  4. Metode standar memungkinkan Anda untuk menambahkan fungsionalitas baru ke antarmuka perpustakaan Anda dan memastikan kompatibilitas biner dengan kode yang ditulis untuk versi yang lebih lama dari antarmuka tersebut
  5. Atur metode penolong di perpustakaan Anda dengan metode statis (Anda dapat menyimpan metode statis khusus untuk antarmuka di antarmuka yang sama daripada di kelas yang terpisah)

Beberapa pertanyaan SE terkait sehubungan dengan perbedaan antara kelas abstrak dan antarmuka dan gunakan kasus dengan contoh kerja:

Apa perbedaan antara antarmuka dan kelas abstrak?

Bagaimana seharusnya saya menjelaskan perbedaan antara Interface dan kelas Abstrak?

Lihat halaman dokumentasi untuk memahami fitur-fitur baru yang ditambahkan di java 8: metode default dan metode statis .

Ravindra babu
sumber
Saya menghapus tag java-8 karena pertanyaannya tidak menanyakan apa pun tentang java-8 (dan sebenarnya sudah lama ditanyakan sebelum java-8). Tag adalah untuk pertanyaan, bukan untuk jawaban.
Tagir Valeev
2

Tujuan dari antarmuka adalah abstraksi , atau decoupling dari implementasi.

Jika Anda memperkenalkan abstraksi dalam program Anda, Anda tidak peduli dengan kemungkinan implementasi. Anda tertarik pada apa yang bisa dilakukan dan bukan bagaimana , dan Anda menggunakan interfaceuntuk mengekspresikan ini di Jawa.

Eljenso
sumber
Tujuan dari semua pemrograman terstruktur adalah abstraksi. Mengapa Anda mengatakan bahwa tujuan antarmuka adalah abstraksi, karena saya dapat mencapai hal yang sama persis menggunakan generik dan komposisi kelas?
Apocalisp
1
Jika semua pemrograman terstruktur adalah abstraksi (klaim Anda), maka antarmuka adalah abstraksi dalam abstraksi itu.
eljenso
1

Jika Anda memiliki CardboardBox dan HtmlBox (keduanya mengimplementasikan IBox), Anda bisa meneruskan keduanya ke metode apa pun yang menerima IBox. Meskipun keduanya sangat berbeda dan tidak sepenuhnya dapat dipertukarkan, metode yang tidak mempedulikan "buka" atau "mengubah ukuran" masih dapat menggunakan kelas Anda (mungkin karena mereka peduli tentang berapa banyak piksel yang diperlukan untuk menampilkan sesuatu di layar).

Todd R
sumber
1

Antarmuka tempat fetature ditambahkan ke java untuk memungkinkan pewarisan berganda. Pengembang Java meskipun saya menyadari bahwa memiliki banyak warisan adalah fitur "berbahaya", itulah sebabnya muncul dengan ide antarmuka.

multiple inheritance berbahaya karena Anda mungkin memiliki kelas seperti berikut:


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
}

Yang akan menjadi metode yang harus dipanggil saat kita gunakan


   FunckyFigure.GetArea(); 

Semua masalah diselesaikan dengan antarmuka, karena Anda tahu Anda dapat memperluas antarmuka dan bahwa mereka tidak akan memiliki metode klasifikasi ... tentu saja kompilernya bagus dan memberi tahu Anda jika Anda tidak menerapkan metode, tetapi saya suka berpikir bahwa itu adalah efek samping dari ide yang lebih menarik.

mandel
sumber
Anda mungkin ingin membuat perbedaan antara warisan implementasi multipel dan warisan antarmuka multipel dalam jawaban Anda, jika tidak maka akan membingungkan.
eljenso
0

Inilah pemahaman saya tentang keunggulan antarmuka. Koreksi saya jika saya salah. Bayangkan kita sedang mengembangkan OS dan tim lain sedang mengembangkan driver untuk beberapa perangkat. Jadi kami telah mengembangkan antarmuka StorageDevice. Kami memiliki dua implementasi (FDD dan HDD) yang disediakan oleh tim pengembang lain.

Kemudian kita memiliki kelas OperatingSystem yang dapat memanggil metode antarmuka seperti saveData hanya dengan melewatkan instance kelas yang mengimplementasikan antarmuka StorageDevice.

Keuntungannya di sini adalah kita tidak peduli dengan implementasi antarmuka. Tim lain akan melakukan pekerjaan itu dengan mengimplementasikan antarmuka StorageDevice.

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...");    
    }
}
Vladimir Georgiev
sumber
hal yang sama dapat dilakukan dengan kelas abstrak StorageDevice dan FDD dan kelas HDD memperluas kelas StorageDevice. tetapi jika kita menggunakan kelas abstrak kita tidak bisa mengambil keuntungan dari banyak pewarisan.
Vladimir Georgiev