Java8 : java.lang.Object에서 메소드의 기본 메소드를 정의하는 것이 금지 된 이유


130

기본 메소드는 Java 툴박스의 새로운 도구입니다. 그러나 메소드 의 default버전 을 정의하는 인터페이스를 작성하려고했습니다 toString. Java는 선언 된 메소드 java.lang.Object가 수행되지 않을 수 있으므로 금지되어 있다고 말합니다 default. 왜 이런 경우입니까?

"기본 클래스 항상 승리"규칙이 있다는 것을 알고 있으므로 기본적으로 (pun;) 메소드의 default구현은 어쨌든 Object메소드에 의해 덮어 쓰여집니다 Object. 그러나 Object사양의 메소드에 예외가 없어야하는 이유는 없습니다 . 특히 toString기본 구현을 갖는 것이 매우 유용 할 수 있습니다.

Java 디자이너가 default메소드를 대체하는 메소드를 허용하지 않기로 결정한 이유는 무엇 Object입니까?


1
나는 지금 나 자신에 대해 너무 기분이 좋으며, 이것을 100 번 올리며 금색 배지를 붙였습니다. 좋은 질문!
유진

답변:


186

이것은 발굴을 시작할 때까지 "분명히 좋은 생각"으로 보이는 언어 디자인 문제 중 하나이며 실제로는 그것이 나쁜 생각임을 깨닫게됩니다.

이 메일 에는 주제 (및 기타 주제)에 많은 내용이 있습니다. 현재 디자인으로 우리를 데려 오기 위해 수렴 된 여러 디자인 세력이있었습니다.

  • 상속 모델을 단순하게 유지하려는 욕구;
  • 일단 당신이 명백한 예를 지나친다는 사실 (예 : 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 메소드를 "재정의"할 수 있다면 사실이 아닙니다.

따라서 무해한 기능처럼 보이지만 실제로는 매우 해 롭습니다. 점진적 표현력이 적기 때문에 많은 복잡성이 추가되며 별도로 컴파일 된 인터페이스를 손상시키기 위해 의도가 좋고 무해한 모양의 변경이 너무 쉽습니다. 클래스 구현의 의미론.


13
시간을내어 설명해 주셔서 감사합니다. 고려 된 모든 요소에 감사드립니다. 나는이 위험 할 것이라는 점에 동의 hashCode하고 equals있지만, 나는 그것이 매우 유용 할 것 같아 toString. 예를 들어, 일부 Displayable인터페이스는 정의 할 수 있습니다 String display()방법을, 그리고 그것을 정의 할 수있는 보일러의 톤을 절약 할 수 default String toString() { return display(); }Displayable대신 매를 필요로의, Displayable구현 toString()a 또는 확장 DisplayableToString기본 클래스를.
Brandon

8
@Brandon 당신은 toString () 상속을 허용 하는 것이 equals () 및 hashCode ()와 같은 방식으로 위험 하지 않다는 것이 맞습니다 . 반면에 이제는 기능이 훨씬 불규칙적입니다. 상속 규칙에 대해서는 여전히 동일한 추가 복잡성이 발생할 수 있습니다. .
Brian Goetz

5
@gexicide toString()인터페이스 메소드만을 기반으로하는 경우, 인터페이스에 비슷한 default String toStringImpl()것을 추가하고 toString()각 서브 클래스에서 재정 의하여 인터페이스 구현을 호출 할 수 있습니다. 이 같은 것을 확인하는 것입니다 할 수있는 또 다른 방법을 :) Objects.hash(), Arrays.deepEquals()하고 Arrays.deepToString(). @BrianGoetz의 답변에 +1했습니다!
Siu Ching Pong -Asuka Kenji-

3
람다의 toString ()의 기본 동작은 정말 불쾌합니다. 람다 공장은 매우 간단하고 빠르도록 설계되었지만 파생 클래스 이름을 뱉어내는 것은 실제로 도움이되지 않습니다. 갖는 default toString()--at 함수의 서명과 구현의 상위 클래스에서 침 같은 것을 할 least--에 기능 인터페이스에 우선하는 것은 우리를 허용합니다. 더 좋은 점은 재귀 적 toString 전략을 적용 할 수 있다면 클로저를 통해 람다에 대한 훌륭한 설명을 얻을 수 있으므로 람다 학습 곡선을 크게 개선 할 수 있습니다.
Groostav

