Jika saya memiliki dua antarmuka, keduanya sangat berbeda dalam tujuannya, tetapi dengan tanda tangan metode yang sama, bagaimana cara membuat kelas menerapkan keduanya tanpa dipaksa untuk menulis satu metode yang berfungsi untuk kedua antarmuka dan menulis beberapa logika yang berbelit-belit dalam metode tersebut implementasi yang memeriksa jenis objek yang panggilan itu dibuat dan memanggil kode yang tepat?
Dalam C #, ini diatasi dengan apa yang disebut sebagai implementasi antarmuka eksplisit. Apakah ada cara yang setara di Java?
Jawaban:
Tidak, tidak ada cara untuk mengimplementasikan metode yang sama dengan dua cara berbeda dalam satu kelas di Java.
Itu dapat menyebabkan banyak situasi yang membingungkan, itulah sebabnya Java tidak mengizinkannya.
interface ISomething { void doSomething(); } interface ISomething2 { void doSomething(); } class Impl implements ISomething, ISomething2 { void doSomething() {} // There can only be one implementation of this method. }
Apa yang dapat Anda lakukan adalah membuat kelas dari dua kelas yang masing-masing menerapkan antarmuka yang berbeda. Kemudian satu kelas itu akan memiliki perilaku kedua antarmuka.
class CompositeClass { ISomething class1; ISomething2 class2; void doSomething1(){class1.doSomething();} void doSomething2(){class2.doSomething();} }
sumber
ISomething1 CompositeClass.asInterface1();
danISomething2 CompositeClass.asInterface2();
ke kelas itu. Kemudian Anda bisa mengeluarkan satu atau yang lain dari kelas gabungan. Tidak ada solusi yang bagus untuk masalah ini.public long getCountAsLong() implements interface2.getCount {...}
[jika antarmuka memerlukanlong
tetapi pengguna kelas mengharapkanint
] atauprivate void AddStub(T newObj) implements coolectionInterface.Add
[mengasumsikancollectionInterface
memilikicanAdd()
metode, dan untuk semua contoh kelas ini mengembalikanfalse
]?Tidak ada cara nyata untuk menyelesaikan ini di Java. Anda dapat menggunakan kelas dalam sebagai solusinya:
interface Alfa { void m(); } interface Beta { void m(); } class AlfaBeta implements Alfa { private int value; public void m() { ++value; } // Alfa.m() public Beta asBeta() { return new Beta(){ public void m() { --value; } // Beta.m() }; } }
Meskipun tidak mengizinkan cast dari
AlfaBeta
keBeta
, downcast umumnya jahat, dan jika dapat diharapkan bahwa sebuahAlfa
instance juga sering memilikiBeta
aspek, dan untuk beberapa alasan (biasanya pengoptimalan adalah satu-satunya alasan yang valid) Anda ingin dapat melakukannya untuk mengubahnyaBeta
, Anda dapat membuat sub-antarmukaAlfa
denganBeta asBeta()
di dalamnya.sumber
Jika Anda mengalami masalah ini, kemungkinan besar karena Anda menggunakan warisan di mana Anda harus menggunakan delegasi . Jika Anda perlu menyediakan dua antarmuka yang berbeda, meskipun serupa, untuk model data dasar yang sama, Anda harus menggunakan tampilan untuk menyediakan akses ke data dengan murah menggunakan beberapa antarmuka lain.
Untuk memberikan contoh konkret untuk kasus terakhir, misalkan Anda ingin mengimplementasikan keduanya
Collection
danMyCollection
(yang tidak mewarisi dariCollection
dan memiliki antarmuka yang tidak kompatibel). Anda dapat memberikan fungsiCollection getCollectionView()
danMyCollection getMyCollectionView()
yang menyediakan implementasi ringanCollection
danMyCollection
, menggunakan data dasar yang sama.Untuk kasus sebelumnya ... misalkan Anda benar-benar menginginkan array bilangan bulat dan array string. Alih-alih mewarisi dari keduanya
List<Integer>
danList<String>
, Anda harus memiliki satu anggota tipeList<Integer>
dan anggota tipe lainList<String>
, dan merujuk ke anggota tersebut, daripada mencoba mewarisi dari keduanya. Bahkan jika Anda hanya memerlukan daftar bilangan bulat, lebih baik menggunakan komposisi / delegasi daripada warisan dalam kasus ini.sumber
Masalah Java "klasik" juga memengaruhi pengembangan Android saya ...
Alasannya tampaknya sederhana:
Lebih banyak framework / pustaka yang harus Anda gunakan, lebih mudah hal-hal menjadi tidak terkontrol ...
Dalam kasus saya, saya memiliki kelas BootStrapperApp diwarisi dari android.app.Application ,
sedangkan kelas yang sama juga harus mengimplementasikan antarmuka Platform dari kerangka kerja MVVM agar bisa terintegrasi.
Tabrakan metode terjadi pada metode getString () , yang diumumkan oleh kedua antarmuka dan harus memiliki implementasi yang berbeda dalam konteks yang berbeda.
Solusinya (ugly..IMO) menggunakan kelas dalam untuk mengimplementasikan semua Platformmetode, hanya karena satu konflik tanda tangan metode kecil ... dalam beberapa kasus, metode pinjaman tersebut bahkan tidak digunakan sama sekali (tetapi mempengaruhi semantik desain utama).
Saya cenderung setuju C # -style indikasi konteks / namespace eksplisit membantu.
sumber
Satu-satunya solusi yang terlintas dalam pikiran saya adalah menggunakan objek referece ke objek yang Anda inginkan untuk menanamkan banyak antarmuka.
misalnya: misalkan Anda memiliki 2 antarmuka untuk diterapkan
public interface Framework1Interface { void method(Object o); }
dan
public interface Framework2Interface { void method(Object o); }
Anda dapat menyertakannya ke dalam dua objek Facador:
public class Facador1 implements Framework1Interface { private final ObjectToUse reference; public static Framework1Interface Create(ObjectToUse ref) { return new Facador1(ref); } private Facador1(ObjectToUse refObject) { this.reference = refObject; } @Override public boolean equals(Object obj) { if (obj instanceof Framework1Interface) { return this == obj; } else if (obj instanceof ObjectToUse) { return reference == obj; } return super.equals(obj); } @Override public void method(Object o) { reference.methodForFrameWork1(o); } }
dan
public class Facador2 implements Framework2Interface { private final ObjectToUse reference; public static Framework2Interface Create(ObjectToUse ref) { return new Facador2(ref); } private Facador2(ObjectToUse refObject) { this.reference = refObject; } @Override public boolean equals(Object obj) { if (obj instanceof Framework2Interface) { return this == obj; } else if (obj instanceof ObjectToUse) { return reference == obj; } return super.equals(obj); } @Override public void method(Object o) { reference.methodForFrameWork2(o); } }
Pada akhirnya kelas yang Anda inginkan harus seperti itu
public class ObjectToUse { private Framework1Interface facFramework1Interface; private Framework2Interface facFramework2Interface; public ObjectToUse() { } public Framework1Interface getAsFramework1Interface() { if (facFramework1Interface == null) { facFramework1Interface = Facador1.Create(this); } return facFramework1Interface; } public Framework2Interface getAsFramework2Interface() { if (facFramework2Interface == null) { facFramework2Interface = Facador2.Create(this); } return facFramework2Interface; } public void methodForFrameWork1(Object o) { } public void methodForFrameWork2(Object o) { } }
Anda sekarang dapat menggunakan metode getAs * untuk "mengekspos" kelas Anda
sumber
Anda dapat menggunakan pola Adaptor untuk membuatnya berfungsi. Buat dua adaptor untuk setiap antarmuka dan gunakan itu. Itu harus menyelesaikan masalah.
sumber
Semuanya baik dan bagus bila Anda memiliki kontrol penuh atas semua kode yang dipermasalahkan dan dapat menerapkannya di awal. Sekarang bayangkan Anda memiliki kelas publik yang digunakan di banyak tempat dengan sebuah metode
public class MyClass{ private String name; MyClass(String name){ this.name = name; } public String getName(){ return name; } }
Sekarang Anda perlu meneruskannya ke WizzBangProcessor yang membutuhkan kelas untuk mengimplementasikan WBPInterface ... yang juga memiliki metode getName (), tetapi alih-alih implementasi konkret Anda, antarmuka ini mengharapkan metode untuk mengembalikan nama tipe Pengolahan Wizz Bang.
Dalam C # itu akan menjadi sepele
public class MyClass : WBPInterface{ private String name; String WBPInterface.getName(){ return "MyWizzBangProcessor"; } MyClass(String name){ this.name = name; } public String getName(){ return name; } }
Di Java Tough, Anda harus mengidentifikasi setiap titik dalam basis kode yang telah diterapkan di mana Anda perlu mengonversi dari satu antarmuka ke antarmuka lainnya. Tentu perusahaan WizzBangProcessor seharusnya menggunakan getWizzBangProcessName (), tetapi mereka juga pengembang. Dalam konteks mereka getName baik-baik saja. Sebenarnya, di luar Java, sebagian besar bahasa berbasis OO lainnya mendukung ini. Java jarang memaksa semua antarmuka untuk diimplementasikan dengan metode yang sama NAME.
Kebanyakan bahasa lain memiliki kompilator yang dengan senang hati menerima instruksi untuk mengatakan "metode ini di kelas ini yang cocok dengan tanda tangan metode ini dalam antarmuka yang diimplementasikan ini adalah implementasinya". Bagaimanapun, inti dari mendefinisikan antarmuka adalah untuk memungkinkan definisi disarikan dari implementasi. (Jangan biarkan saya mulai memiliki metode default di Interfaces di Java, apalagi default overriding .... karena tentu saja, setiap komponen yang dirancang untuk mobil jalan raya harus dapat menabrak mobil terbang dan bekerja - hei keduanya adalah mobil ... Saya yakin fungsi default katakanlah sat nav Anda tidak akan terpengaruh dengan input pitch and roll default, karena mobil hanya menguap!
sumber