
인터페이스를 바꾸면 이전에 해당 인터페이스를 구현했던 모든 클래스의 구현도 고쳐야 한다.
자바 8에서는 이와 같은 문제를 해결하는 두 가지 방법을 제공한다.
첫 번째는 인터페이스 내부에 정적 메서드를 사용하는 방법이 있고 두 번째는 인터페이스의 기본 구현을 제공할 수 있도록 디폴트 메서드 기능을 사용하는 것이다.
자바 8에서는 메서드 구현을 포함하는 인터페이스를 정의할 수 있다.
즉 기존 인터페이스를 구현하는 클래스는 자동으로 인터페이스에 추가된 새로운 메서드의 디폴트 메서드를 상속받는다.
자바에서는 인터페이스 그리고 인터페이스의 인스턴스를 활용할 수 있는 다양한 정적 메서드를 정의하는 유틸리티 클래스를 활용한다.
Ex) Collections는 Collection 객체를 활용할 수 있는 유틸리티 클래스다.
자바 8에서는 인터페이스에 직접 정적 메서드를 선언할 수 있으므로 유틸리티 클래스르 없애고 직접 인터페이스 내부에 정적 메서드를 구현할 수 있다.
// 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()를 구현하였다.
}
}
클래스는 하나의 추상 클래스만 상속받을 수 있지만 인터페이스는 여러 개 구현할 수 있다.
추상 클래스는 인스턴스 변수(필드)로 공통 상태를 가질 수 있지만 인터페이스는 인스턴스 변수를 가질 수 없다.
인터페이스를 구현한 클래스 중에 잘 사용하지 않는다면 메서드를 내용을 비어두기도 한다.
하지만 디폴트 메서드를 사용하면 빈 구현을 제공할 필요가 없어진다.
아래는 이에관한 예시다.
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에서는 디폴트 메서드가 추가되었으므로 같은 시그니처를 갖는 디폴트 메서드를 상속받는 상황이 생길 수 있다.
그때 적용할 수 있는 해석 규칙이다.