Menerapkan dua antarmuka dalam satu kelas dengan metode yang sama. Metode antarmuka mana yang diganti?

235

Dua antarmuka dengan nama dan tanda tangan metode yang sama. Tetapi diimplementasikan oleh satu kelas lalu bagaimana kompiler akan mengidentifikasi metode mana untuk antarmuka yang mana?

Ex:

interface A{
  int f();
}

interface B{
  int f();
}

class Test implements A, B{   
  public static void main(String... args) throws Exception{   

  }

  @Override
  public int f() {  // from which interface A or B
    return 0;
  }
}   
Jothi
sumber

Jawaban:

337

Jika suatu tipe mengimplementasikan dua antarmuka, dan masing-masing interfacemendefinisikan metode yang memiliki tanda tangan yang identik, maka pada dasarnya hanya ada satu metode, dan mereka tidak dapat dibedakan. Jika, katakanlah, kedua metode memiliki tipe pengembalian yang saling bertentangan, maka itu akan menjadi kesalahan kompilasi. Ini adalah aturan umum tentang pewarisan, metode pengesampingan, penyembunyian, dan deklarasi, dan berlaku juga untuk kemungkinan konflik tidak hanya antara 2 interfacemetode yang diwariskan , tetapi juga metode interfacedan super class, atau bahkan hanya konflik karena penghapusan jenis obat generik.


Contoh kompatibilitas

Berikut adalah contoh di mana Anda memiliki interface Gift, yang memiliki present()metode (seperti dalam, menyajikan hadiah), dan juga interface Guest, yang juga memiliki present()metode (seperti dalam, tamu hadir dan tidak absen).

Presentable johnnykeduanya a Giftdan a Guest.

public class InterfaceTest {
    interface Gift  { void present(); }
    interface Guest { void present(); }

    interface Presentable extends Gift, Guest { }

    public static void main(String[] args) {
        Presentable johnny = new Presentable() {
            @Override public void present() {
                System.out.println("Heeeereee's Johnny!!!");
            }
        };
        johnny.present();                     // "Heeeereee's Johnny!!!"

        ((Gift) johnny).present();            // "Heeeereee's Johnny!!!"
        ((Guest) johnny).present();           // "Heeeereee's Johnny!!!"

        Gift johnnyAsGift = (Gift) johnny;
        johnnyAsGift.present();               // "Heeeereee's Johnny!!!"

        Guest johnnyAsGuest = (Guest) johnny;
        johnnyAsGuest.present();              // "Heeeereee's Johnny!!!"
    }
}

Cuplikan di atas mengkompilasi dan menjalankan.

Perhatikan bahwa hanya ada satu yang @Override diperlukan !!! . Ini karena Gift.present()dan Guest.present()" @Override-equivalent" ( JLS 8.4.2 ).

Dengan demikian, johnny hanya memiliki satu implementasi dari present(), dan tidak peduli bagaimana Anda memperlakukan johnny, apakah sebagai Giftatau sebagai Guest, hanya ada satu metode untuk memohon.


Contoh ketidakcocokan

Berikut adalah contoh di mana kedua metode yang diwariskan TIDAK @Override-setara:

public class InterfaceTest {
    interface Gift  { void present(); }
    interface Guest { boolean present(); }

    interface Presentable extends Gift, Guest { } // DOES NOT COMPILE!!!
    // "types InterfaceTest.Guest and InterfaceTest.Gift are incompatible;
    //  both define present(), but with unrelated return types"
}

Ini lebih lanjut menegaskan bahwa mewarisi anggota dari interfaceharus mematuhi aturan umum deklarasi anggota. Di sini kita memiliki Giftdan Guestmendefinisikan present()dengan jenis pengembalian yang tidak kompatibel: satu sama voidlain boolean. Untuk alasan yang sama bahwa Anda tidak bisa void present()dan boolean present()dalam satu tipe, contoh ini menghasilkan kesalahan kompilasi.


Ringkasan

