Skip to content

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

@jseok0917

Description

@jseok0917

Chapter : 6. 열거 타입과 애너테이션

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

Assignee : jseok0917


🍑 서론

  • 클래스나 요소에 특별한 의미를 부여하는 방법
    • 마커 애너테이션
    • 마커 인터페이스

  • 마커 애너테이션 : 클래스, 메소드, 필드 등에 마킹하여 추가적인 정보를 제공하는 애터네이션.

    주석처럼 사용되지만, 컴파일러나 런타임 환경에서 해당 요소들에 대한 특정한 동작을 유발할 수 있다.

    • 컴파일러에게 문법 에러를 체크하도록 정보를 제공한다.
    • 프로그램을 빌드할 때 코드를 자동으로 생성할 수 있도록 정보를 제공한다.
    • 런타임에 특정 기능을 실행하도록 정보를 제공한다.
  • 표준 애너테이션

//@Override 애너테이션
class Parent {
    void display() {
        System.out.println("Parent's display()");
    }
}

class Child extends Parent {
    @Override
    void display() { // 컴파일러가 Parent의 display()가 존재하는지, 문법적 오류는 없는지 체크
        System.out.println("Child's display()");
    }
}
//프로그램 동작에는 직접적인 영향을 미치지 않지만,
//컴파일러나 프레임워크가 애너테이션을 이용하여 미리 오류를 잡아낼 수 있음

  • 메타 애너테이션
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

// @Target 애너테이션을 사용하여 이 애너테이션이 클래스에만 적용될 수 있도록 함
@Target(ElementType.TYPE)
@interface ExampleClassAnnotation {
    // 애너테이션 내용
    String value();
}

// 애너테이션을 사용할 클래스
@ExampleClassAnnotation("This annotation is only applicable to classes")
class ExampleClass {
    
	//메서드에는 사용 불가능(컴파일 오류 발생)
	@ExampleClassAnnotation
	void exampleMethod() {
		System.out.println("I'm Groot");
	}
	
}

🍑 본론

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

  • 마커인터페이스 : 아무런 메서드도 선언하지 않은 인터페이스
    • Serializable, Cloneable, EventListen
//어떤 메서드도 선언돼있지 않다... 이걸 도대체 어따 쓰지?
public interface MarkerInterface{

}

Serializable

  • 대표적인 마커 인터페이스로 어떤 메서드도 선언돼있지 않다.
  • 직렬화 기능을 지원하기 위한 목적

//Serializable을 구현하지 않은 Item 클래스
class Item {
	private long id;
	private String name;
	private BigDecimal price;
	
	public Item(long id, String name, BigDecimal price) {
		this.id = id;
		this.name = name;
		this.price = price;
	}
	
}

public class test02 {
	
    //직렬화하는 메서드를 만든다.
    void serializableTest() throws IOException {
        File f= new File("test.txt");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(f));
        objectOutputStream.writeObject(new Item(1L, "item A", new BigDecimal(30000)));
    }
    
    public static void main(String[] args) throws IOException {
		test02 test= new test02();

        //런타임 에러 발생(Item 클래스가 Serializable을 implement 하지 않으니까)
		test.serializableTest();
        //다만 컴파일 시점에서 에러를 잡아내지 못하기에
        //마커 인터페이스를 제대로 구현해놓은 것은 아님
        
        //컴파일 시점에서 에러를 잡지 못하는 이유는
        //writeObejct 메서드가 Object를 매개변수로 받기 때문에
		
	}
   
}

//그런데 여기서 Serializable 하지 않다는걸 
//프로그래밍적으로 꼼꼼히 체크하는 것이 아니라
//그냥 Serializable에 상속되고있냐 아니냐 수준의
//타입 체크정도로만 사용한다

//위에서 Item implements Serializable 이렇게 바꾸면
//런타임 오류 안뜸

  • 그럼 Serializable은 왜 어떤 메서드도 선언해놓지 않는가?
    • 호환성 : Java 버전에 따라 구체적인 직렬화 방식이 달라질 수도 있기때문에
      • 직렬화 가능 클래스가 명시적으로 선언해놓은 serialVersionUID를 통해 직렬화가 이루어짐
      • serialVersionUID가 선언돼있지 않다면 해당 클래스에 대한 기본 UID값을 계산
      • JAVA(TM) Object Serialization Specification 참고

본론 요약

  • 마커 인터페이스와 마커 애너테이션의 차이

    • 범위 :

      • 마커 애너테이션이 훨씬 광범위하게 사용될 수 있음.
      • 마커 인터페이스는 클래스와 인스턴스 범위에서만 사용가능
    • 세밀함 :

      • 마커 인터페이스는 특정 클래스에 대한 타입체크 용도로 사용할 수 있음
      • @target 애너테이션은 구체적 클래스를 지정하지 못함
  • 마커 인터페이스의 사용 용도 요약(책내용)

    1. 타입체크 용도 : 마커 인터페이스는 이를 구현한 클래스의 인스턴스들을 구분하는 타입으로 쓸 수 있으나,

      마커 애너테이션은 그렇지 않다.

    2. 마커 애너테이션에 비해 적용 대상을 더 정밀하게 지정할 수 있다.
    1. 하지만, 마커 애너테이션은 거대한 애너테이션 시스템의 지원을 받는다.
      • 애너테이션을 자주 활용하는 프레임워크라면
      • 왠만하면 애너테이션을 써서 일관성을 지켜라


그러면 어떤 때에 마커 애너테이션을 쓰고, 마커 인터페이스를 써야하는가?

  1. 클래스와 인터페이스에 마킹을 해야할 때
    • 마커 인터페이스를 쓰는 경우

      • 마킹이 된 객체를 매개변수로 받는 메서드를 작성할 일이 있는 경우
      • 그러면 컴파일 타입에 오류를 잡아낼 수 있다.
    • 위 경우 말고는 마커 애너테이션을 쓰는 것이 낫다.

마커 인터페이스 사용 예시

//마커 인터페이스
interface MarkerInterface {
 // 아무 메소드도 없음
}

//마커 인터페이스를 구현한 클래스
class MarkerClass implements MarkerInterface {
}

//마커 인터페이스를 구현하지 않은 클래스
class AnotherClass {
}

//특정 클래스
class MyClass {
	// 특정 클래스의 생성자
	public MyClass(MarkerInterface marker) {
	     // 생성자의 매개변수로 마커 인터페이스를 구현한 클래스만 허용
	}
}

public class test03 {
	 public static void main(String[] args) {
	     // 올바른 인자 전달
	     MarkerClass markerObj = new MarkerClass();
	     MyClass myObj = new MyClass(markerObj);
	
	     // 잘못된 인자 전달
	     AnotherClass anotherObj = new AnotherClass();
	     // 컴파일 오류 발생
	      MyClass myObj2 = new MyClass(anotherObj);
	 }
}
  1. 클래스와 인터페이스 외의 프로그램 요소에 마킹을 해야할 때
    • 클래스와 인터페이스 외의 프로그램 요소(모듈, 패키지, 필드, 지역변수 등)에 마킹을 해야할 때는 애너테이션을 쓸 수 밖에 없음

🍑 결론

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions