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


335

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 인터페이스를 자바로 만든 적이?


10
젖은 담요가되어 죄송합니다. 제목에 표시된 질문이 SO 용어로 답변되는 유일한 방법은 Brian Goetz 또는 JSR Expert 그룹의 인용을 통해서입니다. BG가 공개 토론을 요청한 것은 이해하지만, '주요 의견 기반'이므로 SO의 용어에 위배됩니다. 여기에 책임이 걸려있는 것 같습니다. 토론을 자극하고 이론적 근거를 도출하는 것은 전문가 그룹의 업무이며보다 광범위한 Java 커뮤니티 프로세스입니다. 그렇지 않습니다. 그래서 나는 '주요 의견 기반'으로 마감하기로 투표하고 있습니다.
Lorne의 후작

2
우리 모두 알다시피, final메소드가 재정의되는 것을 막고, 인터페이스에서 상속 된 메소드를 어떻게 재정의해야하는지 알기 때문에, 그것이 최종적인 이유가 무엇인지 알 수 없습니다. 한 번 재정의 한 후에이 방법이 최종임을 의미하지 않는 한,이 경우 어쩌면 어려움이 있습니까? 이 권리를 이해하지 못한다면, 저를 놀라게하십시오. 흥미로운 것 같습니다
Vince Emigh

22
@EJP : "만약 그렇게 할 수 있다면 당신도 할 수 있고, 당신 자신의 질문에 대답 할 수 있습니다. 그것은이 포럼의 거의 모든 질문에 적용됩니다. 항상 Google에 5 시간 동안 주제를 표시 한 다음 다른 모든 사람이 똑같이 어려운 방식으로 배워야하는 것을 배울 수있었습니다. 또는 Google에서 SO가 잘 참조되기 때문에 미래의 모든 사람 (지금까지 12 개의 upvotes와 8 개의 별을 포함하여)이 이익을 얻을 수있는 더 나은 답변을 제공하기 위해 몇 분 더 기다립니다. 예. 이 질문 SO의 Q & A 양식에 완벽하게 들어 맞을 수 있습니다 .
Lukas Eder

5
@VinceEmigh " ... 인터페이스에서 상속 된 메소드를 어떻게 재정의 해야하는지 보기 ... "Java 8에서는 그렇지 않습니다. Java 8에서는 인터페이스에서 메소드를 구현할 수 있습니다. 즉, 구현시 구현할 필요가 없습니다. 클래스. 여기서는 final클래스 구현이 인터페이스 메소드의 기본 구현을 대체하지 못하게하는 데 사용됩니다.
awksp

28
@EJP 당신은 결코 모른다 : Brian Goetz는 대답 할지도 모른다 !
assylias

답변:


419

이 질문은 어느 정도 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.

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

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


88
새로운 언어 기능에 대한 질문에 답변하는 것이 환상적입니다! 새로운 기능을 어떻게 사용해야하는지 파악할 때 디자인의 의도와 세부 사항을 명확히하는 것이 매우 도움이됩니다 . 디자인에 관여 한 다른 사람들이 SO에 기여하고 있습니까? java-8 태그 아래에서 귀하의 답변을 드리겠습니다. 다른 사람들이 동일한 작업을 수행하고 있는지 확인할 수 있기를 바랍니다.
Shorn

10
@Shorn Stuart Marks 는 java-8 태그에서 활성화되었습니다. Jeremy Manson 은 과거에 게시했습니다. 조슈아 블로흐 (Joshua Bloch)의 메시지를 본 기억이 있지만 지금은 찾을 수 없습니다.
assylias

1
C #이 잘못 구현되고 추악하게 구현 된 확장 메서드를 사용하여 수행하는 방식을 훨씬 더 우아한 방법으로 기본 인터페이스 메서드로 만든 것을 축하합니다. 이 질문에 관해서는, 이름 충돌을 해결할 수 없다는 것에 대한 대답이 문제를 해결하지만 제공된 나머지 이유는 설득력없는 철학이었습니다. (인터페이스 방법이 최종적
이기를 원한다면,

1
그럼에도 불구하고 이름 충돌 해결은 구현 메소드의 선언에 구현되는 특정 인터페이스의 이름을 추가함으로써 C #에서 수행되는 방식과 유사한 방식으로 달성 될 수있었습니다. 따라서이 모든 것을 집어 낸 것은 Java에서 기본 인터페이스 메소드가 최종적이 될 수 없다는 것입니다. 구문에 대한 추가 수정이 필요했기 때문입니다. (아마 너무 예리한가요?)
Mike Nakis

18
@Trying Abstract 클래스는 여전히 상태를 도입하거나 핵심 Object 메소드를 구현하는 유일한 방법입니다. 기본 방법은 순수한 동작입니다 . 추상 클래스는 상태와 결합 된 동작을위한 것입니다.
Brian Goetz

43

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

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

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

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

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

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

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

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

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

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

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

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

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

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


4
오늘 아침에 인터넷 검색 오디세이에 "Defender methods"라는 옛 이름이 포함되어 있어야합니다. 이것을 파고 +1.
Marco13 1

1
역사적 사실을 파헤치는 것이 좋습니다. 귀하의 결론 은 공식 답변
Lukas Eder

왜 이전 버전과의 호환성을 깨뜨릴 지 모르겠습니다. final은 전에 허용되지 않았습니다. 그들은 이제 개인을 허용하지만 보호되지는 않습니다. myeh ... 개인은 구현할 수 없습니다 ... 다른 인터페이스를 확장하는 인터페이스는 상위 인터페이스의 일부를 구현할 수 있지만 과부하 기능을 다른 인터페이스에 노출시켜야합니다.
mmm

17

@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 참조 ). 그래서 실제 이유 (그리고 따라서 대답)는 (복잡한) 메소드 호출 해결 메커니즘 내부에 더 깊이 숨겨져 있어야하지만 어쨌든 이러한 참조는 도움이되는 것으로 간주 될 것입니다.


흥미로운 통찰력에 감사드립니다. John Rose의 작품은 흥미로운 흔적입니다. 그래도 여전히 @EJP에 동의하지 않습니다. 반례로, Peter Lawrey매우 흥미롭고 매우 유사한 스타일의 질문에 대한 나의 대답을 확인하십시오 . 이다 역사적 사실을 발굴하는 것이 가능하고, 나는 항상 스택 오버플로 여기에 그들을 찾을 기쁘다 (다른 어디?). 물론, 당신의 대답은 여전히 ​​투기 적이며, JVM 구현 세부 사항이 JLS가 하나 또는 다른 방식으로 기록되는 최종 이유가 될 것이라고 100 % 확신하지는 않습니다.
Lukas Eder

@LukasEder 물론, 이런 종류의 질문 흥미롭고 IMHO는 Q & A 패턴에 적합합니다. 여기서 논쟁을 일으킨 두 가지 중요한 점이 있다고 생각합니다. 첫 번째는 "이유"를 요청한 것입니다. 많은 경우 공식적 으로 문서화 되지 않을 수 있습니다. 예를 들어, JLS에 서명되지 않은 ints 가없는 이유는 아직 언급되어 있지 않지만 stackoverflow.com/questions/430346 참조 ... ...
Marco13

... ... 두 번째는 당신이 요청한 것입니다 "권한 인용" 입니다. "답장을 쓰는 사람의 수는"수십 "에서" "약 0"으로 줄어 듭니다. 그 외에도 JVM 개발과 JLS 작성이 어떻게 짜여져 있는지 잘 모르겠습니다. 즉, 개발이 JLS에 작성된 내용에 얼마나 영향을 미치는지 잘 모르겠습니다. 그러나 여기서는 추측을 피할 것입니다. -)
Marco13

1
나는 여전히 내 사건을 쉬었다. 내 대답 누가보기 다른 질문을 :-) 이제 영원히 스택 오버플로 여기에, 권위있는 답을 알 수있는 이유 가 지원하지 않기로 결정했다 synchronizeddefault방법.
Lukas Eder

3
@LukasEder 내가 본 것처럼 여기에서도 마찬가지입니다. 누가 그것을 예상 할 수 있었습니까? 그 이유는 특히이 final질문에 대해 설득력이 있으며, 다른 사람이 비슷한 예를 생각하지 않는 것처럼 보이거나 (예를 들어, 일부 예에 대해서는 생각했지만 대답하기에 충분한 권위를 느끼지 못함) 다소 겸손합니다. 이제 (죄송합니다,이 작업을 수행해야합니다 :) 최종 단어가 사용됩니다.
Marco13

4

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

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

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


2
Javadoc 계약은 내 질문에 나열된 구체적인 예에 대한 유효한 해결 방법 이지만 실제로 편리한 인터페이스 방법 사용 사례에 관한 질문은 아닙니다. 문제는 finalJava 8 interface메소드 에서 허용되지 않는 정식 이유에 관한 것 입니다. 불충분 한 비용 / 이익 비율이 좋은 후보이지만 지금까지는 추측입니다.
Lukas Eder

0

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

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

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

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