[java] Java 인터페이스의 선택적 메소드

내 이해에 따르면 Java에서 인터페이스를 구현하는 경우 해당 인터페이스에 지정된 메서드는 해당 인터페이스를 구현하는 하위 클래스에서 사용해야합니다.

Collection 인터페이스와 같은 일부 인터페이스에는 선택 사항으로 주석 처리 된 메서드가 있지만 이것이 정확히 무엇을 의미합니까? 인터페이스에 지정된 모든 메서드가 필요하다고 생각했기 때문에 조금 던졌습니다.



답변

여기에 대한 답변에 끔찍한 혼란이있는 것 같습니다.

Java 언어에서는 인터페이스의 모든 메소드가 해당 인터페이스의 모든 구현에 의해 구현되어야합니다. 기간. 이 규칙에는 예외가 없습니다. “컬렉션은 예외입니다”라는 말은 여기서 실제로 일어나는 일에 대한 매우 모호한 이해를 의미합니다.

인터페이스를 준수하는 데는 두 가지 수준이 있다는 것을 인식하는 것이 중요합니다.

  1. Java 언어로 확인할 수있는 항목. 이것은 꽤 많은 단지로 귀결이 어떤 방법 각각에 대한 구현은?

  2. 실제로 계약을 이행했습니다. 즉, 구현이 인터페이스의 문서에 명시된대로 수행합니까?

    잘 작성된 인터페이스에는 구현에서 예상되는 것을 정확히 설명하는 문서가 포함됩니다. 컴파일러는 이것을 확인할 수 없습니다. 문서를 읽고 그들이 말하는대로해야합니다. 계약서에 명시된대로 수행하지 않으면 컴파일러 에 관한 한 인터페이스 구현 이 가능하지만 결함 / 잘못된 구현이됩니다.

컬렉션 API를 디자인 할 때 Joshua Bloch는 컬렉션의 다양한 변형 (예 : 읽기 가능, 쓰기 가능, 임의 액세스 등)을 구별하기 위해 매우 세밀한 인터페이스를 사용하는 대신 매우 조잡한 인터페이스 세트 만 사용하기로 결정했습니다.
Collection, List, Set그리고 Map다음 “선택”과 같은 특정 작업을 문서화합니다. 이는 세분화 된 인터페이스로 인해 발생할 수있는 조합 적 폭발을 방지하기위한 것입니다. 로부터 자바 컬렉션 API 디자인 자주 묻는 질문 :

문제를 자세히 설명하기 위해 계층 구조에 수정 가능성 개념을 추가한다고 가정합니다. ModifiableCollection, ModifiableSet, ModifiableList 및 ModifiableMap의 네 가지 새 인터페이스가 필요합니다. 이전에 단순한 계층이었던 것이 이제는 지저분한 계층이되었습니다. 또한 제거 작업을 포함하지 않는 수정 불가능한 컬렉션과 함께 사용할 새 Iterator 인터페이스가 필요합니다. 이제 UnsupportedOperationException을 제거 할 수 있습니까? 불행히도.

배열을 고려하십시오. 대부분의 List 작업을 구현하지만 제거 및 추가는 수행하지 않습니다. 그들은 “고정 크기”목록입니다. 계층 구조에서이 개념을 캡처하려면 VariableSizeList 및 VariableSizeMap이라는 두 개의 새 인터페이스를 추가해야합니다. VariableSizeCollection 및 VariableSizeSet은 ModifiableCollection 및 ModifiableSet과 동일하므로 추가 할 필요가 없지만 일관성을 위해 추가하도록 선택할 수 있습니다. 또한 수정 불가능한 List와 함께 이동하려면 추가 및 제거 작업을 지원하지 않는 새로운 다양한 ListIterator가 필요합니다. 이제 우리는 원래 4 개 대신 최대 10 개 또는 12 개의 인터페이스와 2 개의 새로운 Iterator 인터페이스가 있습니다. 끝났어? 아니.

로그 (예 : 복구 가능한 데이터 개체에 대한 오류 로그, 감사 로그 및 저널)를 고려합니다. 제거 및 설정 (바꾸기)을 제외한 모든 List 작업을 지원하는 자연스러운 추가 전용 시퀀스입니다. 새로운 핵심 인터페이스와 새로운 반복자가 필요합니다.

그리고 수정 불가능한 콜렉션과 달리 불변의 콜렉션은 어떻습니까? (즉, 클라이언트가 변경할 수없고 다른 이유로 변경되지 않는 컬렉션). 많은 스레드가 동기화없이 컬렉션에 동시에 액세스 할 수 있기 때문에 이것이 가장 중요한 차이점이라고 주장합니다. 이 지원을 유형 계층 구조에 추가하려면 인터페이스가 4 개 더 필요합니다.

이제 우리는 최대 20 개 정도의 인터페이스와 5 개의 반복자이며, 실제로 어떤 인터페이스에도 깔끔하게 맞지 않는 컬렉션이 실제로 발생하는 것이 거의 확실합니다. 예를 들어 Map에서 반환하는 컬렉션 뷰는 자연적인 삭제 전용 컬렉션입니다. 또한 값을 기준으로 특정 요소를 거부하는 컬렉션이 있으므로 런타임 예외를 처리하지 않았습니다.

모든 것을 말하고 완료했을 때, 우리는 런타임 예외를 던질 수있는 매우 작은 핵심 인터페이스 세트를 제공함으로써 전체 문제를 회피하는 것이 건전한 엔지니어링 타협이라고 느꼈습니다.

Collections API의 메서드가 “선택적 작업”으로 문서화되어 있다고해서 메서드 구현을 구현에서 제외 할 수 있다는 의미도 아니며 빈 메서드 본문을 사용할 수 있다는 의미도 아닙니다. 결과를 반환해야합니다). 오히려 유효한 구현 선택 (여전히 계약을 준수하는)은 UnsupportedOperationException.

그 때문에 참고 UnsupportedOperationExceptionA는 RuntimeException당신이 멀리 컴파일러에 관한 한, 어떤 메소드 구현에서 던질 수 있습니다. 예를 들어, Collection.size(). 그러나 이러한 구현은 문서에 Collection.size()이것이 허용된다고 명시 되어 있지 않으므로 계약을 위반하게 됩니다.

곁에 : Java의 Collections API가 사용하는 접근 방식은 다소 논란의 여지가 있습니다 (하지만 처음 도입되었을 때보 다 지금은 적을 것입니다). 완벽한 세상에서는 인터페이스에 선택적 작업 이 없으며 대신 세분화 된 인터페이스가 사용됩니다. 문제는 Java가 유추 된 구조 유형이나 교차 유형을 지원하지 않는다는 것입니다. 이것이 바로 컬렉션의 경우 “올바른 방법”으로 작업을 시도하는 것이 극도로 다루기 힘들어지는 이유입니다.


답변

인터페이스에 대한 구현 (비추 상) 클래스를 컴파일하려면 모든 메서드를 구현해야합니다.

그러나 구현이 단순한 예외 인 메소드를 ‘구현되지 않은'( Collection인터페이스의 일부 메소드처럼 )으로 생각하면 Collection인터페이스는 일반적인 경우가 아니라이 경우 예외입니다. 일반적 으로 구현하는 클래스는 모든 메소드를 구현해야합니다.

컬렉션의 “선택 사항”은 구현 클래스가 (위의 용어에 따라)이를 ‘구현’할 필요가 없으며 그냥 던질 것임을 의미 NotSupportedException합니다.

좋은 예 add()-불변 컬렉션에 대한 방법-콘크리트는 던지는 것 외에는 아무것도하지 않는 방법을 구현합니다.NotSupportedException

이 경우에는 Collection복잡한 상속 트리를 방지하기 위해 수행되어 프로그래머를 비참하게 만들지 만 대부분의 경우이 패러다임은 권장되지 않으며 가능하면 피해야합니다.


최신 정보:

Java 8부터 기본 메소드 가 도입되었습니다.

즉, 인터페이스는 구현을 포함하여 메소드를 정의 할 수 있습니다.

이것은 인터페이스에 기능을 추가하는 동시에 새로운 기능이 필요하지 않은 코드 부분에 대해 이전 버전과의 호환성을 지원하기 위해 추가되었습니다.

메서드는 선언하는 모든 클래스에서 여전히 구현되지만 인터페이스의 정의를 사용합니다.


답변

Java의 인터페이스는 클래스 구현 계약을 선언합니다. 해당 인터페이스의 모든 메서드는 구현 되어야 하지만 구현 클래스는 구현되지 않은 상태로 남겨 둘 수 있습니다. 인위적인 예로서

interface Foo {
  void doSomething();
  void doSomethingElse();
}

class MyClass implements Foo {
  public void doSomething() {
     /* All of my code goes here */
  }

  public void doSomethingElse() {
    // I leave this unimplemented
  }
}

이제 doSomethingElse()구현되지 않은 상태로 두어 서브 클래스가 구현할 수 있도록 무료로 남겨 둡니다. 그것은 선택 사항입니다.

class SubClass extends MyClass {
    @Override
    public void doSomethingElse() {
      // Here's my implementation. 
    }
}

그러나 다른 사람들이 말했듯이 컬렉션 인터페이스에 대해 이야기하는 경우 예외입니다. 특정 메서드가 구현되지 않은 상태로 유지되고이를 호출하면 UnsupportedOperationException예외 가 발생할 수 있습니다 .


답변

Collection 인터페이스의 선택적 메서드는 메서드 구현에서 예외를 throw 할 수 있지만 어쨌든 구현해야 함을 의미합니다. 문서에 지정된대로 :

일부 컬렉션 구현에는 포함 할 수있는 요소에 대한 제한이 있습니다. 예를 들어, 일부 구현에서는 null 요소를 금지하고 일부 구현에는 요소 유형에 대한 제한이 있습니다. 부적합한 요소를 추가하려고하면 일반적으로 NullPointerException 또는 ClassCastException과 같은 확인되지 않은 예외가 발생합니다. 부적합한 요소가 있는지 쿼리하려고하면 예외가 발생하거나 단순히 false를 반환 할 수 있습니다. 일부 구현은 전자의 동작을 나타내고 일부는 후자를 나타냅니다. 보다 일반적으로, 완료로 인해 부적격 요소가 컬렉션에 삽입되지 않는 부적격 요소에 대한 작업을 시도하면 구현 옵션에 따라 예외가 발생하거나 성공할 수 있습니다. 이러한 예외는 “선택 사항”으로 표시됩니다.


답변

코드를 컴파일하려면 모든 메소드를 구현해야 default하지만 (Java 8 이상에서 구현 된 메소드는 제외 ) 구현시 기능적으로 유용한 작업을 수행 할 필요가 없습니다. 구체적으로 다음과 같습니다.

  • 비어있을 수 있습니다 (빈 메서드).
  • 그냥 던질 수 있습니다 UnsupportedOperationException(또는 유사)

후자의 접근 방식은 종종 컬렉션 클래스에서 사용됩니다. 모든 메서드는 여전히 구현되어 있지만 일부는 런타임에 호출되면 예외가 발생할 수 있습니다.


답변

사실 저는 SurfaceView.Callback2에서 영감을 받았습니다. 이것이 공식적인 방법이라고 생각합니다

public class Foo {
    public interface Callback {
        public void requiredMethod1();
        public void requiredMethod2();
    }

    public interface CallbackExtended extends Callback {
        public void optionalMethod1();
        public void optionalMethod2();
    }

    private Callback mCallback;
}

클래스가 선택적 메서드를 구현할 필요가없는 경우 “콜백 구현”만하면됩니다. 클래스가 선택적 메서드를 구현해야하는 경우 “CallbackExtended를 구현”하면됩니다.

똥 영어 죄송합니다.


답변

Java 8 이상에서이 질문에 대한 대답은 여전히 ​​유효하지만 이제는 더 미묘합니다.

