어노테이션 - 규칙 37 자료형을 정의할 때 표식 인터페이스를 사용하라.
JAVA/Effective Java

어노테이션 - 규칙 37 자료형을 정의할 때 표식 인터페이스를 사용하라.

반응형

표식 인터페이스(marker interface)는 아무런 메스드도 없는 인터페이스이다.

클래스를 선언할 때 이런 표식인터페이스를 선엉ㄴ하는 이유는 해당 클래스가 어떤 속성을 만족한다는 사실을 표시하는 것과 같다.

표식 인터페이스의 장점은 다음과 같다.


1. 표식 인터페이스는 자료형이기 때문에 컴파일 시 오류를 탐지할 수 있다.
예를 들어서 살펴보자.

Wedul이라는 표식 인터페이스를 만들고 Cjung클래스는 이를 구현하고, Gglee 클래스는 이를 구현하지 않았다. 이때 Wedul 인터페이의 인자를 받는 메서드 makeBar를 선언해놓고, 이 메소드에 파라미터로 Cjung과 Gglee를 넣었다. 이때 Wedul 인터페이스를 구현하지 않은 gglee 클래스 객체는 넣을 때 컴파일러에서 오류를 뱉는다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package effective37;
 
/**
 * wedul member라는 표식 인터페이스
 * 
 * @author wedul
 *
 */
public interface Wedul {
 
}
 
package effective37;
 
public class Cjung implements Wedul {
    private int age;
 
    public Cjung(int age) {
        this.age = age;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
 
}
 
package effective37;
 
public class Gglee {
    private int age;
 
    public Gglee(int age) {
        this.age = age;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
}
 
 
package effective37;
 
public class Main {
    
    public static void makeBar(Wedul wedul) {
        System.out.println("dd");
    }
    
    public static void main(String args[]) {
        makeBar(new Cjung(12));
        makeBar(new Gglee(12)); // 오류 발생
    }
 
}
cs




위와 같은 예제가 바로 ObjectOutputStream으로 출력하는 클래스들을 정의할 때 사용하는 Serializable 인터페이스이다.

ObjectOutputStream의 write에 넘기는 Object가 Serializable 인터페이스를 구현하지 않은 클래스인 경우에는 런타임시 오류가 발생한다.


2. 마커 어노테이션과 마커 인터페이스의 비교






위와 같이 선언된 마커 어노테이션을 가지고 해당 어노테이션이 선언된 클래스만을 골라서 동작을 제한할 수 있다.


다음 예를 살펴보자.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.lang.annotation.*;
import java.lang.reflect.*;
 
/* marker annotation */
@Retention(RetentionPolicy.RUNTIME)
@interface MyMarker { }
 
class Marker {
    /* annotate the method using marker.
    *  Notice that no { } is needed 
    */
    @MyMarker
    public static void myMethod() {
        Marker obj = new Marker();
        
        try {
        
            Method m = obj.getClass().getMethod("myMethod");
            
            /* specify if the annotation is present */
            if(m.isAnnotationPresent(MyMarker.class))
               System.out.println("MyMarker is present");
               
        } catch(NoSuchMethodException exc) {
        
            System.out.println("Method not found..!!");
        }
    }
    
    public static void main(String args[])
    {
        myMethod();    
    }
}
cs




하지만 특정한 인터페이스를 구현한 클래스만을 뽑아내는 마커가 필요하다가 해보자. 마커 인터페이스를 사용하면 그 특정 인터페이스를 상속 하도록 선언만 하면 된다. 그럼 마커를 상속한 모든 장료형은 자동으로 그 특정 인터페이스의 하휘 자료형이 된다. 


그렇다면 마커 어노테이션의 장점은 뭘까? 마커 아노테이션은 유연하게 확장이 가능하다. 


 어노테이션을 만들어 사용한 뒤에도 계속적으로 더 많은 정보를 추가 할 수 있는 것이 큰 장점이다. 예를들어 어떤 어노테이션을 만들고 배포를 한 뒤에 뭔가 더 정보를 추가 하고 싶다면 새로 추가 된 요소들에 대해 default 값을 갖게 하면 하위 호환성도 지킬 수 도 있다.


하지만 인터페이스 경우에는 메서드를 만드는 순간 하위 호환성이 깨지므로 마커 어노테이션처럼 지속적인 진화는 불가능하다. 


정리하자면

마커 어노테이션은 새로운 매서드등 더 많은 추가 정보가 있을 때 사용하고,

마커 인터페이스는 새로운 매서드가 없는 자료형으로써 정의하고 싶을 때 사용할 수 있다.


출처 : 조슈아 블로크, 『 Effective Java 2/E』, 이병준 옮김, 인사이트(2014.9.1), 규칙37 인용.

반응형