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

기본메소드

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

 

스태틱 메소드

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

 

위의 내용들을 코드로 이해해보자.

package me.qazyj.java8;

public interface FooInterface {
    void printName();

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

    String getName();
}
package me.qazyj.java8;

public class DefaultFoo implements FooInterface{
    String name;

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

    // 재정의도 가능하다.
    @Override
    public void printNameUpperCase(){
        System.out.println(getName().toUpperCase());
    }

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

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


}

위의 코드에서 기본 메소드란 printNameUpperCase()를 말한다. name을 대문자로 출력하는 기능을 추가했지만, 인터페이스에만 코드를 추가해주면 된다. (default 선언 필수!! 하지 않으면 컴파일 에러가 발생한다.) 물론, 재정의도 가능하다. 하지만, 이전에 리스크가 있다는 말을 했다. 이유는 name이 Null값인 경우 NullPointException과 같은 에러가 발생할 수 있다. 개발자도 사람인지라 정리를 잘 해놓지 않으면 후에 에러를 알게되었을 때 찾기 쉽지 않을 수 있다. (꼭! 문서화 하여 정리하자! 정리 방법은 위의 코드를 보면 예시가 있다.)

 

그렇다면, 두개의 interface를 구현하는 구현체가 있다고 가정해보자. 두개의 interface에는 같은 이름으로된 기본메소드가 있다면 어떻게 될까?? 당연히 컴파일에러가 발생한다. 해결 방법으로는 간단하다. 하나의 인터페이스 기본메소드를 없애주거나, 구현체에서 기본메소드를 재정의해주면 된다. 

 

Object가 제공하는 기능 (equals, hasCode)은 기본 메소드로 제공할 수 없다고했다. 직접 확인해보자.

기본메소드로 선언하면 컴파일 에러가 뜬다.
구현체에서 재정의해서는 사용할 수 있다.

위를 보면 기본메소드로는 선언이 불가하고, 구현체에서 재정의해서는 사용할 수 있는 것을 볼 수 있다.

 

 

static 메소드는 어떻게 사용할까.

package me.qazyj.java8;

public interface FooInterface {
    
    ...

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

    ...
}
package me.qazyj.java8;

public class FooTest {
    public static void main(String[] args) {
        DefaultFoo foo = new DefaultFoo("kyj");
        foo.printName();
        foo.printNameUpperCase();

        FooInterface.printAnything();		// 생성해주지 않아도 바로 사용가능하다.
    }
}

static으로 선언해주면 별다른 생성없이 바로 사용할 수 있는 것을 볼 수 있다.

 

 

 

 

자바 8에서 추가한 기본 메소드로 인한 API 변화

Iterable의 기본 메소드

  • forEach()
  • spliterator()

 

Collection의 기본 메소드

  • stream() / parallelStream()
  • removeIf(Predicate)
  • spliterator()

 

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

  • reversed()
  • thenComparing()
  • static reverseOrder() / naturalOrder()
  • static nullsFirst() / nullsLast()
  • static comparing()

위의 내용을 코드로 이해해 보자.

 

    public static void main(String[] args) {

        List<String> name = new ArrayList<>();
        name.add("kyj");
        name.add("yj");
        name.add("qazyj");


        System.out.println("=====Iterable=====");
        // Iterable
        name.forEach(System.out::println);		// 출력 kyj + "\n" + yj + "\n" + qazyj

        System.out.println();

        Spliterator<String> spliterator = name.spliterator();
        Spliterator<String> spliterator2 = spliterator.trySplit();// 앞에서부터 반으로 쪼갠걸 가져옴 (나머지 반은 원래 변수에)
        while(spliterator2.tryAdvance(System.out::println));	// 출력 kyj
        System.out.println("==========");
        while(spliterator.tryAdvance(System.out::println));		// 출력 yj + "\n" + qazyj



        System.out.println("=====Collection=====");
        //Collection
        name = new ArrayList<>();
        name.add("kyj");
        name.add("yj");
        name.add("qazyj");

        long count = name.stream().map(String::toUpperCase)
                .filter(s -> s.startsWith("K"))
                .count();
                //.collect(Collectors.toSet());
        System.out.println(count);		// 출력 1   (kyj 하나만 있기 때문) 

        name.removeIf(s -> s.startsWith("k"));
        name.forEach(System.out::println);		// 출력 yj + "\n" + qazyj


        System.out.println("=====Comparator=====");
        //Comparator
        name = new ArrayList<>();
        name.add("kyj");
        name.add("yj");
        name.add("qazyj");

        name.sort(String::compareToIgnoreCase); // 오름차순
        Comparator<String> compareToIgnoreCase = String::compareToIgnoreCase;
        name.sort(compareToIgnoreCase.reversed());  // 내림차순 추가적으로 더 정렬하고 싶으면 .thenComparing() 사용
        

    }

 

 

 

 

 

'JAVA' 카테고리의 다른 글

[Java 8] Optional  (0) 2022.10.07
[JAVA 8] Stream  (0) 2022.10.06
[JAVA 8] 함수형 인터페이스와 람다 표현식  (0) 2022.09.23
ListIterator  (0) 2022.09.23
SnakeGame  (0) 2022.07.16

+ Recent posts