[java] Java 8 인터페이스 메소드에서 “최종”이 허용되지 않는 이유는 무엇입니까?

Java 8의 가장 유용한 기능 중 하나는 default인터페이스 의 새로운 메소드입니다. 그들이 소개 된 이유는 본질적으로 두 가지가 있습니다 (다른 것들이있을 수 있습니다).

API 디자이너의 관점에서 나는 인터페이스 메소드에 다른 수정자를 사용할 수 있기를 원했습니다 final. 이는 편리한 메소드를 추가 할 때 유용하며 클래스 구현시 “우연한”재정의를 방지합니다.

interface Sender {

    // Convenience method to send an empty message
    default final void send() {
        send(null);
    }

    // Implementations should only implement this method
    void send(String message);
}

Sender의 클래스 는 이미 일반적인 관행입니다 .

abstract class Sender {

    // Convenience method to send an empty message
    final void send() {
        send(null);
    }

    // Implementations should only implement this method
    abstract void send(String message);
}

지금 default과는 final분명 키워드 모순되지만, 기본 자체가 키워드 엄격하게 요구되지 않았을 나는 사이의 미묘한 차이를 반영하기 위해,이 모순은 고의적 인 있으리라 믿고있어, 그래서 “몸 클래스 메소드” (단지 방법) 및 “인터페이스를 본문이있는 메소드 “ (기본 메소드), 즉 아직 이해하지 못한 차이점이 있습니다.

어떤 시점에서 Brian Goetz는 다음 static과 같이 final인터페이스 방법 과 같은 수정 자에 대한 지원 이 아직 완전히 탐구되지 않았습니다 .

다른 부분은 최종 메소드, 개인 메소드, 보호 된 메소드, 정적 메소드 등과 같은 인터페이스에서 클래스 작성 도구를 지원하기 위해 얼마나 멀리 갈 것인가입니다. 대답은 다음과 같습니다.

2011 년 말 이래로 static인터페이스의 메소드에 대한 지원 이 추가되었습니다. 분명히 이것은와 같이 JDK 라이브러리 자체에 많은 가치를 부여했습니다 Comparator.comparing().

질문:

어떤 이유는 final(도 static final) 8 인터페이스를 자바로 만든 적이?



답변

이 질문은 어느 정도 Java 8 인터페이스 메소드에서 “동기화”가 허용되지 않는 이유 와 관련이 있습니다.

기본 방법에 대해 이해해야 할 핵심 사항은 기본 설계 목표가 “인터페이스를 ( 중요한 ) 특성으로 바꾸지 않고” 인터페이스 진화 라는 것입니다. 둘 사이에 약간의 겹침이 있고 우리는 전자를 방해하지 않는 후자에 적응하려고 노력했지만 이러한 질문은이 관점에서 볼 때 가장 잘 이해됩니다. ( 인터페이스 메소드가 여러 번 상속 될 수 있다는 사실 때문에 클래스 메소드 의도와 상관없이 인터페이스 메소드와 다를 것입니다.)

기본 메소드의 기본 아이디어는 다음과 같습니다. 기본 구현의 인터페이스 메소드이며 파생 클래스는보다 구체적인 구현을 제공 할 수 있습니다. 디자인 센터는 인터페이스의 진화했기 때문에, 그것은 기본 방법은 인터페이스에 추가 할 수있는 중요한 설계 목표였다 사후 소스 호환 및 이진 호환 방식으로는.

“최종 기본 방법이 아닌 이유”에 대한 너무 간단한 대답은 본문이 단순히 기본 구현이 아니라 유일한 구현 일 것입니다. 그것은 너무 간단한 대답이지만, 우리는 질문이 이미 의심스러운 방향으로 가고 있다는 단서를 제공합니다.

최종 인터페이스 방법에 의문을 제기하는 또 다른 이유는 구현 자에게 불가능한 문제가 발생하기 때문입니다. 예를 들어, 다음이 있다고 가정하십시오.

interface A {
    default void foo() { ... }
}

interface B {
}

class C implements A, B {
}

여기서 모든 것이 좋습니다. C상속 foo()에서 A. 이제 기본값으로 메소드 B를 갖도록 변경 되었다고 가정하십시오 foo.

interface B {
    default void foo() { ... }
}

이제 다시 컴파일 할 때 C컴파일러는 상속 할 동작을 알지 못 foo()하므로 C이를 무시해야 A.super.foo()하며 동일한 동작을 유지하려는 경우 위임하도록 선택할 수 있습니다 . B기본을 만들었다 final, 그리고 A저자의 통제하에 있지 C? 이제는 C돌이킬 수 없습니다. 재정의하지 않고는 컴파일 foo()할 수 없지만에서 최종인 foo()경우 재정의 할 수 없습니다 B.

