peaches-book-study / effective-java

이펙티브 자바 3/E
0 stars 2 forks source link

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

Open jseok0917 opened 3 months ago

jseok0917 commented 3 months ago

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");
    }

}


🍑 본론

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

//어떤 메서드도 선언돼있지 않다... 이걸 도대체 어따 쓰지?
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 이렇게 바꾸면
//런타임 오류 안뜸


본론 요약



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

  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. 클래스와 인터페이스 외의 프로그램 요소에 마킹을 해야할 때
    • 클래스와 인터페이스 외의 프로그램 요소(모듈, 패키지, 필드, 지역변수 등)에 마킹을 해야할 때는 애너테이션을 쓸 수 밖에 없음

🍑 결론