JAVA 8(2) - 기본 메소드와 스태틱 메소드

2 분 소요

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

자바의 인터페이스 기능은 추상메소드와는 다릴 메소드 선언만 할 뿐 그 내부에 로직이 들어가는 일이 없었다. 하지만 java8부터 인터페이스에도 메소드 선언이 아니라 구현이 가능해졌는데 이때 default methodstatic method 방법이 있다.

기본 메소드 (Default Method)

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

기존에 이름을 출력하는 PringName인터페이스가 있었고 많은 클래스에서 해당 인터페이스를 상속받고 있었다고 가정할 때,

PringName에 대문자로 이름을 출력하는 새로운 가상메소드를 선언한다면 상속받은 많은 클래스에서 구현을 하지않았기 때문에 에러가 발생할 것이다.

public interface PrintName {
	void pringName();
    
    // void printNmaeUpperCase();
}

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

public interface PrintName {
    void printName();

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

하지만 위 코드처럼 기본 메소드인 printNameUpperCase()는 구현체가 구현한 getName()메소드를 호출해서 toUpperCase()를 호출하지만, 구현체의 getName()에서 null이 반환된다면 에러가 발생할지도 모른다.

그렇기 때문에 @implSpec 애노테이션을 사용해서 문서화를 해야한다,

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

추가로 기본 메소드는 위의 클래스처럼 사용자가 직접 정의한 인터페이스에서만 사용이 가능하며 외부 라이브러리등에 추가해 사용하는 것은 불가능하다.

✔ 만약 인터페이스를 상속하는 인터페이스에서는 기본 메소드가 필요 없는 경우
	/**
     * @implSpec
     * 이 구현체는 한국어 인삿말을 출력합니다.
     */
    default void hello(){
        System.out.println("안녕하세요");
public interface Bar extends PrintName{
    void hello();
}

👉 해당 인터페이스는 이 메소드를 다시 추상 메소드로 변경하면 된다.

✔ 구현체가 두 개의 인터페이스를 상속받았을 때 동일한 기본메소드가 있는 경우

Error: DefaultFoo inherits unrelated defaults for hello() from types Foo and Bar

어느 기본메소드인지 모호하기 때문에 에러가 발생한다.

👉 이 경우 직접 오버라이딩을 해서 두 개의 인터페이스에서 모두 가지고 있는 메소드를 재정의 해줘야 한다.

스태틱 메소드 (Static Method)

  • 해당 타입 관련 헬퍼 또는 유틸리티 메소드를 제공할 때 인터페이스에 스태틱 메소드를 제공할 수 있다.
public interface Foo {
    static void helloAll(){
        System.out.println("인삿말");
    }
}
public class App {
    public static void main(String[] args) {
        //static 메소드 사용
        Foo.helloAll();
    }
}
# 출력결과
> 인삿말

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

Collection

- foreEach

조금더 쉽게 순회를 가능하게 해주는 메소드

public static void main(String[] args) {
    List<String> names = new ArrayList<>();
    names.add("jinsoo");
    names.add("jin");
    names.add("soo");
    names.add("bae");

    names.forEach(System.out::println);
		
	/* 기존의 for of 문을 사용한 방법
	for (String name : names) {
        System.out.println(name);
    }*/
}
- spliterator
public static void main(String[] args) {
    List<String> names = new ArrayList<>();
    names.add("jinsoo");
    names.add("jin");
    names.add("soo");
    names.add("bae");

    Spliterator<String> spliterator = names.spliterator();
    Spliterator<String> trySplit = spliterator.trySplit();
    while(spliterator.tryAdvance(System.out::println));
    System.out.println("=========================");
    while(trySplit.tryAdvance(System.out::println));
}
# 출력 결과
soo
bae
=======
jinsoo
jin
  • Spliterator<String> spliterator = names.spliterator();

    분할할 수 있는 기능

  • Spliterator<String> trySplit = spliterator.trySplit();

    해당 spliterator에서 절반의 요소를 꺼내 새로운 splierator를 만들어 반환함

  • spliterator.tryAdvance(System.out::println)

    내부 메소드에 파라미터로 Functional Interface인 Consumer 가 들어오며 더 이상 들어올게 없을 경우 false를 반환

    이 예제에서는 들어온 값을 단순히 출력하는 메소드 레퍼런스로 사용함

- removeIf

Collection 요소를 순회하며 인자로 넘겨주는 함수를 Functional Interface인 Predicate에 넘겨서 true를 반환하는 값을 삭제함

public static void main(String[] args) {
    List<String> names = new ArrayList<>();
    names.add("jinsoo");
    names.add("jin");
    names.add("soo");
    names.add("bae");
    
    names.removeIf(s -> s.startsWith("s"));
    names.forEach(System.out::println);
}
  • names.removeIf(s -> s.startsWith("s"));

    names를 순회하며 s로 시작하는 단어를 찾아 삭제함

Comparator

정렬에 사용되는 인터페이스

- reversed
public static void main(String[] args) {
    List<String> names = new ArrayList<>();
    names.add("jinsoo");
    names.add("jin");
    names.add("soo");
    names.add("bae");
    
    Comparator<String> compareToIgnoreCase = String::compareToIgnoreCase;
    names.sort(compareToIgnoreCase.reversed());
    names.forEach(System.out::println);
}

카테고리:

업데이트:

댓글남기기