Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Item 40 & 41. @Override 와 마커 인터페이스 #21

Open
daminzzi opened this issue Dec 19, 2023 · 0 comments
Open

Item 40 & 41. @Override 와 마커 인터페이스 #21

daminzzi opened this issue Dec 19, 2023 · 0 comments
Assignees
Labels

Comments

@daminzzi
Copy link
Member

daminzzi commented Dec 19, 2023

🍵 서론

이번 아이템에서는 @Override 어노테이션과 마커인터페이스에 대해서 살펴보려고 한다. 앞서 아이템 39에서 어노테이션에 대해서 살펴볼 수 있었는데, 짧게 어노테이션의 종류에 대해서 정리하고 아이템 40과 41의 내용에 대해서 살펴본 뒤 마무리하려고 한다.

🌒 본론

어노테이션의 종류

어노테이션은 크게 세가지로 분류할 수 있다. 표준어노테이션, 메타어노테이션, 사용자 정의 어노테이션. 지난 번에 자바 성능 튜닝 이야기를 읽으면서 Spring에서의 어노테이션에 대해서도 살펴봤었는데, Spring 보다는 아니지만 자바에서도 다양한 어노테이션을 제공하고 있다.

표준 어노테이션은 자바에서 기본적으로 제공하는 어노테이션으로 잠시 뒤 살펴볼 @Override를 포함해서 @Deprecated, @SuppressWarnings, @SafeVarargs 등 지금까지 언급했던 어노테이션이 대부분 포함된다.

메타 어노테이션은 어노테이션을 위한 어노테이션으로 어노테이션을 정의할 때 적용대상이나 유지기간 등을 지정하는데 사용한다. 예시로는 @Target, @Retention 등이 있다.

더 자세히 어노테이션에 대해서 살펴보기 위해서는 참고자료를 더 살펴보자.

@Override 어노테이션

앞서 이야기한 표준 어노테이션 중에서 @Override 어노테이션에 대해서 좀 더 자세히 살펴보자.

@Override 어노테이션은 메서드 선언에만 달 수 있고, 이 어노테이션이 달렸다는 것은 상위 타입의 메서드를 재정의했음을 의미한다.

@Override 를 통해서 얻을 수 있는 효과 중 가장 큰 것은 잘못된 재정의를 알아채는 것 이라고 생각이 든다. 우리가 만약 새로운 class를 만들면서 equals 함수를 재정의한다고 생각해보자.

public class Bigram {
    private final char first;
    private final char second;
    
    public Bigram(char first, char second){
        this.first = first;
        this.second = second;
    }
    
    public boolean equals(Bigram b){
        return b.first == first && b.second == second;
    }
    
    public int hashCode(){
        return 31 * first + second;
    }

    public static void main(String[] args) {
        Set<Bigram> s = new HashSet<>();
        for(int i = 0; i<10; i++)
            for(char ch = 'a'; ch <= 'z'; ch++)
                s.add(new Bigram(ch, ch));
        System.out.println(s.size());
    }
}

성공적으로 equals와 hashCode를 재정의했다고 생각할 수 있지만 main의 for문에서는 우리의 예측과 다른 결과가 나타난다. 분명 26개의 요소만 set에 들어갈 것이라고 생각했는데, 총 260개의 원소가 삽입되어 260이 출력된다.

이는 위의 코드에서 equals를 잘못 재정의했기 때문이다. override가 아니라 overloading 해버렸다…

overloadingriding
아직도 이거 헷갈리는 흑우 없제~~(그 사람이 바로 나예요)

이 문제를 제대로 캐치하지 못한 이유는 @Override 를 쓰지 않았기 때문이다! equals 함수 위에 어노테이션만 붙였어도 컴파일 오류로 바로 잘못된 부분을 알 수 있었을텐데,, 참으로 비통한 일이 아닐 수 없다,,

그러니까 우리는 상위 클래스의 메서드를 재정의하려는 모든 메서드에 @Override 어노테이션을 달도록 하자! 단 예외적으로 구체 클래스에서 상위 클래스의 추상 메서드를 정의할 떄는 굳이 @Override 를 달지 않아도 된다.(컴파일러가 안한 거 알려주니까)