Anda dapat mewarisi metode-metode yang sama @Override, tunduk pada persyaratan metode override dan persembunyian yang biasa. Karena mereka ADALAH @Override -setara, efektif hanya ada satu metode untuk melaksanakan, dan dengan demikian tidak ada yang membedakan / memilih dari.

Compiler tidak harus mengidentifikasi metode mana untuk antarmuka yang mana, karena begitu mereka ditentukan- @Overridesama, mereka adalah metode yang sama.

Menyelesaikan potensi ketidakcocokan mungkin merupakan tugas yang rumit, tapi itu masalah lain sama sekali.

Referensi

polygenelubricants
sumber
Terima kasih - ini sangat membantu. Namun, saya memiliki pertanyaan lebih lanjut tentang ketidakcocokan, yang saya posting sebagai pertanyaan baru
takjub
2
BTW Ini sedikit berubah dengan dukungan defaultmetode di Jawa 8.
Peter Lawrey
Kelas komposit untuk menyelesaikan potensi ketidaksesuaian mungkin trik :), tapi, saya tidak pernah punya masalah seperti itu, dan masih terbukti itu mungkin terjadi.
Aquarius Power
1
Ini artikel hadiah pola desain yang dapat digunakan untuk agak menghadapi situasi di mana Anda perlu untuk mengimplementasikan dua Bertabrakan Interface, mengatakan Foodan Bar. Pada dasarnya Anda meminta kelas Anda mengimplementasikan salah satu antarmuka, misalnya Foo, dan memberikan Bar asBar()metode untuk mengembalikan kelas dalam yang mengimplementasikan Barantarmuka kedua . Tidak sempurna karena kelas Anda pada akhirnya bukan "Bar", tetapi bisa berguna dalam beberapa keadaan.
Javaru
1
Saya seorang pengembang java tetapi c # benar-benar lebih pintar dalam hal ini: stackoverflow.com/questions/2371178/…
Amir Ziarati
25

Ini ditandai sebagai duplikat untuk pertanyaan ini /programming/24401064/understanding-and-solving-the-diamond-problems-in-java

Anda perlu Java 8 untuk mendapatkan masalah pewarisan berganda, tetapi itu masih bukan masalah diamon seperti itu.

interface A {
    default void hi() { System.out.println("A"); }
}

interface B {
    default void hi() { System.out.println("B"); }
}

class AB implements A, B { // won't compile
}

new AB().hi(); // won't compile.

Seperti yang dikomentari JB Nizet, Anda dapat memperbaikinya dengan mengesampingkan saya.

class AB implements A, B {
    public void hi() { A.super.hi(); }
}

Namun, Anda tidak memiliki masalah dengan itu

interface D extends A { }

interface E extends A { }

interface F extends A {
    default void hi() { System.out.println("F"); }
}

class DE implement D, E { }

new DE().hi(); // prints A

class DEF implement D, E, F { }

new DEF().hi(); // prints F as it is closer in the heirarchy than A.
Peter Lawrey
sumber
Wow. ini baru bagiku. Mengapa mereka harus membuat default di java 8?
Erran Morad
1
Untuk memfasilitasi menambahkan metode baru ke antarmuka (khususnya antarmuka koleksi) tanpa melanggar 60% dari basis kode.
Tassos Bassoukos
@ BoratSagdiyev Alasan terbesar adalah untuk mendukung penutupan dan menjadikannya lebih bermanfaat. Lihat Collection.stream (). Lihat List.sort () docs.oracle.com/javase/8/docs/api/java/util/… Mereka telah menambahkan metode untuk semua Daftar, tanpa harus mengubah implementasi spesifik apa pun. Mereka menambahkan Collection.removeIf () yang berguna
Peter Lawrey
@TassosBassoukos +1 mengatakan Anda memiliki implementasi Daftar Anda sendiri, sekarang Anda dapat myList.stream () atau myList.sort () tanpa mengubah kode Anda
Peter Lawrey
3
@PeterLawrey: AB tidak akan mengkompilasi karena harus menimpa hi()(untuk memperbaiki ambiguitas). Misalnya, dengan mengimplementasikannya A.super.hi()untuk memilih untuk mengimplementasikannya dengan cara yang sama dengan A.
JB Nizet
20

