1. 인터페이스 Predicate<T>

자바 8인 액션에서 람다표현식과 관련된 부분을 읽다가

함수형 인터페이스@FunctionalInterface 에는 메서드가 하나만 있어야 한다.

라고 제약이 있다는 이야기가 나왔다. 그런데 그 다음으로 andornegate을 포함하고 있다는 이야기가 나온다.

이게 어떻게 가능한거지??

이는 JDK8 에 제공되는 Predicate 인터페이스를 살펴보면 어느정도 이해가 된다.

/**
 * Represents a predicate (boolean-valued function) of one argument.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #test(Object)}.
 *
 * @param <T> the type of the input to the predicate
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Predicate<T> {
 
    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);
 
    /**
     * Returns a composed predicate that represents a short-circuiting logical
     * AND of this predicate and another.  When evaluating the composed
     * predicate, if this predicate is {@code false}, then the {@code other}
     * predicate is not evaluated.
     *
     * <p>Any exceptions thrown during evaluation of either predicate are relayed
     * to the caller; if evaluation of this predicate throws an exception, the
     * {@code other} predicate will not be evaluated.
     *
     * @param other a predicate that will be logically-ANDed with this
     *              predicate
     * @return a composed predicate that represents the short-circuiting logical
     * AND of this predicate and the {@code other} predicate
     * @throws NullPointerException if other is null
     */
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }
 
    /**
     * Returns a predicate that represents the logical negation of this
     * predicate.
     *
     * @return a predicate that represents the logical negation of this
     * predicate
     */
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }
 
    /**
     * Returns a composed predicate that represents a short-circuiting logical
     * OR of this predicate and another.  When evaluating the composed
     * predicate, if this predicate is {@code true}, then the {@code other}
     * predicate is not evaluated.
     *
     * <p>Any exceptions thrown during evaluation of either predicate are relayed
     * to the caller; if evaluation of this predicate throws an exception, the
     * {@code other} predicate will not be evaluated.
     *
     * @param other a predicate that will be logically-ORed with this
     *              predicate
     * @return a composed predicate that represents the short-circuiting logical
     * OR of this predicate and the {@code other} predicate
     * @throws NullPointerException if other is null
     */
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }
 
    /**
     * Returns a predicate that tests if two arguments are equal according
     * to {@link Objects#equals(Object, Object)}.
     *
     * @param <T> the type of arguments to the predicate
     * @param targetRef the object reference with which to compare for equality,
     *               which may be {@code null}
     * @return a predicate that tests if two arguments are equal according
     * to {@link Objects#equals(Object, Object)}
     */
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

소스코드 상에서 andor과 negate 메서드에는 default 가 선언되어 있는 것을 확인할 수 있다. default 가 선언된 인터페이스의 메서드를디폴트메서드default method라고 부른다.

  • 디폴트 메서드란 무엇인가? 자바8에서 호환성을 유지하면서 API를 바꿀 수 있도록 추가한 새로운 기능이다. 이제 인터페이스는 자신을 구현하는 클래스에서 메서드를 구현하지 않을 수 있는 새로운 메서드 시그니처를 제공한다. 그럼 디폴트 메서드는 누가 구현할까? 인터페이스를 구현하는 클래스에서 구현하지 않은 메서드는 인터페이스 자체에서 기본으로 제공한다(그래서 디폴트 메서드라고 부른다).

인터페이스에 선언된 메서드는 클래스에서 구현하려할 때에는 반드시오버라이드Override 되어야 하는 대상이다. 그러나 default 선언된 메서드는 구현하지 않아도 된다.

public interface IncludeDefaultMethodInterface {
 
    int add(int aint b);
 
    default int max(int aint b) {
        return a > b ? a : b;
    }
}
 
//default int max 메서드는 오버라이드 하지 않았다. 
public class IncludeDefaultMethodInterfaceImpl implements IncludeDefaultMethodInterface {
 
    @Override
    public int add(int aint b) {
        return a + b;
    }
 
}
 
public class IncludeDefaultMethodInterfaceImplTest {
 
    @Test
    public void testAdd() throws Exception {
        // given 
        IncludeDefaultMethodInterfaceImpl interfaceImpl = new IncludeDefaultMethodInterfaceImpl();
        int a = 10;
        int b = 10;
 
        // when 
        int result = interfaceImpl.add(a, b);
 
        // then 
        assertThat(result, is(20));
    }
 
    @Test
    public void testMax() throws Exception {
        // given 
        IncludeDefaultMethodInterfaceImpl interfaceImpl = new IncludeDefaultMethodInterfaceImpl();
        int a = 10;
        int b = 15;
 
        // when: 구현하지 않았지만 로직이 동작한다. 
        int max = interfaceImpl.max(a, b);
 
        // then 
        assertThat(max, is(15));
    }
}

이와 같은 식으로 Predicate 인터페이스에는 andor과 negate 메서드가 디폴트 메서드로 구현되어 있기 때문에 개발자가 별도로 그 메서드들을 구현하지 않아도 함수인터페이스를 사용하면서 사용할 수 있는 것이다.

라고 정리하면 이해할 수 있을까??

2. 정리

그럼 함수형 인터페이스에 선언된 디폴트 메서드정적 메서드는 함수형 인터페이스의 메서드로 취급하지 않는것인가??

라고 물어보면 어떻게 대답할 수 있을까? 일단은 그렇다라고 봐야겠군. 람다표현식으로 전달받은 동작 파라미터를 구현할 때는 함수형 인터페이스에 선언된 메서드(디폴트 메서드나 정적메스드가 아닌 유일한 메서드)를 이용한다고 보면 되겠다.


+ Recent posts