본문 바로가기

Java & Spring

[Java] 자바 8 Default method 정리

자바 8에서는 Interface에 Body를 가진 메소드를 추가할 수 있는데 이를 디폴트 메소드(Default method)라고 한다.  


자바 8 이전의 인터페이스에서는 API를 추가할 경우 기존 인터페이스를 구현하여 만든 애플리케이션에서는 호환성 문제가 발생하게 된다.


이를 해결하기 위해여 별도의 버전의 API를 만들어 버전을 관리하는 방법등 다양한 방법이 있으나 불편한 점이 많았다.


디폴트 메소드를 이용하면 기존 클라이언트에서도 자동으로 기본 구현이 되므로 기존 코드를 고치지 않아도 되는 장점이 있다.


아래는 자바8 Collection 인터페이스에 sort 메소드가 추가된 것을 확인할 수 있다.


Collection 인터페이스를 구현하는 자바의 클래스들은 소스의 변경 없이 sort 메소드를 이용할 수 있다.

public interface Collection {
        ...
	default void sort(Comparator<? super E> c) {
		Collections.sort(this, c);
	}
}


인터페이스에 구현된 메소드를 추가할 수 있다면 기존 추상 클래스와의 차이는 무엇일까? 


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


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

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


 

 추상 클래스

자바8 인터페이스 

 상속

 하나의 추상클래스만 상속 가능 

 여러 인터페이스를 구현 가능 

 인스턴스 변수

 인터턴스 변수로 공통 상태를 가질 수 있음

 인스턴스 변수 X 



다중 인터페이스 구현 문제


자바에서는 여러 인터페이스를 동시에 구현할 수 있다. 


자바 8에서 디폴트 메소드가 추가되면서 같은 시그너처를 갖는 여러 메소드를 상속받는 상황이 생길 수 있다. 


자바에서는 이러한 문제를 어떻게 해결할까?


아래의 3가지 규칙으로 메소드를 선택한다.


1. 클래스의 메소드가 항상 우선이다. 클래스나 슈퍼클래스에서 정의한 메소드가 인터페이스의 디폴트 메소드보다 우선이다.

public class D implements A{ }
public class C extends D implements B, A{
	public 
}

public class A {
	public void hello() {
		System.out.println("hello a")
	}
}

public interface B {
	default public void hello() {
		System.out.println("hello b")
	}
}

public class C extends A implements B {
	public static void main(String[] args) {
		new C().hello(); // 클래스가 우선이므로 hello a가 출력된다.
	}
}


2. 서브인터페이스의 메소드가 우선이다. B인터페이스가 A의 인터페이스를 상속하고 C클래스가 A와 B의 인터페이스를 구현할 경우 B의 디폴트 메소드가 우선이다.

public interface A {
	default public void hello() {
		System.out.println("hello a")
	}
}

public interface B implements A {
	default public void hello() {
		System.out.println("hello b")
	}
}

public class C implements A, B {
	public static void main(String[] args) {
		new C().hello(); // 서브 인터페이스가 우선이므로 hello b가 출력된다.
	}
}


3. 여러 인터페이스를 상속받는 클래스가 명시적으로 디폴트 메소드를 오버라이드 한다.

public interface A {
	default public void hello() {
		System.out.println("hello a")
	}
}

public interface B {
	default public void hello() {
		System.out.println("hello b")
	}
}

public class C implements A, B {
	void hello() {
		B.super.hello(); // 서로 다른 인터페이스를 구현할 때는 명시적으로 클래스.super.메소드를 호출해야 한다.
	}
}