1. 인터페이스 기본 메소드와 스태틱 메소드


지금까지 사용하던 자바의 인터페이스 기능은 추상메소드와는 달리 메소드 선언만 할 뿐 그 내부에 로직이 들어가는 일이 없었습니다. 하지만 Java 8 부터는 인터페이스에도 메소드 선언이 아니라 구현이 가능해졌는데 이 방법이 default methodstatic method 입니다.

기본 메소드(Default Methods)

public interface Foo {
    void printName();
}

public class DefaultFoo implements Foo{
    @Override
    public void printName() {
        System.out.println("DefaultFoo");
    }
}

기본적인 Foo 인터페이스와 DefaultFoo 구현체입니다. 동작도 제대로 하고있습니다.

여기서 DefaultFoo뿐아니라 DefaultFoo2, ... , DefaultFooN 까지 많은 클래스들이 Foo를 구현한다고 할 때 나중에 처음 기획과는 다르게 대문자로 이름을 출력하는 기능이 추가되야한다고 하면 어떨까요? Foo 인터페이스에 void printNameUpperCase() 라는 메소드를 선언해준다면 구현체 모두 컴파일 에러가 발생합니다. 따로 구현을 해주지 않았기 때문이죠. 기본 메소드(Default Method)는 이런 상황때문에 생겼습니다.

public interface Foo {
    void printName();
		//기본 메소드(Default Method)
    default void printNameUpperCase(){
        System.out.println("FOO");
    }
}
public static void main(String[] args){
      Foo foo = new DefaultFoo();
      foo.printName();
      foo.printNameUpperCase();
}

이처럼 기본 메소드를 사용하면 구현체에서 따로 해당 메소드를 구현하지 않아도 사용이 가능하다.

하지만, 기본 메소드는 구현체가 모르게 추가된 기능이기 때문에 구현체에서는 알 방도가 없습니다.

그렇기에 구현체에서는 기본메소드의 기능, 반환값, 요구값 등등을 모르기 때문에 문제가 생길수 있습니다.

public interface Foo {
    void printName();

    default void printNameUpperCase(){
        System.out.println(getName().toUpperCase());
    }
    String getName();
}

예를들어 위 코드에서 기본메소드인 printNameUpperCase() 에서는 구현체가 구현한 getName()메소드를 호출해 toUpperCase()를 호출하지만, 실제로 구현체의 getName()에서 무조건 문자열이 반환될지에 대해서는 확실하지 않습니다. 만약 null값이 반환된다면 에러가 발생하게 됩니다.

그렇기에 최소한으로 반드시 문서화를 해야합니다. (Java 8에 추가된 @ImplSpec)

/**
 * @ImplSpec
 * 이 구현체는 getName()으로 가져온 문자열을 대문자로 변환 후 출력한다.
 */
default void printNameUpperCase(){
    System.out.println(getName().toUpperCase());
}

만일, 이래도 문제가 된다면 구현체에서 재정의하는것 역시 가능합니다. 일반적으로 선언된 메소드와 동일하게 오버라이드 하여 기능을 정의하면 됩니다.