Java-인터페이스 구현에서 메소드 이름 충돌


88

두 개의 인터페이스가 있는데, 둘 다 목적이 다르지만 동일한 메서드 서명을 사용하는 경우 인터페이스 모두에 사용되는 단일 메서드를 작성하지 않고 메서드에 복잡한 논리를 작성하지 않고 클래스를 둘 다 구현하도록하려면 어떻게해야합니까? 어떤 유형의 객체가 호출되고 있는지 확인하고 적절한 코드를 호출하는 구현?

C #에서 이것은 명시 적 인터페이스 구현이라고하는 것에 의해 극복됩니다. Java에 동등한 방법이 있습니까?


37
하나 개의 클래스는 수행 동일한 서명을 가진 두 가지 방법을 구현해야 다른 일을 다음 클래스가되어, 거의 확실하게 너무 많은 일을합니다.
Joachim Sauer

15
위의 내용은 항상 사실이 아닐 수 있습니다. IMO. 때로는 단일 클래스에서 외부 계약 (따라서 서명 제한)을 확인해야하지만 구현이 다른 메서드가 필요합니다. 사실, 이것들은 사소하지 않은 클래스를 디자인 할 때 일반적인 요구 사항입니다. 오버로딩과 재정의는 반드시 서명이 다르지 않거나 아주 약간 다른 다른 일을하는 메서드를 허용하는 메커니즘입니다. 서명에 약간의 차이.
Bhaskar

1
이 클래스와 메서드가 무엇인지 알고 싶습니다.
Uri

2
레거시 "Address"클래스가 단순히 데이터 모델에서 문자열을 반환하는 getName () 메서드가있는 Person 및 Firm 인터페이스를 구현 한 경우가 발생했습니다. 새로운 비즈니스 요구 사항은 Person.getName ()이 "Surname, Given names"형식의 문자열을 반환하도록 지정했습니다. 많은 논의 끝에 데이터는 대신 데이터베이스에서 다시 형식화되었습니다.
belwood

12
수업이 거의 확실히 너무 많은 일을하고 있다고 말하는 것은 건설적이지 않습니다. 저는 제 클래스가 2 개의 다른 인터페이스에서 mehod 이름 충돌을 가지고 있고 제 클래스가 너무 많은 일을하지 않는 경우가 바로 지금입니다. 목적은 매우 유사하지만 약간 다른 작업을 수행합니다. 질문자가 잘못된 소프트웨어 설계를 구현한다고 비난함으로써 명백히 심각한 장애가있는 프로그래밍 언어를 방어하려고하지 마십시오!
j00hi

답변:


75

아니요, Java의 한 클래스에서 두 가지 다른 방법으로 동일한 메서드를 구현할 수있는 방법이 없습니다.

이는 많은 혼란스러운 상황을 초래할 수 있으며, 이것이 Java가이를 허용하지 않은 이유입니다.

interface ISomething {
    void doSomething();
}

interface ISomething2 {
    void doSomething();
}

class Impl implements ISomething, ISomething2 {
   void doSomething() {} // There can only be one implementation of this method.
}

할 수있는 일은 각각 다른 인터페이스를 구현하는 두 개의 클래스로 클래스를 구성하는 것입니다. 그러면 한 클래스가 두 인터페이스의 동작을 갖게됩니다.

class CompositeClass {
    ISomething class1;
    ISomething2 class2;
    void doSomething1(){class1.doSomething();}
    void doSomething2(){class2.doSomething();}
}

9
그러나 이런 식으로 인터페이스 참조 (ISomething 또는 ISomething2)가 예상되는 어딘가에 CompositeClass의 인스턴스를 전달할 수 없습니다. 클라이언트 코드가 내 인스턴스를 적절한 인터페이스로 캐스트 할 수있을 것이라고 기대할 수도 없습니다.이 제한으로 인해 무언가를 잃어 버리지 않습니까? 또한 이러한 방식으로 각 인터페이스를 실제로 구현하는 클래스를 작성할 때 코드를 단일 클래스로 만드는 이점을 잃어 버릴 수 있으며, 이는 때때로 심각한 장애가 될 수 있습니다.
Bhaskar

9
@Bhaskar, 당신은 유효한 포인트를 만듭니다. 내가 가진 최고의 조언은 그 클래스에 ISomething1 CompositeClass.asInterface1();and ISomething2 CompositeClass.asInterface2();메소드를 추가하는 것입니다. 그런 다음 복합 클래스에서 하나 또는 다른 하나를 가져올 수 있습니다. 하지만이 문제에 대한 훌륭한 해결책은 없습니다.
jjnguy 2010

