[EFFECTIVE JAVA] ITEM 41, 정의하려는 것이 타입이라면 마커 인터페이스를 사용하라

Posted by iheese on October 13, 2023 · 3 mins read

ITEM 41, 정의하려는 것이 타입이라면 마커 인터페이스를 사용하라


마커 인터페이스(Marker Interface)

  • 아무 메서드도 담고 있지 않고, 단지 자신을 구현하는 클래스가 특정 속성을 가짐을 표시하는 인터페이스
  • EX) Serializable : 자신을 구현한 클래스의 인스턴스는 ObjectOutputStream을 쓸 수 있다고, 즉 직렬화할 수 있다고 알려준다.


마커 인터페이스가 마커 애너테이션보다 나은 점

마커 인터페이스는 이를 구현한 클래스의 인스턴스를 구분하는 타입으로 쓸 수 있으나, 마커 애너테이션은 그렇지 않다.

  • 마커 애너테이션을 사용했다면 런타임에야 발견될 오류를 마커 인터페이스는 타입이므로 컴파일에 잡을 수 있다.
  • BAD EX) OBjectOutputStrema.writeObject 메소드는 인수로 받은 객체가 Serialzable을 구현하였을 것이라고 가정하지만 Serialzable이 아닌 Object 타입을 받게 설계되었다. 직렬화 불가능한 객체를 넘겨도 런타임에야 문제를 확인할 수 있다. 마커 인터페이스를 사용하는 이유인 컴파일 타임 오류 검출의 이점을 살리지 못했다.
    • 자바의 직렬화는 Serialzable 마커 인터페이스를 보고 직렬화할 수 있는 대상인지 확인한다.

적용 대상을 더 정밀하게 지정할 수 있다.

  • 적용 대상(@Target)을 ElementType.TYPE으로 선언한 애너테이션은 모든 타입(클래스, 인터페이스, 열거 타입, 애너테이션)에 달 수 있다. 이는 부착할 수 있는 타입을 세밀하게 제한하지 못한다는 뜻이다.
  • 특정 인터페이스를 구현한 클래스에만 적용하고 싶은 마커가 있다고 하면, 마커를 인터페이스로 정의했다면 마킹하고 싶은 클래스에서만 인터페이스(인터페이스라면 확장)를 구현하면 된다. 이는 자동으로 인터페이스의 하위 타입을 보장하는 것이다.


Set 인터페이스

  • Set은 Collection의 하위 타입에만 적용할 수 있다.
  • Collection이 정의한 메소드 외에는 새로 추가한 것이 없다.
    • Set을 마커 인터페이스로 생각하지 않는 이유는 add, equals, hashCode 등 메소드 몇 개의 규약을 살짝 수정했기 때문이다.
  • 특정 인터페이스의 하위 타입에만 적용할 수 있으며, 아무 규약에도 손대지 않은 마커 인터페이스는 충분히 있음직하다.
    • 객체의 특정 부분을 불변식으로 규정
    • 그 타입의 인스턴스는 다른 클래스의 특정 메소드가 처리할 수 있는다는 사실을 명시하는 용도
      • EX) Serialzable 인터페이스가 ObjectOutputStream이 처리할 수 있는 인스턴스임을 명시하듯이


마커 애너테이션이 마커 인터페이스보다 나은 점

거대한 애너테이션 시스템의 지원을 받는다.

  • 애너테이션을 적극 활용하는 프레임워크에 마커 애너테이션을 쓰는 쪽이 일관성에 좋을 수 있다.


사용해야할 때를 구분해보자

마커 애너테이션

  • 클래스와 인터페이스 외의 프로그램 요소들(모듈, 패키지, 필드, 지역변수 등)에 마킹해야 할 때 애너테이션을 쓸 수 밖에 없다.
  • 마커를 인터페이스나 클래스에 적용할 때, 이 마킹된 객체를 매개변수로 받는 메소드를 작성할 일이 절대 없다면 마커 애너테이션를 사용하는 것이 좋다.
  • 애너테이션을 활발히 활용하는 프레임워크라면 마커 애너테이션이 나을 것이다.

마커 인터페이스

  • 마커를 인터페이스나 클래스에 적용할 때, 이 마킹된 객체를 매개변수로 받는 메소드를 작성할 일이 있다면 마커 인터페이스를 사용해야 한다.
    • 컴파일 타임에 오류 잡아내기 위해서


Reference: