자바 기본 메소드 사용법


13

수십 년 동안이 인터페이스가 있다고 경우있었습니다 유일한 방법 서명을 지정 (만). 우리는 이것이 "일을하는 올바른 방법"이라고 들었습니다.

그런 다음 Java 8이 나와서 말했습니다.

음, 이제 기본 방법을 정의 할 수 있습니다. 잘 지내요.

숙련 된 Java 개발자와 최근에 개발 한 (최근 몇 년간) 개발자들에 의해 이것이 어떻게 소화되고 있는지 궁금합니다. 또한 이것이 Java 정통과 연습에 어떻게 적용되는지 궁금합니다.

실험 코드를 작성 중이며 리팩토링을 수행하는 동안 표준 인터페이스 (Iterable)를 확장하고 두 가지 기본 메소드를 추가하는 인터페이스가 생겼습니다. 그리고 나는 정직 할 것이다, 나는 그것에 대해 꽤 기분이 좋다.

나는 이것이 약간 개방적이라는 것을 알고 있지만 이제 실제 프로젝트에서 Java 8을 사용할 시간이 있었으므로 기본 메소드 사용과 관련하여 정통성이 있습니까? 그들이 논의 할 때 주로 보는 것은 기존 소비자를 해치지 않고 인터페이스에 새로운 메소드를 추가하는 방법에 관한 것입니다. 그러나 위에서 설명한 예제와 같이 처음부터 이것을 사용하는 것은 어떻습니까. 인터페이스에 구현을 제공하는 데 문제가있는 사람이 있습니까?


이 관점에도 관심이 있습니다. .Net 세계에서 6 년 후 Java로 돌아갑니다. 이것은 Ruby의 모듈 메소드에 약간의 영향을 미치면서 C # 확장 메소드에 대한 Java의 대답 일 것 같습니다. 나는 그것을 가지고 놀지 않았으므로 확신 할 수 없다.
Berin Loritsch

1
기본 메소드를 추가 한 이유는 크게 다른 인터페이스를 만들지 않고도 컬렉션 인터페이스를 확장 할 수 있기 때문입니다.
Justin

1
@Justin : java.util.function.Function새로운 인터페이스에서 기본 메소드 사용법을 참조하십시오 .
Jörg W Mittag

@Justin 내 추측은 이것이 주요 드라이버 였다는 것입니다. 그들이 실제로 변경을 시작했기 때문에 프로세스에 다시주의를 기울여야합니다.
JimmyJames

답변:


12

훌륭한 사용 사례는 내가 "레버 (lever)"인터페이스라고 부르는 것입니다. 적은 수의 추상 메소드 (이상적으로는 1) 만 있지만 많은 기능을 제공한다는 점에서 많은 "레버리지"를 제공하는 인터페이스입니다. 클래스에서 1 개의 메소드를 구현해야하지만 "무료로"다른 메소드를 많이 얻으십시오. 단일 추상적으로, 예를 들어, 수집 인터페이스 생각 foreach방법 및 default방법처럼 map, fold, reduce, filter, partition, groupBy, sort, sortBy, 등

다음은 몇 가지 예입니다. 로 시작하겠습니다 java.util.function.Function<T, R>. 단일 추상 방법이 R apply<T>있습니다. 또한 두 가지 기본 방법을 사용하여 이전 또는 이후의 두 가지 방법으로 다른 함수로 함수를 구성 할 수 있습니다. 이러한 구성 방법은 모두 다음을 사용하여 구현됩니다apply .

default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
    return (V v) -> apply(before.apply(v));
}

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
    return (T t) -> after.apply(apply(t));
}

비슷한 객체에 대한 인터페이스를 다음과 같이 만들 수도 있습니다.

interface MyComparable<T extends MyComparable<T>> {
  int compareTo(T other);

  default boolean lessThanOrEqual(T other) {
    return compareTo(other) <= 0;
  }

  default boolean lessThan(T other) {
    return compareTo(other) < 0;
  }

  default boolean greaterThanOrEqual(T other) {
    return compareTo(other) >= 0;
  }

  default boolean greaterThan(T other) {
    return compareTo(other) > 0;
  }

  default boolean isBetween(T min, T max) {
    return greaterThanOrEqual(min) && lessThanOrEqual(max);
  }

  default T clamp(T min, T max) {
    if (lessThan(   min)) return min;
    if (greaterThan(max)) return max;
                          return (T)this;
  }
}

class CaseInsensitiveString implements MyComparable<CaseInsensitiveString> {
  CaseInsensitiveString(String s) { this.s = s; }
  private String s;