1
이로 인해 발생할 수있는 혼란스러운 상황에 대해 설명해 주시겠습니까? 메서드 이름에 추가 된 인터페이스 이름을 충돌 / 혼란을 피할 수있는 추가 범위 해결이라고 생각할 수 없습니까?
Bhaskar

@Bhaskar 우리 수업이 단일 책임 원칙을 고수하면 더 좋습니다. 두 개의 매우 다른 인터페이스를 구현하는 클래스가 있으면 단일 책임을 처리하기 위해 클래스를 분할하도록 디자인을 다시 작업해야한다고 생각합니다.
Anirudhan J 2013

1
public long getCountAsLong() implements interface2.getCount {...}[인터페이스에 a가 필요 long하지만 클래스 사용자가 예상하는 경우 int] 또는 private void AddStub(T newObj) implements coolectionInterface.Add[ 메서드 collectionInterface가 있다고 가정 canAdd()하고이 클래스의 모든 인스턴스에 대해 반환하는 경우 false] 와 같은 것을 허용하는 것이 실제로 얼마나 혼란 스러울 까요?
supercat

13

Java에서이 문제를 해결할 실제 방법은 없습니다. 해결 방법으로 내부 클래스를 사용할 수 있습니다.

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

에서 AlfaBeta으로의 캐스트를 허용하지 않지만 Beta다운 캐스트는 일반적으로 악의적이며 Alfa인스턴스에도 종종 Beta측면 이 있을 것으로 예상 될 수 있으며 어떤 이유로 (일반적으로 최적화가 유일한 유효한 이유입니다) 할 수 있기를 원합니다. 로 변환하려면 with Beta의 하위 인터페이스를 만들 수 있습니다.AlfaBeta asBeta()


내부 클래스가 아닌 익명 클래스를 의미합니까?
Zaid Masud

2
@ZaidMasud 내부 클래스를 의미합니다. 왜냐하면 그들은 둘러싸는 개체의 개인 상태에 액세스 할 수 있기 때문입니다. 물론 이러한 내부 클래스는 익명 일 수도 있습니다.
gustafc 2012 년

11

이 문제가 발생하면 delegation 을 사용해야하는 곳에 상속을 사용하고 있기 때문일 가능성이 큽니다 . 동일한 기본 데이터 모델에 대해 비슷하지만 서로 다른 두 개의 인터페이스를 제공 해야하는 경우 다른 인터페이스를 사용하여 데이터에 대한 액세스를 저렴하게 제공하기 위해 를 사용해야합니다 .

후자의 경우에 대한 구체적인 예를 제공하기 위해 CollectionMyCollection(상속되지 않고 Collection호환되지 않는 인터페이스가 있음) 둘 다를 구현한다고 가정합니다 . 동일한 기본 데이터를 사용하여 및 의 간단한 구현을 제공하는 Collection getCollectionView()MyCollection getMyCollectionView()함수를 제공 할 수 있습니다 .CollectionMyCollection

전자의 경우 ... 정말로 정수 배열과 문자열 배열을 원한다고 가정합니다. List<Integer>및 둘 다에서 상속하는 대신 List<String>유형의 멤버 하나와 유형의 List<Integer>다른 멤버가 List<String>있어야하며 둘 다에서 상속하려고하지 말고 해당 멤버를 참조해야합니다. 정수 목록 만 필요하더라도이 경우 상속보다 합성 / 위임을 사용하는 것이 좋습니다.


나는 그렇게 생각하지 않는다. 서로 다른 인터페이스를 구현해야하는 라이브러리를 잊고 있습니다. 충돌하는 여러 라이브러리를 훨씬 더 자주 사용하여이 문제에 부딪 힐 수 있으며 자신의 코드에서 실행할 수 있습니다.
나이트 풀

1
@nightpool 각각 다른 인터페이스가 필요한 여러 라이브러리를 사용하는 경우 단일 개체가 두 인터페이스를 모두 구현할 필요는 없습니다. 두 개의 서로 다른 인터페이스 각각을 반환하기위한 접근자를 개체에 가질 수 있습니다 (그리고 개체를 기본 라이브러리 중 하나로 전달할 때 적절한 접근자를 호출).
마이클 아론 SAFYAN

1

