NMP-Study / EffectiveJava2022

Effective Java Study 2022
5 stars 0 forks source link

아이템 41. 정의하려는 것이 타입이라면 마커 인터페이스를 사용하라 #41

Closed okhee closed 1 year ago

luckyDaveKim commented 1 year ago

마커 인터페이스 vs 마커 애너테이션

마커 인터페이스 장점

  1. 클래스의 인스턴스들을 구분하는 타입으로 쓸 수 있다. 마커 인터페이스는 컴파일 타임에 오류를 잡을 수 있으나, 마커 애너테이션은 런타임에야 오류를 알 수 있다. ObjectOutput.writeObject 와 같이 Serializable 마커 인터페이스를 사용했으나 Object로 값을 받으며 마커 인터페이스의 장점을 살리지 못한 나쁜 예시다.
    
    public interface ObjectOutput {
    public void writeObject(Object obj) throws IOException;
    }

public class ObjectOutputStream implements ObjectOutput { private void writeObject0(Object obj, boolean unshared) throws IOException { // ... 중략

    } else if (obj instanceof Serializable) {
        writeOrdinaryObject(obj, desc, unshared);
    } else {
        throw new NotSerializableException(cl.getName());
    }

// ... 중략
}

}

2. 적용 대상을 더 정밀하게 지정할 수 있다.
  마커 애너테이션은 적용 대상(`@Target`)을 `ElementType.TYPE`으로 선언하면 모든 타입(클래스, 인터페이스, 열거 타입, 애너테이션)에 달 수 있다. 하지만 특정 인터페이스를 구현한 클래스에만 마커를 적용하고 싶다면? 이는 마커 인터페이스만 가능하다.
  `Set`은 `Collection`의 마커 인터페이스로 볼 수 있다.
  책에서는 `Set`이 `add`, `equals`, `hashCode`를 재정의 하였다고 했으나, 작성일 기준 확인한 바로는 `spliterator`만 재정의 하였고, `Set`에는 `Collection` 인터페이스 이외의 메서드는 없으며, 편의를 위한 일부 스태틱 메서드(`of`, `copyOf`)만 보유하고 있다.
```java
public interface Set<E> extends Collection<E> {
    boolean add(E e);

    boolean equals(Object o);

    int hashCode();

    @Override
    default Spliterator<E> spliterator() {
        return Spliterators.spliterator(this, Spliterator.DISTINCT);
    }
}
image

마커 애너테이션의 장점

  1. 거대한 애너테이션 시스템의 지원을 받는다. 애너테이션을 적극 활용하는 프레임워크에서는 마커 애너테이션을 쓰는게 일관성을 지키는데 유리하다.

결론

어떻게 마커 인터페이스와 마커 애너테이션 사용성을 구분할까? 클래스와 인터페이스 외의 요소 (모듈, 패키지, 필드, 지역 변수 등)에 마킹이 필요하면 애너테이션을 쓸 수 밖에 없다. 그 외에 마킹된 객체를 매개변수로 받는 경우가 필요하다면 마커 인터페이스를 사용해야 한다. (컴파일 타임에 오류를 잡기 위해)