아무 메서드도 담고 있지 않고 단지 자신을 구현하는 클래스가 특정 속성을 가짐을 표시해주는 인터페이스
public interface Serializable {
}
대표적인 예로 Serializable
인터페이스를 예로 들 수 있다. Serializable
은 자신을 구현한 클래스의 인스턴스는 직렬화할 수 있음(ObjectOutputStream
을 통해 쓸 수 있음)을 알려주는 역할을 한다.
= Serializable
을 구현하지 않은 클래스는 직렬화를 할 수 없다.
마커 애너테이션이 등장하면서 마커 인터페이스가 더 이상 쓸모없게 느껴질 수 있겠지만, 사실 마커 인터페이스는 두 가지 측면에서 마커 애너테이션보다 더 나은 점이 있다.
마커 인터페이스는 이를 구현한 클래스의 인스턴스들을 구분하는 타입으로 쓸 수 있으나, 마커 애너테이션은 그렇지 않다. 따라서 애너테이션을 사용하였다면 런타임에나 발견될 오류를 컴파일 타임에 잡을 수 있다.
하지만, 자바의 Serializable
은 컴파일 타임 오류 검출의 장점을 살리지 못하였다.
private void writeObject0(Object obj, boolean unshared)
throws IOException
{
boolean oldMode = bout.setBlockDataMode(false);
depth++;
try {
// (중략)
// remaining cases
// 실질적으로 뭘 하지 않고 간단히 Serializable가 선언되었는지 안되어 있는지 체크
if (obj instanceof String) {
writeString((String) obj, unshared);
} else if (cl.isArray()) {
writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
writeEnum((Enum<?>) obj, desc, unshared);
} else if (obj instanceof Serializable) {
writeOrdinaryObject(obj, desc, unshared);
} else {
if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "\\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
} finally {
depth--;
bout.setBlockDataMode(oldMode);
}
}
writeObject
메서드는 당연히 인수로 받을 객체가 Serializable
을 구현하였을 거라고 가정하지만, Object
객체를 받기 때문에, 직렬화할 수 없는 객체를 넘겨도 런타임에야 문제를 확인할 수 있다.
마커 인터페이스는 마커 애너테이션보다 적용 대상을 더 정밀하게 지정할 수 있다.
@Retention(RetentionPolicy.RUNTIME) // 보존 정책
@Target(ElementType.METHOD) // 적용 대상
public @interface Test {
}
마크 애너테이션은 적용 대상( @Target
)을 Element.Type
으로 선언한 애너테이션은 모든 타입(클래스, 인터페이스, 열거 타입, 애너테이션)에 달 수 있지만, 부착할 수 있는 타입을 더 세밀하게 제한할 수는 없다.
반면, 마크 인터페이스는 그냥 마킹하고 싶은 클래스에서만 그 인터페이스를 구현하면 마킹된 타입은 자동으로 그 인터페이스의 하위 타입임이 보장된다.
반면, 마커 애너테이션이 더 나은 점은 거대한 애너테이션 시스템의 지원을 받는다는 것이다. 따라서 애너테이션을 적극 활용하는 프레임워크에서는 마커 애너테이션을 쓰는 것이 일관성을 지키는 데 유리할 것이다.
클래스와 인터페이스만이 인터페이스를 구현하거나 확장할 수 있기 때문에, 이 두 가지 외의 프로그램 요소(모듈, 패키지, 지역변수 등)에 마킹해야 할 때는 마커 애너테이션을 사용해야 한다.
클래스와 인터페이스에 마킹해야 한다면, 아래의 질문을 자문해보자.
마킹이 된 객체를 매개변수로 받는 메서드를 작성할 일이 있을까?
새로 추가하는 메서드 없이 단지 타입 정의가 목적이라면 마커 인터페이스를 정의하자.
클래스나 인터페이스 외의 프로그램 요소에 마킹해야 하거나, 애너테이션을 적극 활용하는 프레임워크의 일부로 마커를 편입시키고자 한다면 마커 애너테이션을 사용하자.