"고전적인"Java 문제는 Android 개발에도 영향을 미칩니다 ...
그 이유는 간단 해 보입니다.
더 많은 프레임 워크 / 라이브러리를 사용해야하며, 더 쉽게 제어 할 수 없습니다

. 제 경우에는 BootStrapperApp 클래스가 있습니다. android.app.Application 에서 상속
되지만 동일한 클래스는 통합을 위해 MVVM 프레임 워크 의 Platform 인터페이스 도 구현해야합니다 . getString () 메서드
에서 메서드 충돌이 발생했습니다.이 메서드는 두 인터페이스에 의해 발표되고 다른 컨텍스트에서 다른 구현을 가져야합니다. 해결 방법 (ugly..IMO)은 내부 클래스를 사용하여 모든 플랫폼 을 구현하는 것입니다.
메서드, 단지 하나의 사소한 메서드 서명 충돌 때문에 ... 어떤 경우에는 그러한 빌린 메서드가 전혀 사용되지도 않습니다 (그러나 주요 디자인 의미에 영향을 미침).
나는 C # 스타일의 명시 적 컨텍스트 / 네임 스페이스 표시가 도움이된다는 데 동의하는 경향이 있습니다.


1
Android 개발에 Java를 사용하기 전에는 C #이 얼마나 사려 깊고 기능이 풍부한 지 깨닫지 못했습니다. 나는 그 C # 기능을 당연하게 여겼다. Java에는 너무 많은 기능이 없습니다.
Damn Vegetables

1

내 마음에 떠오른 유일한 해결책은 다중 인터페이스를 단순화하려는 객체에 대한 참조 객체를 사용하는 것입니다.

예 : 구현할 인터페이스가 2 개 있다고 가정합니다.

public interface Framework1Interface {

    void method(Object o);
}

public interface Framework2Interface {
    void method(Object o);
}

두 개의 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);
    }
}

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

결국 당신이 원하는 수업은 다음과 같아야합니다.

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

이제 getAs * 메소드를 사용하여 클래스를 "노출"할 수 있습니다.


0

이러한 작업을 수행하기 위해 어댑터 패턴을 사용할 수 있습니다. 각 인터페이스에 대해 두 개의 어댑터를 만들고 사용하십시오. 문제가 해결되어야합니다.


-1

문제의 모든 코드를 완전히 제어 할 수 있고이를 미리 구현할 수있을 때 모두 훌륭하고 좋습니다. 이제 메서드를 사용하여 여러 곳에서 사용되는 기존 공용 클래스가 있다고 가정합니다.

public class MyClass{

    private String name;

    MyClass(String name){
        this.name = name;
    }

    public String getName(){
        return name;
    }
}

이제 WBPInterface ...를 구현하는 클래스가 필요한 선반 WizzBangProcessor로 전달해야합니다. WBPInterface ...도 getName () 메서드가 있지만 구체적인 구현 대신이 인터페이스는 메서드가 형식의 이름을 반환 할 것으로 예상합니다. Wizz Bang Processing의.

C #에서는 trvial입니다.

public class MyClass : WBPInterface{

    private String name;

    String WBPInterface.getName(){
        return "MyWizzBangProcessor";
    }

    MyClass(String name){
        this.name = name;
    }

    public String getName(){
        return name;
    }
}

Java Tough에서는 한 인터페이스에서 다른 인터페이스로 변환해야하는 기존 배포 된 코드베이스의 모든 지점을 식별해야합니다. 물론 WizzBangProcessor 회사는 getWizzBangProcessName ()을 사용 했어야하지만 그들도 개발자입니다. 그들의 맥락에서 getName은 괜찮 았습니다. 실제로 Java 외부에서는 대부분의 다른 OO 기반 언어가이를 지원합니다. Java는 모든 인터페이스가 동일한 메소드 NAME로 구현되도록하는 경우는 드뭅니다.

대부분의 다른 언어에는 "이 구현 된 인터페이스에서이 메서드의 시그니처와 일치하는이 클래스의 메서드가 바로 구현입니다"라고 말하는 명령을받는 것보다 더 행복한 컴파일러가 있습니다. 인터페이스 정의의 모든 요점은 정의가 구현에서 추상화되도록 허용하는 것입니다. (기본 재정의는 말할 것도없고 Java의 인터페이스에서 기본 메서드를 시작하지도 마십시오. 그들은 둘 다 자동차입니다 ... 자동차 만 yaw!

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