마커 인터페이스

아무 메서드도 담고 있지 않고, 단지 자신을 구현하는 클래스가 특정 속성을 가짐을 표시해주는 인터페이스를 마커 인터페이스(marker interface)라 한다. 대부분의 경우 마커 인터페이스를 통해서 단순 타입 체크를 수행하곤 한다.

대표적인 예시로 @Serializable이 있는데, 이 어노테이션은 자신을 구현한 클래스의 인스턴스는 ObjectOutputStream을 통해 쓸(write) 수 있다고, 즉 직렬화할 수 있다고 알려준다.

비교 대상으로 마커 어노테이션에 대해서도 이야기해보면, 마커 어노테이션은 멤버를 포함하지 않으며 데이터로 구성되지 않는다. 단지 어노테이션 선언을 표시하여 대상을 정의한 이유를 표시하는데에 쓰인다.

마커 어노테이션 vs 마커 인터페이스

마커 어노테이션과 마커 인터페이스를 좀 더 자세히 비교해보자.

  1. 마커 인터페이스를 구현한 클래스의 인스턴스를 구분하는 타입으로 쓸 수 있다.

    마커 어노테이션은 구분하는 타입으로 사용할 수 없으며, 마커 어노테이션의 경우 런타임시 발견할 오류를 마커 인터페이스를 구현하면 컴파일 타임에 발견할 수 있다. 위에서 살펴본 ObjectOutputStream.writeObject는 런타임시 문제를 확인하므로 이러한 마커 인터페이스의 장점을 살리지 못한 케이스이다.

  2. 마커 인터페이스는 적용 대상을 더 정밀하게 지정할 수 있다.

    마커 어노테이션은 ElevmentType.Type 으로 타겟을 지정하므로 모든 타입(클래스, 인터페이스, 열거 타입, 어노테이션)에 적용된다. 마킹하고 싶은 특정 클래스에서만 마커 인터페이스를 구현하여 적용대상을 더 정밀하게 지정할 수 있다.

  3. 마커 어노테이션은 거대한 어노테이션 시스템의 지원을 받을 수 있다.

    어노테이션을 적극적으로 사용하는 프레임워크에서는 마커 어노테이션을 쓰는 것이 일관성을 지키는데 유리하다.

이러한 정보를 바탕으로 둘의 사용 방법에 대해서도 정리를 해보자면 다음과 같이 정리를 할 수 있다.

  • 마커 어노테이션 사용
    • 클래스, 인터페이스 외 프로그램 요소(모듈, 패키지, 필드, 지역변수 등)에 마킹해야하는 경우 (클래스와 인터페이스만이 인터페이스를 구현하거나 확장이 가능)
    • 어노테이션을 적극적으로 사용하는 프레임워크
  • 마커 인터페이스 사용
    • 마킹된 객체를 매개변수로 받는 메서드를 작성해야할 때 (마커 인터페이스를 해당 메서드의 매개변수 타입으로 사용하면 컴파일타임에 오류 발생)
    • 새로 추가하는 메서드 없이 단지 타입 정의가 목적인 경우

🍃 결론

항상 결론이 비슷한 거 같긴한데,, 결국 우리는 항상 코드의 간결함, 명확함을 위해 작성, 이용하고자 하는 코드의 특성에 대해서 잘 파악하고 그 특성에 맞게 코드를 작성하기 위해서 더욱 더 노력을 기울여야 한다고 생각된다. 그럼 끝!


reference

https://velog.io/@gkskaks1004/어노테이션

https://dahye-jeong.gitbook.io/til/java/effective_java/2021-06-14-use-marker-interfaces-to-define-types

@daminzzi daminzzi self-assigned this Dec 19, 2023
@daminzzi daminzzi changed the title Item40&41. @Override 와 마커 인터페이스 Item 40 & 41. @Override 와 마커 인터페이스 Dec 19, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant