반응형

자바8 인터페이스 변화

기본 메소드 (Default Methods)

  • 인터페이스에 메소드 선언이 아니라 구현체를 제공하는 방법
  • 해당 인터페이스를 구현한 클래스를 깨트리지 않고 새 기능을 추가할 수 있다.
  • 기본 메소드는 구현체가 모르게 추가된 기능으로 그만큼 리스크가 있다.
    • 컴파일 에러는 아니지만 구현체에 따라 런타임 에러가 발생할 수 있다.
    • 기본 메소드가 항상 모든 상황에 동작한다는 보장이 없기 때문에 명시적으로 어떤 동작을 하는지 반드시 문서화 해야한다. (@implSpec 자바독 태그 사용)
  • Object가 제공하는 기능 (equals, hasCode)는 기본 메소드로 제공할 수 없다.
    • 구현체가 재정의해야 한다.
  • 본인이 수정할 수 있는 인터페이스에만 기본 메소드를 제공할 수 있다.
  • 인터페이스를 상속받는 인터페이스에서 다시 추상 메소드로 변경할 수 있다.
  • 인터페이스 구현체가 재정의 할 수도 있다.

 

스태틱 메소드

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

인터페이스 예제

  • Goo.java
public interface Goo {

    void printName();
    String getName();

    /*
        나중에 해당 인터페이스로 공통적인 기능을 제공해주었으면 좋겠다고하여 추가한 추상 메소드 : printNameUpperCase()
    */
    //void printNameUpperCase(); 그러나 해당 형태로 추가하면 해당 인터페이스를 구현한 모든 클래스에서 컴파일 에러 발생

    /*
        java8에서 등장한 default 키워드를 이용하여 메소드를 추가하면 해당 인터페이스를 구현하는 클래스에서 메소드를 구현하지 않아도 사용 가능하다.
        하지만 이 기능이 항상 모든 상황에 동작한다는 보장이 없다. 현재는 getName()에서 null을 리턴하면 런타임 에러가 발생할 수 있다.
        문서화(@implSpec 자바독 태그)를 통해 이 기본 구현체가 어떤 일을하는지 명시해야한다.

        인터페이스의 기본 메소드(default 메소드)의 제약사항
        - Object가 제공하는 메소드(equals, hasCode 등)들은 인터페이스의 기본 메소드로 제공할 수 없다.
          단, 추상 메소드로 선언하는 것은 상관이 없다.
    */
    /**
     * @implSpec 이 구현체는 getName()으로 가져온 문자열을 소문자로 바꿔 출력한다.
     */
    default void printNameLowerCase(){
        System.out.println(getName().toLowerCase());
    }
    
    /**
     * @implSpec 이 구현체는 getName()으로 가져온 문자열을 대문자로 바꿔 출력한다.
     */
    default void printNameUpperCase(){
        System.out.println(getName().toUpperCase());
    }

    /*
        Object가 제공하는 기본 메소드를 인터페이스의 기본 메소드로 제공하려했기에 컴파일 에러 발생
    */
    //default String toString(){ }

    /*
        Object가 제공하는 기본 메소드이지만 추상 메소드로 선언만 하는 것은 가능하다.
        다만 이는 모든 인스턴스가 오브젝트에서 구현된 것을 상속받고 있다.
        이것을 사용하는 경우는 인터페이스에서 가지는 특별한 제약사항이 있을때 사용한다.
        예를 들어, 이 인터페이스를 구현하는 인스턴스의 toString은 일반적인 Object의 toString과는 다르다.
    */
    String toString();

    /*
        static 키워드를 이용하여 해당 타입과 관련하여 헬퍼 또는 유틸리티 메소드를 제공할 수 있다.
    */
    static void printAnything() {
        System.out.println("Goo");
    }
}

 

  • Bar.java
public interface Bar extends Goo{
    /*
        기존 Goo가 기본 메소드로 제공하던 void printNameUpperCase()를
        Bar 인터페이스에서는 더 이상 기본 메소드로 제공하고 싶지 않을 때는 추상 메소드로 다시 선언 해준다.
    */
    void printNameUpperCase();
}

 

  • Hoo.java
public interface Hoo {
    default void printNameUpperCase() {
        System.out.println("HOO");
    }
}

 

  • DefaultGoo.java
/*
    만약, 동일한 기본 메소드를 제공하는 인터페이스를 여러 개 구현한 경우 컴파일 에러 발생.
    어떤 인터페이스의 기본 메소드를 사용해야 될지 모르기 때문이다.
    이렇게 기본 메소드가 충돌하는경우는 직접 재정의(Override)해서 사용해야한다.
*/
public class DefaultGoo implements Goo, Hoo {

    String name;

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

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

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

    /*
        인터페이스에서 기본으로 제공하는 메소드가 문제가 있을 경우
        재정의(Override)하여 사용할 수 있다.
    */
    @Override
    public void printNameUpperCase() {
        System.out.println(this.name.toUpperCase());
    }
}

 

  • Main.java
public class Main {
    public static void main(String[] args){
        Goo goo = new DefaultGoo("Goo");

        /*
            default 키워드 메소드 & 추상 메소드 : 인스턴스 사용
        */
        goo.printName(); //인터페이스의 추상 메소드 구현
        goo.printNameUpperCase(); //기본 메소드의 충돌이 발생하여 인터페이스의 기본 메소드 재정의
        goo.printNameLowerCase(); //인터페이스의 기본 메소드

        /*
            static 키워드 메소드 : 해당 인터페이스 타입에서 사용
        */
        Goo.printAnything(); //인터페이스의 헬퍼, 유틸리티 메소드
    }
}

[참고자료]

더 자바, Java 8, 백기선

The Java™ Tutorials - Evolving Interfaces

The Java™ Tutorials - Default Methods

반응형

+ Recent posts