스프링 스터디 (인프런)/더 자바 8

인터페이스의 변화 - 인터페이스 기본 메소드와 스태틱 메소드

백엔드 개발자 2023. 4. 8. 00:24

 

 

 

package me.whiteship.java8to11;

public interface Foo {
    void printName();

    default void printNameUpperCase() {
        System.out.println("FOO");
    }
}

기본 메소드 (Default Methods)

     인터페이스에 메소드 선언이 아니라 구현체를 제공하는 방법

     해당 인터페이스를 구현한 클래스를 깨트리지 않고 새 기능을 추가할 수 있다.

인터페이스를 만들다 보면 나중에 인터페이스를 수정할 때 구현체에 모두 구현해줘야 하는 불편함이 있었는데,

default 메서드를 생성해서 이를 대처할 수 있다.

 

 

Foo 인터페이스

public interface Foo {
    void printName();

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

    String getName();

}

 printNameUpperCase라는 기본 메서드를 생성했다.

 

 

Foo 구현체 DefaultFoo 

public class DefaultFoo implements Foo{

    String name;

    public DefaultFoo(String name) {
        this.name = name;
    }


    @Override
    public void printName() {
        System.out.println(this.name);
    }

    @Override
    public String getName() {
        return this.name;
    }
}

Foo 메서드를 상속받았어도 printNameUpperCase를 구현하지 않아도 된다.

 

 

App 클래스

Foo foo = new DefaultFoo("keesun");
foo.printName();
foo.printNameUpperCase();

구현체의 메서드 printName, 

인터페이스의 기본 메서드 printNameUpperCase 모두 정상적으로 출력된다.

 

 

주의점!

     기본 메소드는 구현체가 모르게 추가된 기능으로 그만큼 리스크가 있다.

 

     컴파일 에러는 아니지만 구현체에 따라 런타임 에러가 발생할 수 있다.

예를 들어 getName()의 구현체가 null을 리턴하면 런타임 에러가 발생할 것이다.

 

     반드시 문서화 할 것. (@implSpec 자바독 태그 사용)

 

 

 

구현체인 DefaultFoo가 재정의하는 방법도 있다.

 

 

     Object가 제공하는 기능 (equals, hasCode)는 기본 메소드로 제공할 수 없다.

   

toString을 기본메서드로 제공할 수 없음. Object가 제공하는 기능이므로

단, 인터페이스에 추상메서드로 선언하는 것은 상관없다.

String toString();

 

 

 

public interface Bar extends Foo{
    
    void printNameUpperCase();

}

만약 인터페이스(Foo)를 확장한 인터페이스(Bar)에서 Foo의 기본메서드였던 printNameUpperCase()를 사용하고 싶지 않다면, 다시 추상메서드로 재정의하면 된다. 이러면 Bar를 상속받는 구현체들은 저 메서드를 다시 구현해야 한다.

 

 

 

상속받은 2개의 인터페이스가 동일한 이름의 기본메서드를 가졌을 경우

둘중 어떤것을 써야할 지 몰라 컴파일 에러가 발생한다.

이 경우는 직접 해당 메서드를 구현해야 한다.

 

 

 

 

 

스태틱 메소드

     해당 타입 관련 헬퍼 또는 유틸리티 메소드를 제공할 때 인터페이스에 스태틱 메소드를 제공할 수 있다.


static void printAnything() {
    System.out.println("Foo");
}

Foo 인터페이스에 스태틱 메서드를 추가한다.

 

 

 

public class App {
    public static void main(String[] args) {
        
  /*      Foo foo = new DefaultFoo("keesun");
        foo.printName();
        foo.printNameUpperCase();
*/
        Foo.printAnything();
    }
}

 

main에서 출력시 인스턴스 생성 없이도 Foo를 출력하는 것을 확인.

 

 

 

기본메서드는 실무에서 인터페이스를 유지보수할 때 변화를 줄이는데 좋은 방식중 하나가 될 수 있을 것 같다.

 

 

 

참고

     https://docs.oracle.com/javase/tutorial/java/IandI/nogrow.html

     https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html