Java 8에서는 인터페이스에 기본(default) 메소드 구현이라는 새로운 개념이 도입되었습니다. 이 기능은 하위 호환성(backward compatibility)을 위해 추가된 것으로, 기존에 작성된 인터페이스를 수정하지 않고도 Java 8의 람다 표현식(lambda expression) 기능을 그대로 활용할 수 있게 해줍니다.
기본 메소드가 필요한 이유
예를 들어, 'List'나 'Collection' 인터페이스에는 'forEach' 메소드 선언이 존재하지 않습니다. 만약 이러한 메소드를 인터페이스에 단순히 추가한다면, 해당 인터페이스를 구현하는 모든 컬렉션 프레임워크 구현체들이 컴파일 오류를 일으키며 깨지게 됩니다.
Java 8은 여기에 기본 메소드를 도입함으로써 문제를 해결했습니다. List/Collection 인터페이스가 forEach 메소드의 기본 구현을 직접 가질 수 있게 되었고, 따라서 이 인터페이스들을 구현하는 클래스는 해당 메소드를 별도로 재정의(override)하지 않아도 됩니다.
예제 코드
아래 예제는 두 개의 인터페이스(Vehicle, FourWheeler)가 동일한 이름의 기본 메소드를 가질 때, 구현 클래스에서 이를 어떻게 처리하는지 보여줍니다.
public class Java8Tester {
public static void main(String args[]) {
Vehicle vehicle = new Car();
vehicle.print();
}
}
interface Vehicle {
default void print() {
System.out.println("I am a vehicle!");
}
static void blowHorn() {
System.out.println("Blowing horn!!!");
}
}
interface FourWheeler {
default void print() {
System.out.println("I am a four wheeler!");
}
}
class Car implements Vehicle, FourWheeler {
public void print() {
Vehicle.super.print();
FourWheeler.super.print();
Vehicle.blowHorn();
System.out.println("I am a car!");
}
}실행 결과
I am a vehicle! I am a four wheeler! Blowing horn!!! I am a car!
핵심 포인트 정리
- default 키워드: 인터페이스 내 메소드에 default 키워드를 붙이면 메소드 본문(구현)을 가질 수 있습니다.
- 정적(static) 메소드: Java 8부터 인터페이스는 static 메소드도 가질 수 있으며, 위 예제의 blowHorn()처럼 인터페이스 이름으로 직접 호출합니다.
- 충돌 해결 방법: 여러 인터페이스가 같은 시그니처의 기본 메소드를 가질 경우, 구현 클래스는 반드시 해당 메소드를 재정의해야 합니다. 이때
Vehicle.super.print()와 같은 문법으로 특정 인터페이스의 기본 구현을 명시적으로 호출할 수 있습니다. - 다중 상속 문제 완화: 이러한 방식은 다이아몬드 상속(diamond inheritance) 문제를 개발자가 명확하게 제어할 수 있게 해주어, 인터페이스 진화와 API 확장을 안전하게 지원합니다.