이것은 단지 하나의 예이지만, 요점은 메소드의 최종성이 실제로 행동에 기여하고 곱할 수있는 인터페이스보다 단일 상속 클래스 (일반적으로 상태를 행동에 연결하는)의 세계에서 더 의미가있는 도구라는 것입니다 상속. “다른 인터페이스가 최종 구현 자에 혼합 될 수있는”것에 대해 추론하기는 너무 어렵고, 인터페이스 메소드를 최종적으로 허용하면 이러한 문제가 발생할 수 있습니다 (그리고 인터페이스를 작성한 사람이 아니라 그것을 구현하려는 가난한 사용자.)

그것들을 허용하지 않는 또 다른 이유는 그들이 당신이 생각하는 것을 의미하지 않기 때문입니다. 기본 구현은 클래스 (또는 그 슈퍼 클래스)가 메소드의 선언 (콘크리트 또는 추상)을 제공하지 않는 경우에만 고려됩니다. 기본 메소드가 최종이지만 수퍼 클래스가 이미 메소드를 구현 한 경우, 디폴트는 무시 될 것입니다. 아마도 기본 작성자가 최종 선언시 기대하지 않았을 것입니다. (이 상속 동작은 기본 메소드-인터페이스 진화에 대한 디자인 센터의 반영입니다. 기본 메소드 (또는 기존 인터페이스 메소드에 대한 기본 구현)를 변경하지 않고 이미 구현 된 기존 인터페이스에 추가 할 수 있어야합니다. 인터페이스를 구현하는 기존 클래스의 동작


답변

람다 메일 링리스트 에는 그것에 관한 많은 토론이 있습니다 . 그 모든 것들에 대해 많은 토론을 포함하고있는 것 중 하나는 다음과 같습니다. 다양한 인터페이스 방법 가시성 (최종 수비수) .

이 토론에서 원래 질문 의 저자 Talden 은 귀하의 질문과 매우 비슷한 것을 묻습니다.

모든 인터페이스 멤버를 공개하기로 한 결정은 실제로 불행한 결정이었습니다. 내부 디자인에서 인터페이스를 사용하면 구현 개인 정보가 노출되는 것이 큰 문제입니다.

모호하거나 호환성을 떨어 뜨리는 뉘앙스를 언어에 추가하지 않고 고치는 것은 어려운 일입니다. 그 크기와 잠재적 인 미묘함의 호환성 중단은 눈에 띄지 않을 것이므로 기존 코드를 위반하지 않는 솔루션이 존재해야합니다.

‘package’키워드를 액세스 지정자로 다시 도입 할 수 있습니다. 인터페이스에 지정자가 없으면 공개 액세스를 의미하고 클래스에 지정자가 없으면 패키지 액세스를 의미합니다. 인터페이스에서 어떤 지정자가 의미가 있는지는 명확하지 않습니다. 특히 개발자에 대한 지식 부담을 최소화하기 위해 액세스 지정자가 클래스와 인터페이스에서 동일한 것을 의미하는지 확인해야합니다.

기본 메소드가없는 경우 인터페이스의 멤버 지정자가 인터페이스 자체만큼 가시적이어야하므로 인터페이스는 실제로 모든 보이는 컨텍스트에서 구현 될 수 있습니다. 기본 메소드는 그렇지 않습니다. 너무 확실합니다.

이것이 가능한 범위 내 토론인지에 대한 명확한 의사 소통이 있었습니까? 그렇지 않은 경우 다른 곳에서 개최해야합니다.

결국 Brian Goetz의 답변 은 다음과 같습니다.

예, 이것은 이미 탐구되고 있습니다.

그러나 언어 / VM 기능은 리드 타임이 길고 사소한 것 같은 리드 타임을 가지고 있습니다. Java SE 8에 대한 새로운 언어 기능 아이디어를 제안 할 시간은 거의 지났습니다.

따라서 범위의 일부가 아니기 때문에 구현되지 않았을 가능성이 큽니다. 고려할 시간에 제안 된 적이 없습니다.

브라이언은 이 주제에 대한 최종 수비수 방법대한 또 다른 열띤 토론에서 다시 말했다 .

그리고 당신은 당신이 원하는 것을 정확히 얻었습니다. 이것이 바로이 기능이 추가하는 것, 즉 행동의 다중 상속입니다. 물론 우리는 사람들이 그것들을 특성으로 사용할 것임을 이해합니다. 그리고 우리가 제공하는 상속 모델이 사람들이 다양한 상황에서 좋은 결과를 얻을 수있을 정도로 단순하고 깨끗하도록하기 위해 열심히 노력했습니다. 동시에, 우리는 그것들을 간단하고 깨끗하게 작동하는 것의 경계를 넘어서지 않기로 선택했고, 어떤 경우에는 “아, 충분히 멀지 않았습니다”반응으로 이어집니다. 그러나 실제로이 스레드의 대부분은 유리가 98 % 만 가득 차있는 것처럼 보입니다. 나는 98 %를 가지고 그것을 가지고 갈 것이다!

따라서 이것은 단순히 이론의 범위 나 디자인의 일부가 아니라는 이론을 강화합니다. 그들이 한 일은 API 진화 문제를 처리하기에 충분한 기능을 제공하는 것이 었습니다.


답변

@EJP의 의견에 언급 된 공명에 대해서는 “THE”답변을 찾아서 식별하기가 어려울 것입니다. 전 세계에 약 2 명 (+/- 2)의 사람들이 정답 줄 수 있습니다 . 그리고 의심의 여지가있는 답변은 “최종 기본 방법을 지원하는 것이 내부 통화 해결 메커니즘을 재구성하려는 노력에 가치가없는 것 같습니다”와 같은 것일 수 있습니다. 물론 이것은 추측이지만 OpenJDK 메일 링리스트에있는 두 사람 중 한 사람에 의한진술 과 같은 미묘한 증거에 의해 뒷받침됩니다 .

“최종 기본값”메소드가 허용 된 경우 내부 invokespecial에서 사용자가 볼 수있는 invoke 인터페이스로 다시 작성해야 할 수 있습니다. “

그리고 OpenJDK 의 Method :: is_final_method 메소드에서 현재 구현 된 것처럼 메소드 가 메소드 일 때 메소드가 (실제로) 최종 메소드로 간주 되지 않는 것과 같은 사소한 사실 .default

더 많은 웹 검색이나 커밋 로그를 읽음으로써도 실제로 “권한있는”정보는 찾기가 어렵습니다. invokeinterface명령에 해당하는 인터페이스 메소드 호출 및 클래스 메소드 호출을 분석하는 동안 잠재적 인 모호성과 관련이 있다고 생각했습니다 invokevirtual. invokevirtual명령에 대해서는 메소드가 상속되어야하기 때문에 간단한 vtable 조회 가있을 수 있습니다 수퍼 클래스에서 또는 클래스에 의해 직접 구현됩니다. 이와는 대조적으로, invokeinterface호출은 해당 호출 이 실제로 참조 하는 인터페이스 를 찾기 위해 해당 호출 사이트를 검사해야합니다 (이는 HotSpot Wiki 의 InterfaceCalls 페이지에 자세히 설명되어 있습니다 ). 하나,final 메소드가 vtable 에 삽입되지 않을 수 있습니다 . vtable의 기존 항목을 모두 대체하거나 ( klassVtable.cpp. Line 333 참조 ), 마찬가지로 기본 방법은 vtable의 기존 항목을 대체합니다 ( klassVtable.cpp, Line 202 참조 ). 그래서 실제 이유 (그리고 따라서 대답)는 (복잡한) 메소드 호출 해결 메커니즘 내부에 더 깊이 숨겨져 있어야하지만 어쨌든 이러한 참조는 도움이되는 것으로 간주 될 것입니다.


답변

final편의성 인터페이스 방법 을 지정하는 것이 필요하다고 생각하지 않을 수도 있지만 동의 할 수는 있습니다. 도움 될 비용이 이점을 능가하는 것처럼 보일 있습니다.

어느 쪽이든,해야 할 일은 기본 메소드에 대해 올바른 javadoc을 작성하여 메소드가 수행하는 것과 수행 할 수없는 것을 정확하게 보여주는 것입니다. 이런 식으로 인터페이스를 구현하는 클래스는 구현을 변경할 수는 없지만 “보증은 없습니다”.

누구나 Collection인터페이스를 고수하고 완전히 직관적 인 방법으로 작업을 수행 할 수 있습니다. 광범위한 단위 테스트를 작성하는 것 외에는 자신을 보호 할 수있는 방법이 없습니다.


답변

우리 는 클래스가 구현을 확장 하거나 구현 하지 않을 수 있음을 알 때 default키워드를 메소드 안에 추가 합니다 . 그러나 구현 클래스를 재정의하지 않으려는 메소드를 추가하려면 어떻게해야합니까? 우리에게는 두 가지 옵션이 있습니다.interfaceinterfaceoverride

  1. default final방법을 추가하십시오 .
  2. static방법을 추가하십시오 .

이제 Java는 class두 개 이상의 구현을 통해 정확히 동일한 메소드 이름과 서명을 가진 메소드를 interfaces가지면 ( default즉, 복제 된 경우) 클래스에서 해당 메소드의 구현을 제공해야한다고 말합니다. 이제 default final메소드의 경우 구현을 제공 할 수 없으며 붙어 있습니다. 이것이 final키워드가 인터페이스에서 사용되지 않는 이유 입니다.


답변