인터페이스에 메소드 선언이 아니라 구현체를 제공하는 방법
해당 인터페이스를 구현한 클래스를 깨트리지 않고 새 기능을 추가할 수 있다.
기본 메소드는 구현체가 모르게 추가된 기능으로 리스크가 있다.
public interface Foo {
void printName();
String getName();
/**
* @implSpec
* 이 구현체는 getName()으로 가져온 문자열을 대문자로 바꿔 출력한다.
* getName()을 참조하므로, 해당 메소드에서 Null을 반환하는 경우 RuntimeException을 발생시킬 수 있습니다.
*/
default void printNameUpperCase(){
System.out.println(getName().toUpperCase());
}
}
Object가 제공하는 기능(equals, hasCode..)는 기본 메소드로 제공할 수 없고, 구현체가 재정의 해야한다.
인터페이스를 상속받는 다른 인터페이스에서 기본 메소드를 그대로 제공하기 싫은 경우 다시 추상 메소드로 변경 할 수 있다.
서로 다른 인터페이스에서 같은 이름의 기본 메소드를 가지고 있고, 그 서로 다른 인터페이스를 모두 구현하는 구현체의 경우
충돌이 발생하므로, 기본 메소드를 직접 재정의 해주어야 한다.
public class DefaultFoo implements Foo, Bar{
String name;
public DefaultFoo(String name) {
this.name = name;
}
/*
* Foo, Bar 인터페이스 모두 printNameUpperCase 디폴트 메소드를 가지고 있는 경우
* 컴파일러는 어떤 메소드를 사용해야될지 판단할 수 없기 때문에, 직접 재정의해줘야 한다.
* */
@Override
public void printNameUpperCase() {
System.out.println("디폴트 메소드를 재정의한 메소드");
}
@Override
public void printName() {
System.out.println(this.name);
}
@Override
public String getName() {
return this.name;
}
}
public interface Foo {
void printName();
// 스태틱 메소드
static void printAnything(){
System.out.println("Foo");
}
}
Java8 이전에는 인터페이스 내 하나의 메소드를 사용하기 위해서는 해당 인터페이스 안에 있는 메소드를 모두 구현해야 되는 번거로움이 있었습니다.
그래서 편의성을 위해서 해당 인터페이스를 구현한 추상클래스를 만들고, 그 추상클래스를 상속받아 필요한 메소드만 재정의 하여 사용하는 개발방식이 있었습니다.
하지만, Java8 에서 인터페이스의 기본 메소드가 추가되고 난 후에는 추상클래스를 생성하여 상속받는 형태의 개발을 하지 않아도 기본 메소드로 선언해주고, 상속이 아닌 구현체로서 필요한 메소드만 재정의하여 사용할 수 있게 되었습니다.
예를 들면, Java8 이전에는 WebMvcConfigurer 인터페이스를 구현한 추상클래스 WebMvcConfigurerAdapter를 상속받아 사용해왔지만,
Java8 이후에 해당 인터페이스 내의 메소드들이 기본 메소드로 선언이 되면서 WebMvcConfigurerAdapter 추상클래스를 상속받아 사용할 일이 없어져 해당 클래스는 Deprecated 되었습니다.
➕ Default Methods
➕ Evolving Interfaces
➕ Iterable
➕ Spliterator
➕ Comparator