디폴트 메서드

김종준·2023년 2월 24일
0

모던자바

목록 보기
10/15

디폴트 메서드

인터페이스를 바꾸면 이전에 해당 인터페이스를 구현했던 모든 클래스의 구현도 고쳐야 한다.

자바 8에서는 이와 같은 문제를 해결하는 두 가지 방법을 제공한다.

첫 번째는 인터페이스 내부에 정적 메서드를 사용하는 방법이 있고 두 번째는 인터페이스의 기본 구현을 제공할 수 있도록 디폴트 메서드 기능을 사용하는 것이다.

자바 8에서는 메서드 구현을 포함하는 인터페이스를 정의할 수 있다.

즉 기존 인터페이스를 구현하는 클래스는 자동으로 인터페이스에 추가된 새로운 메서드의 디폴트 메서드를 상속받는다.


정적 메서드와 인터페이스

자바에서는 인터페이스 그리고 인터페이스의 인스턴스를 활용할 수 있는 다양한 정적 메서드를 정의하는 유틸리티 클래스를 활용한다.

Ex) Collections는 Collection 객체를 활용할 수 있는 유틸리티 클래스다.

자바 8에서는 인터페이스에 직접 정적 메서드를 선언할 수 있으므로 유틸리티 클래스르 없애고 직접 인터페이스 내부에 정적 메서드를 구현할 수 있다.


API

// API 1
public interface Resizable extends Drawable {
  int getWidth();
  int getHeight();
  ...
  void setAbsoluteSize(int width, int height);
}

위와 같은 API에 아래와 같이 setRelativeSize가 추가 되었다고 생각해보자.

// API 2
public interface Resizable extends Drawable {
  int getWidth();
  int getHeight();
  ...
  void setAbsoluteSize(int width, int height);
  void setRelativeSize(int wFactor, int hFactor);
}

이미 첫 번째 Resizable를 구현한 클래스는 추가된 setRelativeSize를 구현해야 한다.

하지만 setRelativeSize가 필요하지 않아 구현하지 않을 수도 있다.

이때 바이너리 호환성은 유지 된다.(뭔가를 바꾼 이후에도 에러 없이 기존 바이너리가 실행될 수 있는 상황)

하지만 누군가 setRelativeSize를 사용하게 된다면 런타임 에러를 만나게 될 것이다.

또 전체 애플리케이션을 재빌드시 컴파일 에러가 발생한다.

디폴트 메서드

디폴트 메서드는 호환성을 유지하면서 API를 바꿀 수 있도록 도와주는 새로운 기능이다.

디폴트 메서드를 통해서 인터페이스는 자신을 구현하는 클래스에서 메서드를 구현하지 않을 수 있는 새로운 메서드 시그니처를 제공한다.

인터페이스를 구현하는 클래스에서 구현하지 않는 메서드는 인터페이스 자체에서 기본으로 제공한다.

그렇기에 디폴트 메서드는 default라는 키워드로 시작하며 다른 클래스에 선언된 메서드처럼 메서드 바디를 포함한다.

public interface Sized {
  int size();
  default boolean isEmpty() {
    return size() == 0; // 이미 선언된 size()를 통해 default 메서드 isEmpty()를 구현하였다.
  }
}

추상 클래스와 자바 8의 인터페이스

클래스는 하나의 추상 클래스만 상속받을 수 있지만 인터페이스는 여러 개 구현할 수 있다.

추상 클래스는 인스턴스 변수(필드)로 공통 상태를 가질 수 있지만 인터페이스는 인스턴스 변수를 가질 수 없다.


디폴트 메서드 활용 패턴

선택형 메서드

인터페이스를 구현한 클래스 중에 잘 사용하지 않는다면 메서드를 내용을 비어두기도 한다.

하지만 디폴트 메서드를 사용하면 빈 구현을 제공할 필요가 없어진다.

아래는 이에관한 예시다.

interface Iterator<T> {
  boolean hasNext();
  T next();
  default void remove() {
    throw new UnsupportedOperationException();
  }
}

동작 다중 상속

다중 상속 형식

ArrayList는 한 개의 클래스르 상속받고, 여섯 개의 인터페이스를 구현한다.

결과적으로 ArrayList는 AbstractList, List, RandomAccess, Cloneable, Serializable, Iterable, Collection의 서브형식이 된다.

따라서 디폴트 메서드를 사용하지 않아도 다중 상속을 활용할 수 있다.

기능이 중복되지 않는 최소의 인터페이스
public interface Calculate {
  int plus(int a, int b);
  int multiple(int a, int b);
  
  int multipleAndPlus(int a, int b, int c) {
    return plust(multiple(a, b), c);
  }
}

plus그리고 multiple만 구현하다면 multipleAndPlus는 기본 구현이 제공되므로 따로 구현을 제공하지 않아도 된다.

해석 규칙

자바 8에서는 디폴트 메서드가 추가되었으므로 같은 시그니처를 갖는 디폴트 메서드를 상속받는 상황이 생길 수 있다.

그때 적용할 수 있는 해석 규칙이다.

  1. 클래스가 항상 이긴다.
    클래스나 슈퍼클래스에서 정의한 메서드가 디폴트 메서드보다 우선권을 갖는다.
  2. 1번 규칙 이외의 상황에서는 서브인터페이스가 이긴다.
    B가 A를 상속 받는다면 B가 A를 이긴다.
  3. 우선순위가 결정되지 않는다면 여러 인터페이스를 상속받는 클래스가 명시적으로 디폴트 메서드를 오버라이드하고 호출해야 한다.

0개의 댓글