크리에이티브 커먼즈 라이선스
Creative Commons License

어느 책을 보다가 .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

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


저작자 표시
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

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. 정리

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

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


저작자 표시
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

분기마다 정독해보아요.


  1. 소개 및 OOP

  2. OOP part2

  3. Function

  4. Function part2

  5. Function Structure

  6. Form

7-1. TDD 1

7-2. TDD 2 - tyrant

  1. TDD 3 - bowling game

  2. TDD 4 - primefactors and wordwrap

  3. Architecture

  4. Architecture UseCase

  5. SOLID Fundation

  6. SRP(Single Responsibility Principle)

14-1. OCP(Open-Closed Principle)

14-2. LSP(Liskov Subtitution Priciple)

14-3. ISP(Interface Segregation)

15-1. DIP(Dependency Inversion Principle)

15-2. SOLD Case Study

저작자 표시
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

스프링시큐리티의 GrantedAuthority 인터페이스를 enum 타입으로 구현했는데, 이 구현체에 toString() 을 선언하면서 의도와는 다르게 동작하는 문제가 발생했다.

public enum MemberAuthority implements OrderedGrantedAuthorityCodeableEnum {
    /**
     * Administrator
     */
    ADMINISTRATOR("administrator""code.memberAuthority.administrator"1),
    /**
     * Project Manager(project, project member, jobs of project management)
     */
    PROJECT_MANAGER("project-manager""code.memberAuthority.projectManager"2),
    /**
     * Operator( operator jobs of project)
     */
    OPERATOR("operator""code.memberAuthority.operator"3),
    /**
     * Inspector(monitoring)
     */
    INSPECTOR("inspector""code.memberAuthority.inspector"4);
 
    private String code;
    private String key;
    private int order;

이런 코드인데, toString() code, key, order 에 대한 내용을 출력하도록 만들면...MemberAuthority[code="project", key="code.memberAuthority.administrator", order=1]의 형태로 나오게 된다. 스프링시큐리티에서는 권한을 문자열로 받기에 ADMINISTRATOR 으로 나와야하는데... 전혀 다른 형태가 되어버리니 권한 체크가 제대로 되지 않을 수밖에...

○ 정리

  • enum 타입에 대해서 toString()을 적용할 때는 잠시만 고심해보자.


저작자 표시
신고
  1. 2015.05.14 17:25

    비밀댓글입니다

크리에이티브 커먼즈 라이선스
Creative Commons License

실행가능한 JAR(executable jar)을 생성하게 되면 다음과 같이 META-INF 폴더 밑에 MANIFEST.MF 파일이 생성된다.

├── Application.class
└── META-INF
    └── MANIFEST.MF

MANIFEST.MF 내용

  • Manifest specification
    Manifest-Version: 1.0
    Class-Path: .
    Main-Class: Application // 실행가능한jar의 엔트리포인트가 되는 클래스명
    

예제

  • java.util.jar.JarFile API를 이용해서 Jar 파일에 대한 정보를 읽어드릴 수 있다.
  • 다음과 같이 JDK6과 JDK7에서 가져올 수 있는 부분이 다른 것으로 보인다.

    ㅡ_-);; JDK6, JDK7, JDK8에서 다를 수 있는데 그걸 테스트하기가 좀… 귀찮…

JarFile jarFile = new JarFile(/** 식별하려고 하는 jar 파일*/);

private boolean hasMainClassManifest(JarFile jarFile) throws IOException {
    Double javaVersion = Double.parseDouble(System.getProperty("java.specification.version"));
    log.debug("Java version: {}", javaVersion);
    log.debug("Has Main-Class: {}", jarFile.getManifest().getEntries().containsKey("Main-Class"));
    log.debug("Has Main-Class: {}", jarFile.getManifest().getMainAttributes().getValue("Main-Class"));
    if(1.7 > javaVersion) {
        return jarFile.getManifest().getEntries().containsKey("Main-Class");
    } else if(1.7 == javaVersion) {
        return null != jarFile.getManifest().getMainAttributes().getValue("Main-Class");
    } else {
        //TODO JDK 8 에서는 어떻게 될까?
        log.debug("Not implements");
        return false;
    }
}

대략 위의 메서드를 통해서 선택한 jar가 실행가능한 Main-Class를 가지고 있는지 여부를 확인할 수 있다.

참고


저작자 표시
신고

+ Recent posts