기본 메소드는 Java 툴박스의 새로운 도구입니다. 그러나 메소드 의 default
버전 을 정의하는 인터페이스를 작성하려고했습니다 toString
. Java는 선언 된 메소드 java.lang.Object
가 수행되지 않을 수 있으므로 금지되어 있다고 말합니다 default
. 왜 이런 경우입니까?
“기본 클래스 항상 승리”규칙이 있다는 것을 알고 있으므로 기본적으로 (pun;) 메소드의 default
구현은 어쨌든 Object
메소드에 의해 덮어 쓰여집니다 Object
. 그러나 Object
사양의 메소드에 예외가 없어야하는 이유는 없습니다 . 특히 toString
기본 구현을 갖는 것이 매우 유용 할 수 있습니다.
Java 디자이너가 default
메소드를 대체하는 메소드를 허용하지 않기로 결정한 이유는 무엇 Object
입니까?
답변
이것은 발굴을 시작할 때까지 “분명히 좋은 생각”으로 보이는 언어 디자인 문제 중 하나이며 실제로는 그것이 나쁜 생각임을 깨닫게됩니다.
이 메일 에는 주제 (및 기타 주제)에 많은 내용이 있습니다. 현재 디자인으로 우리를 데려 오기 위해 수렴 된 여러 디자인 세력이있었습니다.
- 상속 모델을 단순하게 유지하려는 욕구;
- 일단 당신이 명백한 예를 지나친다는 사실 (예 :
AbstractList
인터페이스로 equals / hashCode / toString 상속은 단일 상속 및 상태와 밀접한 관련이 있으며 인터페이스는 여러 번 상속되고 상태 비 저장됨을 알 수 있습니다. - 그것은 잠재적으로 놀라운 행동의 문을 열었습니다.
당신은 이미 “간단하게 유지”목표를 다뤘습니다. 상속 및 충돌 해결 규칙은 매우 단순하도록 설계되었습니다 (클래스가 인터페이스를 통해, 파생 된 인터페이스가 슈퍼 인터페이스를 통해, 다른 모든 충돌이 구현 클래스에 의해 해결됨). 물론 이러한 규칙은 예외를 만들기 위해 조정될 수 있습니다. 나는 당신이 그 줄을 당기기 시작할 때, 증가하는 복잡도는 생각만큼 작지 않다는 것을 알게 될 것입니다.
물론, 더 많은 복잡성을 정당화 할 수있는 어느 정도의 이점이 있지만이 경우에는 그렇지 않습니다. 여기서 말하는 방법은 equals, hashCode 및 toString입니다. 이러한 방법은 모두 본질적으로 객체 상태에 관한 것이며 인터페이스가 아닌 상태를 소유하는 클래스입니다. 인터페이스가 아니라 해당 클래스에 대한 평등의 의미를 결정하는 가장 좋은 위치에 있습니다 (특히 평등에 대한 계약이 매우 강력하기 때문에 효과적인 참조). 놀라운 결과를위한 자바); 인터페이스 작성자가 너무 멀리 제거되었습니다.
AbstractList
예제를 꺼내는 것은 쉽습니다 . 인터페이스 AbstractList
를 제거 하고 동작을 제거 할 수 있다면 좋을 것 List
입니다. 그러나이 명백한 예를 넘어 서면 찾을 수있는 다른 좋은 예는 많지 않습니다. 기본적 AbstractList
으로 단일 상속을 위해 설계되었습니다. 그러나 인터페이스는 다중 상속을 위해 설계되어야합니다.
또한이 클래스를 작성한다고 상상해보십시오.
class Foo implements com.libraryA.Bar, com.libraryB.Moo {
// Implementation of Foo, that does NOT override equals
}
Foo
퍼 유형의 작가 외모, 등호의 어떤 구현을 볼 수 없습니다, 그리고 얻을 참조 평등을 마무리, 그가 할 필요가 모두 상속 동등하다 Object
. 그런 다음 다음 주에 Bar의 라이브러리 관리자가 “유용하게”기본 equals
구현을 추가합니다 . 죄송합니다! 이제 Foo
다른 유지 관리 도메인의 인터페이스에 의해 의미론 이 깨져서 일반적인 방법에 대한 기본값을 “유용하게”추가했습니다.
기본값은 기본값입니다. 계층 구조의 아무 곳에 나없는 인터페이스에 기본값을 추가하면 구체적인 구현 클래스의 의미에 영향을 미치지 않아야합니다. 그러나 기본값이 Object 메소드를 “재정의”할 수 있다면 사실이 아닙니다.
따라서 무해한 기능처럼 보이지만 실제로는 매우 해 롭습니다. 점진적 표현력이 적기 때문에 많은 복잡성이 추가되며 별도로 컴파일 된 인터페이스를 손상시키기 위해 의도가 좋고 무해한 모양의 변경이 너무 쉽습니다. 클래스 구현의 의미론.
답변
java.lang.Object
기본 메소드는 “도달 가능”하지 않으므로의 메소드에 대한 인터페이스에서 기본 메소드를 정의하는 것은 금지되어 있습니다.
인터페이스를 구현하는 클래스에서 기본 인터페이스 메소드를 덮어 쓸 수 있으며 메소드가 슈퍼 클래스에서 구현 되더라도 메소드의 클래스 구현이 인터페이스 구현보다 우선 순위가 높습니다. 모든 클래스는에서 상속 받았으므로 java.lang.Object
의 메소드 java.lang.Object
는 인터페이스의 기본 메소드보다 우선하며 대신 호출됩니다.
Oracle의 Brian Goetz는이 메일 링리스트 포스트 에서 디자인 결정에 대한 몇 가지 세부 사항을 제공합니다 .
답변
Java 언어 저자의 머리를 보지 못하므로 추측 할 수 있습니다. 그러나 나는 여러 가지 이유를보고이 문제에서 절대적으로 동의합니다.
기본 메소드를 도입하는 주된 이유는 이전 구현의 하위 호환성을 손상시키지 않고 인터페이스에 새 메소드를 추가 할 수 있기 때문입니다. 기본 메소드는 각 구현 클래스에서 정의 할 필요없이 “편의”메소드를 제공하는 데 사용될 수도 있습니다.
이들 중 어느 것도 toString 및 Object의 다른 메소드에는 적용되지 않습니다. 간단히 말해서, 기본 방법은 다른 정의가없는 기본 동작 을 제공하도록 설계되었습니다 . 다른 기존 구현과 “경쟁”할 구현을 제공하지 않습니다.
“기본 클래스 항상 승리”규칙에도 확실한 이유가 있습니다. 인터페이스는 기본 구현을 정의하지만 클래스는 실제 구현을 정의한다고 가정합니다 .
또한 일반 규칙에 예외를 도입하면 불필요한 복잡성이 발생하고 다른 질문이 발생합니다. 객체는 다른 클래스와 비슷하거나 다른 클래스이므로 왜 다른 행동을 가져야합니까?
당신이 제안하는 솔루션은 아마도 프로보다 더 많은 단점을 가져올 것입니다.
답변
추론은 매우 간단합니다. Object는 모든 Java 클래스의 기본 클래스이기 때문입니다. 따라서 일부 인터페이스에서 Object의 메소드를 기본 메소드로 정의하더라도 Object의 메소드가 항상 사용되므로 쓸모가 없습니다. 그렇기 때문에 혼동을 피하기 위해 Object 클래스 메서드를 재정의하는 기본 메서드를 가질 수 없습니다.
답변
매우 현명한 답변을 제공 default
하기 위해의 공개 메소드에 대한 메소드 를 정의하는 것은 금지되어 있습니다 java.lang.Object
. 고려해야 할 11 가지 방법이 있는데,이 질문에 답하기 위해 세 가지 방법으로 분류 할 수 있습니다.
- 의 여섯
Object
방법을 가질 수 없습니다default
그들이 있기 때문에 방법을final
모두에서 재정의 할 수 없습니다 :getClass()
,notify()
,notifyAll()
,wait()
,wait(long)
,와wait(long, int)
. - 의 세 가지
Object
방법이있을 수 없습니다default
: 브라이언 게츠에 의해 위에 주어진 이유에 대한 방법을equals(Object)
,hashCode()
하고toString()
. -
두 가지
Object
방법 에는 방법 이 있을 수default
있지만 이러한 기본값의 값에 대해서는 의문의 여지가 있습니다 :clone()
및finalize()
.public class Main { public static void main(String... args) { new FOO().clone(); new FOO().finalize(); } interface ClonerFinalizer { default Object clone() {System.out.println("default clone"); return this;} default void finalize() {System.out.println("default finalize");} } static class FOO implements ClonerFinalizer { @Override public Object clone() { return ClonerFinalizer.super.clone(); } @Override public void finalize() { ClonerFinalizer.super.finalize(); } } }