동일한 메소드를 사용하여 클래스에서 두 개의 인터페이스를 구현합니다. 어떤 인터페이스 방법이 재정의됩니까?


235

메소드 이름과 서명이 동일한 두 개의 인터페이스. 그러나 단일 클래스로 구현되면 컴파일러가 어떤 인터페이스가 어떤 인터페이스인지 식별하는 방법은 무엇입니까?

전의:

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

답변:


337

유형이 두 개의 인터페이스를 구현하고 각각 interface동일한 서명을 가진 메소드를 정의하는 경우 실제로는 하나의 메소드 만 있으며 구별 할 수 없습니다. 예를 들어 두 메소드가 리턴 유형이 충돌하면 컴파일 오류가됩니다. 이것은 상속, 메소드 재정의, 숨기기 및 선언의 일반적인 규칙이며, 상속 된 두 interface메소드뿐만 아니라 interface슈퍼 class메소드 사이의 가능한 충돌에도 적용 되거나 제네릭의 유형 삭제로 인한 충돌에도 적용됩니다.


호환성 예

여기에 (선물을 제시하는 것과 같은) 방법 interface Gift이있는 present()및가 있는 예가 interface Guest있습니다.present() (, 게스트가없는 현재와하지에로) 방법을.

Presentable johnnya Gift와 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!!!"
    }
}

위의 스 니펫은 컴파일되고 실행됩니다.

참고 하나가 @Override 필요합니다! . 이 때문입니다 Gift.present()Guest.present()"이다 @Override(-equivalent" JLS 8.4.2 ).

따라서의 johnny 구현하나만 있으며 a로 또는 a 로 present()처리하는 방법은 중요하지 않습니다 . 호출 할 메소드는 하나뿐입니다.johnnyGiftGuest


비 호환성 예

다음은 상속 된 두 메소드가 동일하지 않은 예입니다 @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"
}

이것은 또한 멤버로부터 상속받는 멤버 interface가 멤버 선언의 일반적인 규칙을 준수해야 함을 반복합니다 . 여기에서 우리는이 GiftGuest정의 present()호환되지 않는 반환 형식으로 하나 void다른 boolean. 같은 이유로 당신은 할 수 없다는 void present()하고 boolean present()한 가지 유형에서, 컴파일 오류이 예의 결과.


요약

@Override메서드 재정의 및 숨기기의 일반적인 요구 사항에 따라 동등한 메서드를 상속 할 수 있습니다 . 그들은 이후 내용입니다 @Override -equivalent 효과적으로 구현하는 하나의 방법이있다, 따라서 선택 / 구별 할 것도 없다.

컴파일러는 일단 어떤 인터페이스가 어떤 인터페이스인지 결정하지 않아도됩니다. @Override 동일한 되면 동일한 메소드 입니다.

잠재적 인 비 호환성을 해결하는 것은 까다로운 작업 일 수 있지만 이는 또 다른 문제입니다.

참고 문헌


감사합니다-도움이되었습니다. 그러나 호환성 문제에 대한 추가 질문이있었습니다. 새로운 질문으로
amaidment

2
BTW이의 지원을 조금 변경 default자바 (8)의 방법
피터 Lawrey

잠재적 비 호환성을 해결하는 복합 클래스는 속임수 일 수 있습니다.) 그러나 그런 문제는 없었지만 여전히 발생할 수 있습니다.
물병 자리 힘

1
기사에서는 두 개의 충돌 인터페이스 (예 : 및) 를 구현해야하는 상황 을 다소 처리하는 데 사용할 수있는 디자인 패턴을 제시합니다 . 기본적으로 클래스는 인터페이스 중 하나 를 구현하고 두 번째 인터페이스 를 구현하는 내부 클래스를 반환하는 메소드를 제공합니다 . 수업이 "바"가 아니기 때문에 완벽하지는 않지만 일부 상황에서는 유용 할 수 있습니다. FooBarFooBar asBar()Bar
Javaru

1
자바 개발자 메신저하지만 C #을이에 정말 더 똑똑 : stackoverflow.com/questions/2371178/...
아미르 Ziarati

25

이것은이 질문에 중복으로 표시되었습니다 /programming/24401064/understanding-and-solving-the-diamond-problems-in-java

다중 상속 문제를 얻으려면 Java 8이 필요하지만 여전히 그러한 문제는 아닙니다.

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가 언급 했듯이이 문제를 해결할 수 있습니다.

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

