특성으로 Java 8 기본 방법 : 안전합니까?


116

Java 8에서 기본 메소드를 가난한 사람 버전의 특성 으로 사용하는 것이 안전한 방법 입니까?

어떤 사람들은 팬더 를 사용하면 팬더를 슬프게 할 수 있다고 주장합니다 . 왜냐하면 멋있기 때문입니다.하지만 그것은 내 의도가 아닙니다. 또한 API 진화 및 이전 버전과의 호환성을 지원하기 위해 기본 메서드가 도입되었다는 사실을 종종 상기시킵니다. 이는 사실이지만, 이것이 그 자체로 특성으로 사용하는 것이 잘못되거나 왜곡되지는 않습니다.

나는이 다음과 같은 실제 사용 사례 를 염두에를 :

public interface Loggable {
    default Logger logger() {
        return LoggerFactory.getLogger(this.getClass());
    }
}

또는 다음을 정의하십시오 PeriodTrait.

public interface PeriodeTrait {
    Date getStartDate();
    Date getEndDate();
    default isValid(Date atDate) {
        ...
    }
}

물론 컴포지션을 사용할 수 있지만 (또는 도우미 클래스까지) 더 장황하고 복잡해 보이며 다형성의 이점을 허용하지 않습니다.

따라서 기본 메서드를 기본 특성으로 사용하는 것이 안전 합니까? 아니면 예기치 않은 부작용에 대해 걱정해야합니까?

SO에 대한 몇 가지 질문 은 Java 대 Scala 특성과 관련이 있습니다. 그게 요점이 아닙니다. 나는 단순히 의견을 묻는 것도 아닙니다. 대신, 권위있는 답변이나 최소한 현장 통찰력을 찾고 있습니다. 기업 프로젝트에서 기본 방법을 특성으로 사용했다면 시한 폭탄으로 판명 되었습니까?


추상 클래스를 상속하여 동일한 이점을 얻을 수 있고 팬더를 울리는 것에 대해 걱정할 필요가없는 것 같습니다. 인터페이스에서 기본 메서드를 사용하는 유일한 이유는 기능이 필요하고 인터페이스를 기반으로하는 레거시 코드를 수정합니다.
Deven Phillips

1
자체 정적 로거 필드를 정의하는 추상 클래스 확장에 대해 @ infosec812에 동의합니다. logger () 메서드가 호출 될 때마다 새 로거 인스턴스를 인스턴스화하지 않습니까?
Eidan Spiegel

로깅의 경우 Projectlombok.org 및 @ Slf4j 주석을 살펴볼 수 있습니다.
Deven Phillips

답변:


120

짧은 대답은 : 안전하게 사용하면 안전합니다. :)

은밀한 대답 : 특성 의미하는 바를 알려 주시면 더 나은 대답을 드리겠습니다. :)

진지하게 "특성"이라는 용어는 잘 정의되어 있지 않습니다. 많은 Java 개발자가 스칼라로 표현되는 특성에 가장 익숙하지만 스칼라는 이름이나 효과가있는 특성을 가진 첫 번째 언어와는 거리가 멀다.

예를 들어, Scala에서 특성은 상태 저장 ( var변수 를 가질 수 있음 )입니다. Fortress에서는 순수한 행동입니다. 기본 메소드가있는 Java의 인터페이스는 상태 비 저장입니다. 이것은 그들이 특성이 아니라는 것을 의미합니까? (힌트 : 그것은 속임수 질문이었습니다.)

다시, Scala에서 형질은 선형화를 통해 구성됩니다. 클래스는 경우 A특성 확장 X하고 Y,있는 다음 순서 X와는 Y갈등 방법이 결정에 혼합 X하고이 Y해결됩니다. 자바에서는 이러한 선형화 메커니즘이 존재하지 않습니다 (부분적으로 너무 "Java와 유사하지 않기 때문에"거부되었습니다.)

인터페이스에 기본 메소드를 추가하는 가장 가까운 이유는 인터페이스 진화 를 지원하기위한 것이었지만, 우리는 그 이상으로 나아가고 있다는 것을 잘 알고있었습니다. 그것이 "인터페이스 진화 ++"인지 "특성-"인지 여부는 개인적인 해석의 문제입니다. 따라서 안전에 대한 질문에 대답하려면 ... 메커니즘이 실제로 지원하는 것에 충실하는 한, 지원하지 않는 부분에 원하는대로 늘리려 고 시도하는 것이 아니라 괜찮습니다.