모든 클래스, 서브 클래스, 인스턴스 멤버에서 toString을 변경하면 클래스 또는 클래스의 사용자를 구현하는 데 영향을 줄 수 있습니다. 또한 기본 메소드를 변경하면 모든 구현 클래스에 영향을 줄 수 있습니다. 인터페이스의 동작을 변경하는 누군가에게 toString, hashCode의 특별한 점은 무엇입니까? 클래스가 다른 클래스를 확장하면 다른 클래스도 변경할 수 있습니다. 또는 위임 패턴을 사용중인 경우. Java 8 인터페이스를 사용하는 사람은 업그레이드하여 업그레이드해야합니다. 서브 클래스에서 억제 될 수있는 경고 / 오류가 제공 될 수 있습니다.
mmm

30

java.lang.Object기본 메소드는 "도달 가능"하지 않으므로의 메소드에 대한 인터페이스에서 기본 메소드를 정의하는 것은 금지되어 있습니다.

인터페이스를 구현하는 클래스에서 기본 인터페이스 메소드를 덮어 쓸 수 있으며 메소드가 슈퍼 클래스에서 구현 되더라도 메소드의 클래스 구현이 인터페이스 구현보다 우선 순위가 높습니다. 모든 클래스는에서 상속 받았으므로 java.lang.Object의 메소드 java.lang.Object는 인터페이스의 기본 메소드보다 우선하며 대신 호출됩니다.

Oracle의 Brian Goetz는이 메일 링리스트 포스트 에서 디자인 결정에 대한 몇 가지 세부 사항을 제공합니다 .


3

Java 언어 저자의 머리를 보지 못하므로 추측 할 수 있습니다. 그러나 나는 여러 가지 이유를보고이 문제에서 절대적으로 동의합니다.

기본 메소드를 도입하는 주된 이유는 이전 구현의 하위 호환성을 손상시키지 않고 인터페이스에 새 메소드를 추가 할 수 있기 때문입니다. 기본 메소드는 각 구현 클래스에서 정의 할 필요없이 "편의"메소드를 제공하는 데 사용될 수도 있습니다.

이들 중 어느 것도 toString 및 Object의 다른 메소드에는 적용되지 않습니다. 간단히 말해서, 기본 방법은 다른 정의가없는 기본 동작 을 제공하도록 설계되었습니다 . 다른 기존 구현과 "경쟁"할 구현을 제공하지 않습니다.

"기본 클래스 항상 승리"규칙에도 확실한 이유가 있습니다. 인터페이스는 기본 구현을 정의하지만 클래스는 실제 구현을 정의한다고 가정합니다 .

또한 일반 규칙에 예외를 도입하면 불필요한 복잡성이 발생하고 다른 질문이 발생합니다. 객체는 다른 클래스와 비슷하거나 다른 클래스이므로 왜 다른 행동을 가져야합니까?

당신이 제안하는 솔루션은 아마도 프로보다 더 많은 단점을 가져올 것입니다.


내 게시물을 게시 할 때 gexicide의 답변의 두 번째 단락을 보지 못했습니다. 문제를보다 자세히 설명하는 링크가 포함되어 있습니다.
Marwin

1

추론은 매우 간단합니다. Object는 모든 Java 클래스의 기본 클래스이기 때문입니다. 따라서 일부 인터페이스에서 Object의 메소드를 기본 메소드로 정의하더라도 Object의 메소드가 항상 사용되므로 쓸모가 없습니다. 그렇기 때문에 혼동을 피하기 위해 Object 클래스 메서드를 재정의하는 기본 메서드를 가질 수 없습니다.


1

매우 현명한 답변을 제공 default하기 위해의 공개 메소드에 대한 메소드 를 정의하는 것은 금지되어 있습니다 java.lang.Object. 고려해야 할 11 가지 방법이 있는데,이 질문에 답하기 위해 세 가지 방법으로 분류 할 수 있습니다.

  1. 의 여섯 Object방법을 가질 수 없습니다 default그들이 있기 때문에 방법을 final모두에서 재정의 할 수 없습니다 : getClass(), notify(), notifyAll(), wait(), wait(long),와 wait(long, int).
  2. 의 세 가지 Object방법이있을 수 없습니다 default: 브라이언 게츠에 의해 위에 주어진 이유에 대한 방법을 equals(Object), hashCode()하고 toString().
  3. 두 가지 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();
            }
        }
    }

.점은 무엇인가? "왜 Java 디자이너가 Object에서 메소드를 재정의하는 기본 메소드를 허용하지 않기로 결정한 이유는 무엇입니까?"
pro_cheats
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.