자바 9 모듈화


아침에 조금씩 살펴보는 중이다.


현재는 자바 8을 기반으로 개발하고 있는데, 얼마 지나지 않아 자바 9 이상(아마도 11?)으로 개발해야할테니 그에 맞춰 슬슬 준비하려는 생각으로 하고 있는데,

시각적으로 보이는 큰 차이라면 기존에는 jdk에 있는 rt.jar 안에서 필요한 클래스를 탐색하여 사용했다면, 이제는 분리된 모듈을 확인하고 그에 따라 사용할 모듈을 선언하는 방식으로 변경되었다 할까? @_@);;;

Java 8 까지 

 Java 9 이후

 



 


사용하는 방식도 제법 많이 달라질 것으로 예상된다. @_@);;
뭐 일단 하나씩...

윈도우즈 환경에서 자바 개발환경 구축하기

어느 책을 보다가 .properties 파일에 구분자로 = 대신 : 를 기재한 것을 보고

잘못 사용했음이 분명하다!!

라고 생각하고 열심히 찾아봤다. 그런데 java.util.Properties#load메서드를 찾아보니 =  : 가 사용가능하다고 나와있다.

The key contains all of the characters in the line starting with the first non-white space character and up to, but not including, the first unescaped '=', ':', or white space character other than a line terminator. All of these key termination characters may be included in the key by escaping them with a preceding backslash character; for example,

\:\=

키에 대한 값으로 :, = 또는 공백문자가 아닌 첫번째 문자부터 시작하여 줄의 끝까지 값으로 포함된다.

그러나, 관례라고 하긴 그렇지만 넓게 사용되는 것은

.properties
server.port=9090
app.info.name=honeymon
app.info.ver=v1.0
.yml
server.port: 9090
app.info:
name: honeymon
ver: v1.0

가 아닐까 생각한다. 그냥 문득…​


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