Sejauh menyangkut kompiler, kedua metode itu identik. Akan ada satu implementasi dari keduanya.

Ini bukan masalah jika kedua metode ini identik secara efektif, karena keduanya harus memiliki implementasi yang sama. Jika mereka berbeda secara kontraktual (sesuai dokumentasi untuk setiap antarmuka), Anda akan bermasalah.

Abu
sumber
2
Ini menjelaskan mengapa Java tidak memungkinkan Anda memperluas lebih dari satu kelas
Arthur Ronald
1
@ArthurRonald, sebenarnya itu hanya terkait. Namun, IMO, kelas yang memanjang lebih dari satu kelas dapat berjalan ke Diamond Problem (yang diduplikasi keadaan objek di kelas yang paling diturunkan) dan itu kemungkinan besar mengapa Java membawa penggunanya menjauh dari masalah. Di sisi lain, kelas yang mengimplementasikan lebih dari satu kelas tidak akan pernah bisa mengalami Masalah Berlian hanya karena antarmuka tidak menyediakan keadaan objek. Dan masalahnya adalah murni karena batasan sintaksis - ketidakmampuan untuk memenuhi panggilan fungsi sepenuhnya.
uvsmtid
13

Tidak ada yang bisa diidentifikasi. Antarmuka hanya melarang nama metode dan tanda tangan. Jika kedua antarmuka memiliki metode dengan nama dan tanda tangan yang persis sama, kelas pelaksana dapat mengimplementasikan kedua metode antarmuka dengan metode konkret tunggal.

Namun, jika kontrak semantik dari kedua metode antarmuka saling bertentangan, Anda telah kehilangan banyak hal; Anda tidak dapat mengimplementasikan kedua antarmuka dalam satu kelas saja.

Michael Borgwardt
sumber
4

Coba terapkan antarmuka sebagai anonim.

public class MyClass extends MySuperClass implements MyInterface{

MyInterface myInterface = new MyInterface(){

/* Overrided method from interface */
@override
public void method1(){

}

};

/* Overrided method from superclass*/
@override
public void method1(){

}

}
dcanh121
sumber
4

Seperti dalam antarmuka, kita hanya mendeklarasikan metode, kelas konkret yang mengimplementasikan kedua antarmuka ini mengerti bahwa hanya ada satu metode (seperti yang Anda jelaskan keduanya memiliki nama yang sama dalam tipe pengembalian). jadi seharusnya tidak ada masalah dengan itu. Anda akan dapat mendefinisikan metode itu di kelas beton.

Tetapi ketika dua antarmuka memiliki metode dengan nama yang sama tetapi jenis kembali berbeda dan Anda menerapkan dua metode di kelas beton:

Silakan lihat kode di bawah ini:

public interface InterfaceA {
  public void print();
}


public interface InterfaceB {
  public int print();
}

public class ClassAB implements InterfaceA, InterfaceB {
  public void print()
  {
    System.out.println("Inside InterfaceA");
  }
  public int print()
  {
    System.out.println("Inside InterfaceB");
    return 5;
  }
}

ketika kompiler mendapatkan metode "public void print ()" pertama kali terlihat di InterfaceA dan mendapatkannya. Tapi tetap saja itu memberikan kesalahan waktu kompilasi bahwa tipe pengembalian tidak kompatibel dengan metode InterfaceB.

Jadi itu menjadi rusak untuk kompiler.

Dengan cara ini, Anda tidak akan dapat mengimplementasikan dua antarmuka yang memiliki metode dengan nama yang sama tetapi tipe pengembalian yang berbeda.

Bhagrav Jain
sumber
3

Ya, jika keduanya sama, itu tidak masalah. Ini mengimplementasikan keduanya dengan metode beton tunggal per metode antarmuka.

Paul Whelan
sumber