주요 디자인 목표는 인터페이스 의 클라이언트 관점 에서 기본 메서드가 "일반"인터페이스 메서드와 구분할 수 없어야한다는 것입니다. 따라서 메서드의 기본값 은 인터페이스 의 설계자구현 자 에게만 흥미 롭습니다 .

다음은 디자인 목표에 적합한 몇 가지 사용 사례입니다.

  • 인터페이스 진화. 여기에서는 기존 인터페이스에 새 메소드를 추가합니다.이 인터페이스에는 해당 인터페이스의 기존 메소드 측면에서 합리적인 기본 구현이 있습니다. 예를 들어에 forEach메서드를 추가하는 Collection경우 기본 구현이 iterator()메서드 측면에서 작성됩니다 .

  • "선택적"방법. 여기에서 인터페이스 설계자는 "수반되는 기능의 한계를 감수 할 의사가 있다면 구현자가이 방법을 구현할 필요가 없습니다"라고 말합니다. 예를 들어, Iterator.remove다음을 던지는 기본값이 주어졌습니다 UnsupportedOperationException. 대부분의 구현은 Iterator어쨌든이 동작 을 가지고 있기 때문에 기본값은이 메서드를 본질적으로 선택적으로 만듭니다. (의 동작이에서 AbstractCollection기본값으로 표현 된 Collection경우 돌연변이 메서드에 대해 동일한 작업을 수행 할 수 있습니다.)

  • 편리한 방법. 이것들은 엄격히 편의를위한 메서드이며, 일반적으로 클래스의 기본값이 아닌 메서드 측면에서 구현됩니다. logger()첫 번째 예에서 방법이 합리적인 그림입니다.

  • 결합 자. 현재 인스턴스를 기반으로 인터페이스의 새 인스턴스를 인스턴스화하는 구성 메서드입니다. 예를 들어, 방법 Predicate.and()또는 Comparator.thenComparing()결합 자의 예입니다.

기본 구현을 제공하는 경우 @implSpec구현자가 메서드를 재정의할지 여부를 이해하는 데 도움이 되도록 기본값 (JDK에서는 javadoc 태그를 사용)에 대한 일부 사양도 제공해야 합니다. 편의 메서드 및 결합 자와 같은 일부 기본값은 거의 무시되지 않습니다. 선택적 메서드와 같은 다른 것들은 종종 재정의됩니다. 기본이 무엇을 약속하는지에 대한 충분한 사양 (문서뿐만 아니라)을 제공해야 구현자가이를 재정의해야하는지 여부에 대해 현명한 결정을 내릴 수 있습니다.


9
이 포괄적 인 답변에 대해 Brian에게 감사합니다. 이제 가벼운 마음으로 기본 방법을 사용할 수 있습니다. 독자 : 인터페이스 진화 및 기본 방법에 대한 Brian Goetz의 자세한 정보는 예를 들어 NightHacking Worldwide Lambdas 에서 찾을 수 있습니다 .
youri

감사합니다 @ brian-goetz. 당신이 말한 바에 따르면 Java의 기본 메서드는 Ducasse 등의 논문 ( scg.unibe.ch/archive/papers/Duca06bTOPLASTraits.pdf )에 정의 된 전통적인 특성 개념에 더 가깝다고 생각 합니다. 스칼라 "특성"은 상태가 있고 구성에 선형화를 사용하기 때문에 나에게 전혀 특성이 아닌 것 같습니다. 또한 메서드 충돌을 암시 적으로 해결하는 것처럼 보입니다.이 모든 것이 전통적인 특성이 아닌 것입니다. 있다. 사실, 스칼라 특성은 특성 이라기보다는 믹스 인과 더 비슷합니다. 어떻게 생각해? PS : 저는 스칼라로 코딩 한 적이 없습니다.
adino

리스너 역할을하는 인터페이스의 메소드에 빈 기본 구현을 사용하는 것은 어떻습니까? 리스너 구현은 몇 가지 인터페이스 메소드 만 수신하는 데 관심이있을 수 있으므로 메소드를 기본값으로 설정하여 구현자는 수신해야하는 메소드 만 구현하면됩니다.
Lahiru Chandima

1
@LahiruChandima 등으로 MouseListener? 이 API 스타일이 의미가있는 한 이는 "선택적 메소드"버킷에 적합합니다. 선택 사항을 명확하게 문서화하십시오!
Brian Goetz

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