자바/모던 자바 인 액션

[모던 자바 인 액션] 13장. 디폴트 메서드

Rudtjs 2023. 2. 24. 23:03

자바에서 인터페이스와 메서드는 한 몸처럼 구성된다. 인터페이스를 구현하는 클래스는 인터페이스에서 정의하는 모든 메서드 구현을 제공하거나 슈퍼클래스의 구현을 상속받아야 한다. 하지만 인터페이스를 수정하려고 할 때 문제가 발생한다.

 

자바 8에서는 이 문제를 해결하기 위해 정적 메서드디폴트 메서드 기능을 사용하여 해결했다.

 

디폴트 메서드란 무엇인가?

인터페이스를 구현하는 클래스에서 구현하지 않은 메서드는 인터페이스 자체에서 기본으로 제공한다. 그래서 이를 디폴트 메서드라고 부른다. 

 

디폴트 메서드를 어떻게 알아볼 수 있을까? 

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

public interface Sized {
	int size();
	default boolean isEmpty() { //디폴트 메서드
		return size() == 0;
	}
}

 

디폴트 메서드 활용

우리가 만든 인터페이스에도 디폴트 메서드를 추가할 수 있다.

  • 선택형 메서드
  • 동작 다중 상속

선택형 메서드

디폴트 메서드를 이용하면 메서드의 기본 구현을 제공할 수 있으므로 구현 클래스에서 빈 구현을 만들 필요가 없다.

Iterator 인터페이스의 경우 많은 사용자들이 remove 기능을 잘 사용하지 않으므로 default 메서드로 제공한다.

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

 

동작 다중 상속

다중 상속을 이용해 기존 코드를 재사용할 수 있다.

자바에서는 인터페이스가 구현을 포함할 수 있으므로 클래스는 여러 인터페이스에서 동작을 상속받을 수 있다.

 

예시

우리가 만드는 게임에서 어떤 모양은 회전 o, 크기 조절 o, 어떤 모양은 회전 o, 크기 조절 x 

이 문제를 기존 코드를 재사용해서 만들려면 어떻게 해야 할까?

public interface Rotatable {
	void setRotationAngle(int angleInDegrees);
	int getRotationAngle();
	default void rotateBy(int angleInDegrees) { //디폴트 메서드
		setRotationAngle((getRotationAngle() + angleInDegrees) % 360);
	}
}

 

Rotatable 인터페이스를 구현하는 클래스는 setRotationAngle과 getRotationAngle의 구현을 제공해야 하지만, rotateBy는 기본 구현이 제공되므로 구현을 제공하지 않아도 된다.

public interface Movable {
	int getX();
	int getY();
	void setX(int x);
	void setY(int y);

	default void moveVertically(int distance) {
		setY(getY() + distance);
	}
}
public interface Resizable {
  int getWidth();
  int getHeight();
  void setWidth(int width);
  void setHeight(int height);
  void setAbsoluteSize(int width, int height);
  
  default void setRelativeSize(int wFactor, int hFactor) {
    setAbsoluteSize(getWidth() / wFactor, getHeight() / hFactor);
  }
}

위와 같이 회전, 이동, 크기조절 기능을 담당하는 인터페이스를 만들 수 있다.

 

 알아야 할 세 가지 해결 규칙

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