Aynı yöntemle bir sınıfta iki arayüz uygulamak. Hangi arayüz yöntemi geçersiz kılınır?


235

Aynı yöntem adlarına ve imzalarına sahip iki arabirim. Ancak tek bir sınıf tarafından uygulandığında, derleyici hangi arayüz için hangi yöntemin ne olduğunu nasıl belirleyecektir?

Ör:

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

Yanıtlar:


337

Bir tür iki arabirim uygular ve her biri interfaceaynı imzası olan bir yöntem tanımlarsa, aslında tek bir yöntem vardır ve bunlar ayırt edilemez. Örneğin, iki yöntemin çakışan dönüş türleri varsa, bu bir derleme hatası olacaktır. Bu genel kalıtım kuralı, yöntem geçersiz kılma, gizleme ve bildirimlerdir ve sadece kalıtsal 2 interfaceyöntem arasındaki olası çatışmalar için değil , aynı zamanda bir interfaceve bir süper classyöntem veya hatta jeneriklerin silinmesi nedeniyle oluşan çatışmalar için de geçerlidir.


Uyumluluk örneği

Burada interface Gift, bir present()yöntemi olan (hediyeler sunulduğu gibi) ve ayrıca bir yöntemi interface Guestolan bir de present()(misafir olduğu gibi ve yok) olduğu bir örnek var.

Presentable johnnyhem a hem de Gifta 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!!!"
    }
}

Yukarıdaki kod parçası derlenir ve çalışır.

Sadece bir tane @Override gerekli olduğunu unutmayın !!! . Bunun sebebi Gift.present()ve Guest.present()" @Overrideeşdeğerdir" ( JLS 8.4.2 ).

Böylece, johnny tek bir uygulama vardır ve present(), ve bunu nasıl ele fark etmez johnnyister bir şekilde, Giftya da bir şekilde Guest, çağırmak için tek bir yöntem yoktur.


Uyumsuzluk örneği

Aşağıda, devralınan iki yöntemin DEĞİL olduğu bir örnek verilmiştir @Override:

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

Bu ayrıca üyelerin bir üyeden miras interfacealınmasının üye beyanlarının genel kuralına uyması gerektiğini de yineler . Burada uyumsuz dönüş türlerine sahibiz Giftve Guesttanımladık present(): biri voiddiğeri boolean. Aynı türde bir void present()ve bir arada olamaz boolean present(), bu örnek bir derleme hatasıyla sonuçlanır.


özet

@OverrideEşdeğer olan yöntemleri, geçersiz kılma ve gizleme yöntemlerinin olağan gereksinimlerine tabi olarak devralabilirsiniz . Onlar bu yana OLAN @Override -eşdeğer etkin uygulamak için yalnızca bir yöntem yoktur ve dolayısıyla seçim / ayırt etmek bir şey yok.

Derleyicinin hangi yöntem için hangi arabirim olduğunu tanımlaması gerekmez, çünkü bir kez @Overrideeşdeğer olduklarına karar verildikten sonra aynı yöntemdir.

Potansiyel uyumsuzlukları çözmek zor bir iş olabilir, ancak bu tamamen başka bir konudur.

Referanslar


Teşekkürler - bu yardımcı oldu. Ancak, ben olarak gönderdiniz uyumsuzluk üzerinde bir başka soru vardı yeni soru
amaidment

2
BTW Bu default, Java 8'deki yöntemlerin desteği ile biraz değişir .
Peter Lawrey

Potansiyel uyumsuzlukları çözmek için kompozit sınıflar hile olabilir :), ancak hiç böyle bir sorun yaşamadım ve yine de olabileceği açıktır.
Kova Gücü

1
Bu makale hediyeler için kullanılabilecek bir tasarım deseni biraz Arayüz'ü Çarpışan iki uygulamak için gereken durumla başa söylemek Foove Bar. Temelde, sınıfınızın arayüzlerden birini uygulamanız ve ikinci arayüzü uygulayan bir iç sınıfa dönmek Fooiçin bir Bar asBar()yöntem sağlamanız gerekir Bar. Sınıfınız nihayetinde "Bar" olmadığı için mükemmel değil, ancak bazı durumlarda yararlı olabilir.
Javaru

1
bir java geliştiricisi im ama c # bu konuda gerçekten daha akıllı: stackoverflow.com/questions/2371178/…
Amir Ziarati

25

Bu, bu sorunun kopyası olarak işaretlendi /programming/24401064/understanding-and-solving-the-diamond-problems-in-java

