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 특성과 관련이 있습니다. 그게 요점이 아닙니다. 나는 단순히 의견을 묻는 것도 아닙니다. 대신, 권위있는 답변이나 최소한 현장 통찰력을 찾고 있습니다. 기업 프로젝트에서 기본 방법을 특성으로 사용했다면 시한 폭탄으로 판명 되었습니까?
답변
짧은 대답은 : 안전하게 사용하면 안전합니다. 🙂
은밀한 대답 : 특성 이 의미하는 바를 알려 주시면 더 나은 대답을 드리겠습니다. 🙂
진지하게 “특성”이라는 용어는 잘 정의되어 있지 않습니다. 많은 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 태그를 사용)에 대한 일부 사양도 제공해야 합니다. 편의 메서드 및 결합 자와 같은 일부 기본값은 거의 무시되지 않습니다. 선택적 메서드와 같은 다른 것들은 종종 재정의됩니다. 기본이 무엇을 약속하는지에 대한 충분한 사양 (문서뿐만 아니라)을 제공해야 구현자가이를 재정의해야하는지 여부에 대해 현명한 결정을 내릴 수 있습니다.