첫째, 수락 된 답변의 다음 진술은 정확합니다.

  • 인터페이스는 계약에서 암시 적 동작을 지정하기위한 것입니다 (유효한 것으로 간주되기 위해 구현 클래스가 준수해야하는 동작에 대한 규칙 설명)
  • 계약 (규칙)과 구현 (규칙의 프로그래밍 방식 코딩) 사이에 차이가 있습니다.
  • 인터페이스에 지정된 메소드는 항상 구현되어야합니다 (언젠가는)

그렇다면 Java 8의 새로운 뉘앙스는 무엇입니까? “선택적 방법”에 대해 말할 때 다음 중 하나가 적합합니다.

1. 계약 상 선택적 구현 방법

“세 번째 진술”은 추상 인터페이스 메소드가 항상 구현되어야하며 이는 Java 8+에서도 여전히 적용된다는 것을 말합니다. 그러나 Java Collections Framework에서와 같이 일부 추상 인터페이스 메소드를 계약에서 “선택 사항”으로 설명 할 수 있습니다.

이 경우 인터페이스를 구현하는 작성자는 메서드를 구현하지 않도록 선택할 수 있습니다. 그러나 컴파일러는 구현을 주장하므로 작성자는 특정 구현 클래스에서 필요하지 않은 선택적 메서드에 대해이 코드를 사용합니다.

public SomeReturnType optionalInterfaceMethodA(...) {
    throw new UnsupportedOperationException();
}

Java 7 및 이전 버전에서 이것은 실제로 존재하는 유일한 “선택적 메소드”였습니다. 즉, 구현되지 않은 경우 UnsupportedOperationException을 발생시키는 메소드였습니다. 이 동작은 반드시 인터페이스 계약 (예 : Java Collections Framework의 선택적 인터페이스 메서드)에 의해 지정됩니다.

2. 재 구현이 선택적인 기본 방법

Java 8은 기본 메소드 개념을 도입했습니다 . 이들은 인터페이스 정의 자체에 의해 구현 될 수 있고 제공되는 메소드입니다. 일반적으로 메서드 본문이 다른 인터페이스 메서드 (예 : “프리미티브”)를 사용하여 작성 될 수 있고 ” this클래스가이 인터페이스를 구현 한이 객체”를 의미 할 수있는 경우에만 기본 메서드를 제공 할 수 있습니다 .

기본 메소드는 인터페이스의 계약을 이행해야합니다 (다른 인터페이스 메소드 구현과 마찬가지로). 따라서 구현 클래스에서 인터페이스 메서드의 구현을 지정하는 것은 작성자의 재량입니다 (동작이 자신의 목적에 적합한 경우).

이 새로운 환경에서 Java Collections Framework 는 다음 과 같이 다시 작성할 수 있습니다 .

public interface List<E> {
    :
    :
    default public boolean add(E element) {
        throw new UnsupportedOperationException();
    }
    :
    :
}

이런 식으로 “선택적”메서드 add()는 구현 클래스가 자체적으로 새로운 동작을 제공하지 않는 경우 UnsupportedOperationException을 던지는 기본 동작을 갖습니다. 이는 정확히 발생하고 싶은 동작이고 List 계약을 준수합니다. 작성자가 새 요소를 List 구현에 추가 할 수없는 클래스를 작성하는 add()경우 기본 동작이 정확히 필요한 것이기 때문에의 구현 은 선택 사항입니다.

이 경우 메서드가 인터페이스 자체에서 구현 되었기 때문에 위의 “세 번째 문”은 여전히 ​​유효합니다.

3. Optional결과 를 반환하는 메서드

마지막으로 새로운 종류의 선택적 메서드는 단순히 Optional. 이 Optional클래스는 null결과 를 처리하는보다 객체 지향적 인 방법을 제공 합니다.

새로운 Java Streams API로 코딩 할 때 흔히 볼 수있는 것과 같은 유창한 프로그래밍 스타일에서, 어떤 시점에서든 null 결과는 프로그램이 NullPointerException과 함께 충돌하게합니다. 이 Optional클래스는 클라이언트 코드가 충돌하지 않고 유창한 스타일을 가능하게하는 방식으로 클라이언트 코드에 null 결과를 반환하는 메커니즘을 제공합니다.