  @Override public int compareTo(CaseInsensitiveString other) {
    return s.toLowerCase().compareTo(other.s.toLowerCase());
  }
}

또는 Collection원래 유형이 무엇이든 관계없이 모든 컬렉션 작업이 반환되는 매우 단순화 된 컬렉션 프레임 워크 :

interface MyCollection<T> {
  void forEach(java.util.function.Consumer<? super T> f);

  default <R> java.util.Collection<R> map(java.util.function.Function<? super T, ? extends R> f) {
    java.util.Collection<R> l = new java.util.ArrayList();
    forEach(el -> l.add(f.apply(el)));
    return l;
  }
}

class MyArray<T> implements MyCollection<T> {
  private T[] array;

  MyArray(T[] array) { this.array = array; }

  @Override public void forEach(java.util.function.Consumer<? super T> f) {
    for (T el : array) f.accept(el);
  }

  @Override public String toString() {
    StringBuilder sb = new StringBuilder("(");
    map(el -> el.toString()).forEach(s -> { sb.append(s); sb.append(", "); } );
    sb.replace(sb.length() - 2, sb.length(), ")");
    return sb.toString();
  }

  public static void main(String... args) {
    MyArray<Integer> array = new MyArray<>(new Integer[] {1, 2, 3, 4});
    System.out.println(array);
    // (1, 2, 3, 4)
  }
}

이러한 "레버"인터페이스는 람다 (SAM 인터페이스)로 구현 될 수 있기 때문에 람다와 함께 사용하면 매우 흥미로워집니다.

이것은 확장 메소드가 C♯에서 추가 된 것과 동일한 유스 케이스이지만 기본 메소드에는 한 가지 뚜렷한 장점이 있습니다. "적절한"인스턴스 메소드입니다. 즉, 인터페이스의 개인 구현 세부 사항에 액세스 할 수 있습니다 ( private인터페이스 메소드가 제공됨). 확장 메소드는 정적 메소드의 구문 설탕 일뿐입니다.

Java가 Interface Injection을 받으면 형식이 안전하고 범위가 지정된 모듈 식 원숭이 패치도 가능합니다. 예를 들어 JRuby는 Java 클래스를 상속하거나 랩하여 추가 Ruby 의미를 제공하지만 이상적으로는 동일한 클래스를 사용하려고합니다. 인터페이스 주입과 기본 메소드를 사용하면 예를 들어 RubyObject인터페이스를 주입 할 수 java.lang.Object있으므로 Java Object와 Ruby Object가 완전히 동일 합니다.


1
나는 이것을 완전히 따르지 않습니다. 인터페이스의 기본 메소드는 인터페이스의 다른 메소드 또는 오브젝트에 정의 된 메소드와 관련하여 정의되어야합니다. 기본 메소드를 사용하여 의미있는 단일 메소드 인터페이스를 작성하는 방법에 대한 예제를 제공 할 수 있습니까? 시연을 위해 Java 9 구문이 필요하다면 괜찮습니다.
JimmyJames

예를 들면 : Comparable추상과 인터페이스 compareTo방법 및 기본 lessThan, lessThanOrEqual, greaterThan, greaterThanOrEqual, isBetween, 및 clamp방법, 모든 측면에서 구현 compareTo. 또는 살펴보십시오 java.util.function.Function: 추상 apply메소드와 두 가지 기본 컴포지션 메소드가 apply있습니다. Collection인터페이스에 대한 예제를 제공하려고했지만 모든 유형이 안전하도록하는 것은 까다 롭고이 답변에는 너무 길다. 유형이 안전하지 않은 유형 보존 버전을 제공하려고 노력할 것이다. 계속 지켜봐 주시기 바랍니다.
Jörg W Mittag

3
예제가 도움이됩니다. 감사. 단일 메소드 인터페이스의 의미를 잘못 이해했습니다.
JimmyJames

기본 메소드의 의미는 단일 추상 메소드 인터페이스가 더 이상 단일 메소드 인터페이스 일 필요가 없다는 것입니다. ;-)
Jörg W Mittag

나는 이것에 대해 생각하고 있었고 AbstractCollection과 AbstractList가 기본적으로 여기서 이야기하고있는 것으로 나타났습니다 (1 대신 2 가지 방법이지만 중요하지 않다고 생각합니다). 이것이 defualt 메소드와의 인터페이스로 다시 캐스팅 된 경우 크기를 추가하고 크기를 알 수 있다면 크기를 추가하고 목록에서 목록을 생성하여 iterable을 컬렉션으로 바꾸는 것이 매우 간단합니다.
JimmyJames
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.