Link Search Menu Expand Document

섹션 2. 인터페이스의 변화

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

기본 메소드 (Default Methods)

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

  • 본인이 수정할 수 있는 인터페이스에만 기본 메소드를 제공할 수 있다.
  • 인터페이스를 상속받는 인터페이스에서 다시 추상 메소드로 변경할 수 있다.
    • 상속받은 인터페이스에서 기본 메소드를 추상 메소드로 변경할 경우 이 인터페이스를 상속받는 구현체는 전부 메소드를 재정의해야 한다.
  • 인터페이스 구현체가 재정의 할 수도 있다.

스태틱 메소드

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

예제

Foo.java

public interface Foo {

    void printName();

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

    String getName();

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

Bar.java - 1

public interface Bar extends Foo{
    
    void printNameUpperCase(); // 이 인터페이스를 상속받는 클래스는 메소드를 재구현해야 함
}

Bar.java - 2

public interface Bar{

    /**
     * @implSpec
     * 이 구현체는 getName()으로 가져온 문자열을 대문자로 바꿔 출력한다.
     * */
    default void printNameUpperCase() {
        System.out.println("BAR");
    }
}

DefaultFoo.java

public class DefaultFoo implements Foo {
// public class DefaultFoo implements Foo, Bar { 2번째 Bar를 상속받을 경우 컴파일 에러 발생 (Diamond Problem) 

    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;
    }
}

Practice1.java

public class Practice1 {

    public static void main(String[] args) {
        Foo practice1 = new DefaultFoo("melt");
        practice1.printName(); // melt
        practice1.printNameUpperCase(); // MELT

        Foo.printAnything(); // Foo
    }
}

자바 8 API의 기본 메소드와 스태틱 메소드

Iterable의 기본 메소드

BaseCode

import java.util.ArrayList;
import java.util.List;
import java.util.Spliterator;

public class Practice2 {

    public static void main(String[] args) {
        List<String> name = new ArrayList<>();
        name.add("MELT");
        name.add("keesun");
        name.add("melodist");
        name.add("toby");
  • forEach()
        name.forEach(System.out::println); // method reference
		
		// 위의 코드는 아래 코드와 똑같이 동작한다.
        for (String n: name) {
            System.out.println(n);
        }
  • spliterator(): stream()과 조합하여 병렬 처리에 이용
        Spliterator<String> spliterator = name.spliterator();
        Spliterator<String> spliterator1 = spliterator.trySplit();
        while (spliterator.tryAdvance(System.out::println));
        System.out.println("============");
        while (spliterator1.tryAdvance(System.out::println));

Collection의 기본 메소드

stream() / parallelStream()

        long k = name.stream().map(String::toUpperCase)
                .filter(s -> s.startsWith("K"))
                .count();
        System.out.println(k); // 1

removeIf(Predicate)

		name.removeIf(s -> s.startsWith("k")); // name = ["MELT, melodist, toby"]

Comparator의 기본 메소드 및 스태틱 메소드

reversed()

        Comparator<String> compareToIgnoreCase = String::compareToIgnoreCase;
        name.sort(compareToIgnoreCase.reversed());

        name.forEach(System.out::println); 
/* 
toby
MELT
melodist
keesun
*/

thenComparing()


● static reverseOrder() / naturalOrder() ● static nullsFirst() / nullsLast() ● static comparing()

API 라이브러리의 변화

Java 8 이전에는…

01 API 라이브러리의 변화 - 1

  • Interface : 메소드 a(), b(), c()를 모두 제공하는 인터페이스

  • AbstractClass : Abstract method (UML에서 이탤릭체로 표현)를 제공하는 추상 클래스

  • ClassA, ClassB, ClassC : 하나의 메소드만 상속받는 실제 클래스

Java 8 이전에는 클래스가 인터페이스를 상속받으면 인터페이스가 제공하는 메소드를 모두 구현해야 하기 때문에 클래스가 원하는 함수만 상속받을 수 있도록 인터페이스의 모든 메소드를 추상 메소드로 구현한 추상 클래스를 제공하였다.

Java 8 이후

01 API 라이브러리의 변화 - 2

추상 클래스를 상속받지 않고 기본 메소드를 사용하는 인터페이스를 직접 구현한다.