그러나, 당신은 문제가 없습니다

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.

와. 이것은 나에게 새로운 것입니다. 왜 Java 8에서 기본값을 만들어야 했습니까?
Erran Morad

1
코드베이스의 60 %를 손상시키지 않으면 서 인터페이스 (특히 컬렉션 인터페이스)에 새로운 메소드를 쉽게 추가 할 수 있습니다.
Tassos Bassoukos

@BoratSagdiyev 가장 큰 이유는 클로제를 지원하고 더 유용하게 만드는 것이 었습니다. Collection.stream ()을 참조하십시오. List.sort () docs.oracle.com/javase/8/docs/api/java/util/…을 살펴보십시오 . 특정 구현을 변경하지 않고도 모든 List에 대한 메소드를 추가했습니다. 유용한 Collection.removeIf ()를 추가했습니다
Peter Lawrey

@TassosBassoukos +1은 당신이 자신의 List 구현을 가지고 있다고 말합니다. 이제 코드를 변경하지 않고 myList.stream () 또는 myList.sort () 할 수 있습니다
Peter Lawrey

3
@PeterLawrey : AB는 hi()(모호성을 수정하기 위해) 재정의해야하기 때문에 컴파일하지 않습니다 . 예를 들어, A.super.hi()A와 동일한 방식으로 구현하도록 구현함으로써 구현 됩니다.
JB Nizet

20

컴파일러에 관한 한이 두 가지 방법은 동일합니다. 두 가지 모두 한 가지 구현이있을 것입니다.

두 메소드가 동일한 구현을 가져야한다는 점에서 두 메소드가 효과적으로 동일한 경우에는 문제가되지 않습니다. 계약이 다르면 (각 인터페이스의 설명서에 따라) 문제가 생길 수 있습니다.


2
그것은 자바를 허용하지 않는 이유를 설명 확장 이상의 클래스
아서 로널드

1
@ArthurRonald, 실제로는 관련이 있습니다. 그러나 두 개 이상의 클래스를 확장 하는 IMO 클래스는 다이아몬드 문제 (가장 파생 된 클래스에서 복제 된 객체 상태)에 빠질 수 있으므로 Java가 사용자를 문제에서 멀어지게 만들 가능성이 높습니다. 반면에 하나 이상의 클래스를 구현 하는 클래스는 인터페이스가 객체에 상태를 제공하지 않기 때문에 절대 다이아몬드 문제에 빠질 수 없습니다. 그리고 문제는 순전히 구문 제한 때문에 함수 호출을 완전히 규정 할 수 없기 때문입니다.
uvsmtid

13

식별 할 것이 없습니다. 인터페이스는 메소드 이름과 서명 만 제공합니다. 두 인터페이스 모두 정확히 동일한 이름과 서명의 메소드를 갖는 경우, 구현 클래스는 하나의 구체적 메소드로 두 인터페이스 메소드를 모두 구현할 수 있습니다.

그러나 두 인터페이스 방법 의 의미 적 계약이 모순되는 경우 거의 손실 된 것입니다. 그러면 단일 클래스에서 두 인터페이스를 모두 구현할 수 없습니다.


4

익명으로 인터페이스를 구현하십시오.

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

인터페이스에서와 같이 우리는 단지 메소드를 선언하고 있습니다.이 두 인터페이스를 모두 구현하는 콘크리트 클래스는 하나의 메소드 만 있다는 것을 이해합니다 (둘 다 리턴 유형에서 동일한 이름을 가짐). 문제가 없어야합니다. 구체적인 클래스에서 해당 메소드를 정의 할 수 있습니다.

그러나 두 개의 인터페이스가 이름은 같지만 리턴 유형이 다른 메소드를 가지고 있고 구체적인 클래스에서 두 가지 메소드를 구현 한 경우 :

아래 코드를보십시오 :

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

컴파일러가 "public void print ()"메소드를 가져 오면 먼저 InterfaceA를 찾아서 가져옵니다. 그러나 여전히 리턴 유형이 InterfaceB의 메소드와 호환되지 않는 컴파일 시간 오류가 발생합니다.

그래서 컴파일러를 위해 haywire로갑니다.

이런 방식으로 이름은 같지만 리턴 유형이 다른 메소드를 갖는 두 개의 인터페이스를 구현할 수 없습니다.


3

둘 다 같으면 문제가되지 않습니다. 인터페이스 방법마다 하나의 구체적인 방법으로 두 가지를 모두 구현합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.