똑같은 삽질은 2번 하지 말자

Java 8 인터페이스의 변화 본문

Java

Java 8 인터페이스의 변화

곽빵 2020. 10. 25. 15:10

한 인터페이스를 여러 클래스에서 구현을 하고 있다고 한다면,

인터페이스에 기능 추가를 위해 메소드를 선언하면 구현하고 있는 모든 클래스들도 구현을 해야한다.

그 말은 그 클래스들 중에 나는 이 기능이 필요없음에도 구현을 해야하는 불필요한 코드들이 추가해야 한다.

그럼 어떻게 해야 불필요한 코드들을 작성하지 않고 인터페이스에 기능추가를 할 수 있을까?

 

 

기본 메소드 (Default Methods)

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

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

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

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

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

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

    • 구현체가 재정의해야 한다.

  • 본인이 수정할 수 있는 인터페이스에만 기본 메소드를 제공할 수 있다.

  • 인터페이스를 상속받는 인터페이스에서 다시 추상 메소드로 변경할 수 있다.

  • 인터페이스 구현체가 재정의 할 수도 있다.

스태틱 메소드

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

 

 

기본 메소드로 구현한 대표적인 친구들

public interface Iterable<T> {
    /**
     * Returns an iterator over elements of type {@code T}.
     *
     * @return an Iterator.
     */
    Iterator<T> iterator();

    
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

   
    default Spliterator<T> spliterator() {
        return Spliterators.spliteratorUnknownSize(iterator(), 0);
    }
}

사용해 볼까?

public class GHW {
	public static void main(String[] args) {
		
		List<String> name = new ArrayList<>();
		name.add("GHW");
		name.add("JJang1");
		name.add("JJang2");
		name.add("Man");
		
		// Parameter 형태는 Consumer<T> Functional Interface다.
		name.forEach((s) -> System.out.println(s));
		
		System.out.println("=============");
		
		// 함수 래퍼런스 적용
		name.forEach(System.out::println);
		
		Spliterator<String> spliterator = name.spliterator(); // iterator인데 쪼갤수 있는 iterator
		Spliterator<String> spliterator1 = spliterator.trySplit();
		
		System.out.println("=============");
		
		while(spliterator.tryAdvance(System.out::println)); // JJang2 Man
		System.out.println("=============");
		while(spliterator1.tryAdvance(System.out::println)); // GHW JJang1
	}
}

 

 

이렇게 default 기본 메소드들 덕분에 Java API에도 많은 변화들이 있었다고 한다.

 

 

예를 들면 WebMvcConfigurer 인터페이스인데,

/**
 * Defines callback methods to customize the Java-based configuration for
 * Spring MVC enabled via {@code @EnableWebMvc}.
 *
 * <p>{@code @EnableWebMvc}-annotated configuration classes may implement
 * this interface to be called back and given a chance to customize the
 * default configuration.
 *
 * @author Rossen Stoyanchev
 * @author Keith Donald
 * @author David Syer
 * @since 3.1
 */
public interface WebMvcConfigurer {

	/**
	 * Helps with configuring HandlerMappings path matching options such as trailing slash match,
	 * suffix registration, path matcher and path helper.
	 * Configured path matcher and path helper instances are shared for:
	 * <ul>
	 * <li>RequestMappings</li>
	 * <li>ViewControllerMappings</li>
	 * <li>ResourcesMappings</li>
	 * </ul>
	 * @since 4.0.3
	 */
	default void configurePathMatch(PathMatchConfigurer configurer) {
	}

	/**
	 * Configure content negotiation options.
	 */
	default void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
	}

	//.. 이하 생략

}

 

 

이걸 상속받은 WebMvcConfigurerAdapter라는 추상클래스가 있다.

/**
 * An implementation of {@link WebMvcConfigurer} with empty methods allowing
 * subclasses to override only the methods they're interested in.
 *
 * @author Rossen Stoyanchev
 * @since 3.1
 * @deprecated as of 5.0 {@link WebMvcConfigurer} has default methods (made
 * possible by a Java 8 baseline) and can be implemented directly without the
 * need for this adapter
 */
@Deprecated
public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer {

	/**
	 * {@inheritDoc}
	 * <p>This implementation is empty.
	 */
	@Override
	public void configurePathMatch(PathMatchConfigurer configurer) {
	}

	/**
	 * {@inheritDoc}
	 * <p>This implementation is empty.
	 */
	@Override
	public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
	}

	//..이하 생략

}

Java8 이전에 WebMvcConfigurerAdapter가 있었던 이유는 WebMvcConfigurer를

바로 구현하려고 하면 저 안에 있는 수 많은 메소드들은 다 구현해야 되기 때문에

중간에 추상클래스로 구현을 하고 필요한 메소드 들만 콘크리트 클래스에서 구현해도 되게 하기 위한 기법?

이었다고 한다.  뭐 이제는 필요가 없어졌으니 Deprecated가 붙여져있다.

Comments