Birden fazla kalıtım sorunu elde etmek için Java 8'e ihtiyacınız var, ancak yine de böyle bir sorun değil.

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.

JB Nizet'in söylediği gibi, bunu geçersiz kılmamı düzeltebilirsiniz.

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

Ancak, bir sorununuz yok

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.

vay. bu benim için yeni. Java 8'de neden varsayılan oluşturmaları gerekiyordu?
Erran Morad

1
Kod tabanının% 60'ını bozmadan arabirimlere (özellikle koleksiyon arabirimleri) yeni yöntemler eklemeyi kolaylaştırmak için.
Tassos Bassoukos

@BoratSagdiyev En büyük neden kapanışları desteklemek ve daha kullanışlı hale getirmekti. Bkz. Collection.stream (). List.sort göz at () docs.oracle.com/javase/8/docs/api/java/util/... herhangi bir spesifik uygulama değiştirmek zorunda kalmadan Yetkililer, bütün Listeler için bir yöntem ekledik. Yararlı Collection.removeIf ()
eklediler

@TassosBassoukos +1 kendi Liste uygulamanız olduğunu söylüyor, şimdi myList.stream () veya myList.sort () kodunuzu değiştirmeden yapabilirsiniz
Peter Lawrey

3
@PeterLawrey: AB derlenmeyecek çünkü geçersiz kılmak hi()zorunda (belirsizliği düzeltmek için). Örneğin, A.super.hi()A ile aynı şekilde uygulamayı seçerek uygulayarak
JB Nizet

20

Derleyici ile ilgili olarak, bu iki yöntem aynıdır. Her ikisinin de bir uygulaması olacak.

İki yöntem etkili bir şekilde aynı ise, aynı uygulamaya sahip olmaları gerektiğinde bu bir problem değildir. Sözleşmeye bağlı olarak farklılarsa (her arabirim için dokümantasyona göre), sorun yaşarsınız.


2
Java'nın neden birden fazla sınıfı genişletmenize izin vermediğini açıklar
Arthur Ronald

1
@ArthurRonald, aslında sadece ilgili görünüyor. Ancak, birden fazla sınıfı genişleten IMO sınıfı, Diamond Problem'e (en çok türetilmiş sınıfta yinelenen nesne durumu) girebilir ve Java'nın kullanıcılarını sorunlardan uzaklaştırmasının nedeni budur. Öte yandan, birden fazla sınıf uygulayan sınıf hiçbir zaman Diamond Problemine giremez çünkü arayüz nesnelere durum sağlamaz. Ve sorun tamamen sözdizimi sınırlamalarından kaynaklanıyor - işlev çağrısını tam olarak nitelendirememe.
uvsmtid

13

Tanımlanacak bir şey yok. Arabirimler yalnızca bir yöntem adı ve imzası yasaklar. Her iki arabirimin de tam olarak aynı ad ve imzayı içeren bir yöntemi varsa, uygulayıcı sınıf her iki arabirim yöntemini tek bir somut yöntemle uygulayabilir.

Ancak, iki arayüz yönteminin anlamsal sözleşmeleri çelişiyorsa, hemen hemen kaybettiniz; o zaman her iki arabirimi tek bir sınıfta uygulayamazsınız.


4

Arabirimi anonim olarak uygulamayı deneyin.

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(){

}

}

4

Arayüzde olduğu gibi, sadece her iki arayüzü uygulayan somut sınıfın sadece bir yöntem olduğunu (her ikisinin de dönüş tipinde aynı ada sahip olduğunu açıkladığınız) olduğunu açıklıyoruz. bu yüzden onunla ilgili bir sorun olmamalıdır. Bu yöntemi somut sınıfta tanımlayabileceksiniz.

Ancak iki arabirim aynı ada ancak farklı dönüş türüne sahip bir yönteme sahip olduğunda ve somut sınıfta iki yöntem uyguladığınızda:

Lütfen aşağıdaki koda bakın:

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

derleyici "public void print ()" yöntemini aldığında önce InterfaceA'ya bakar ve onu alır.Ancak yine de dönüş türünün InterfaceB yöntemiyle uyumlu olmadığı derleme zamanı hatası verir.

Yani derleyici için haywire gider.

Bu şekilde, aynı ada ancak farklı dönüş türüne sahip bir yöntemi olan iki arabirimi uygulayamazsınız.


3

İkisi de aynıysa önemli değil. Arayüz yöntemi başına her ikisini de tek bir somut yöntemle uygular.

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.