6장 - Enum, Annotation
int 대신에 enum 쓰기
- 타입 안전이 보장된다
- 출력, 또는 디버깅할 때 어떤 값인지 쉽게 알아볼 수 있다
- 성능적으로 int를 사용하는 것과 비슷하다
- 물론 메모리는 약간 더 먹긴 한다
- enum은 별개의 네임스페이스를 가지기 때문에 동일한 이름의 상수를 만드는데 지장이 없다
- enum에다가 별도의 메소드나 필드를 정의해서 좀 더 편리하게 사용할 수 있다
- enum에다가 abstract 메소드를 넣고 각각의 인스턴스마다 각자 구현한 다음 그 메소드를 호출하게끔 하여 switch-case문을 대체할 수 있다. 이 경우 enum의 요소가 늘어나더라도 switch-case의 case를 늘린다던가 하는 불편한 점이 없어진다.
- 불필요하게 코드가 중복된다 싶으면 한 depth를 늘려서 중첩 enum을 만든 다음 거기에서 구현하게 하는 방법도 있다
ordinal 대신에 인스턴스 필드 쓰기
ordinal을 사용하는 로직은 enum 인스턴스의 순서가 바뀌거나 새로운 요소를 추가할 때 문제를 일으킬 수 있음
bit field 대신에 EnumSet 쓰기
- 비트 연산자를 쓰게 되면 알아보기 어렵고 각 요소를 차례대로 반복해서 처리할 방법이 없다
- EnumSet의 각 요소들은 내부적으로 bit vector으로 표현되며, 갯수가 64개 이하라면 전체가 하나의 long으로 처리된다. (
RegularEnumSet
)
굳이 확장이 필요하다면 enum이 interface를 상속하게 만들기
- 대부분의 경우에는 필요없음. 단점이 더 많으므로.
특별한 네이밍 패턴을 만드는 대신에 Annotation을 쓰기
네이밍 패턴의 단점
- 오타가 났을 때 별도의 에러메세지 없이 그냥 무시, 스킵한다.
- 경우에 따라서는 원하지 않는 경우에도 실행해버릴 수 있다.
- 이름에다가 별도의 패러미터를 지정하게 될 경우, 컴파일러가 그 패러미터를 검사 할 수 없으므로 직접 돌려보기 전까지는 무슨 문제가 있는지 알 수 없다.
Annotation
Target
,Retention
등의 meta-annotation을 가짐- 별도의 변수를 가지지 않는 annotation을 marker annotation이라고 부름
- annotation 자체로는 아무런 의미도 가지지 않음. 그냥 말 그대로 metadata를 제공할 뿐. annotation을 달고 나서 리플렉션을 사용하여 별도의 로직을 수행해야함.
가능하면 @Override
Annotation을 모조리 붙이기
- 패러미터나 이름을 착각해서 override 대신에 overloading을 한다던가, 새로 구현한 메소드 대신에 super class의 메소드를 호출한다던가 하는 버그들을 막아준다.
Marker Interface vs Marker Annotation
- marker interface의 경우 runtime이 아닌 compile 시점에서 몇몇 문제점들을 미리 확인할 수 있다는 장점을 가지고 있다. 반면에 marker annotation은 돌려봐야 알 수 있다.
- marker interface의 경우 marker annotation에 비해서 좀 더 정확한 타겟을 지정할 수 있다. 예를 들어
Collection
인터페이스와 상속하는 클래스들에만 사용가능한 marker를 만들고 싶다고 가정했을 때 marker interface는 그냥Collection
을 상속한 인터페이스를 marker interface로 정의하면 끝이지만, annotation은@Target
에TYPE
이 들어가면 어떤 클래스나 인터페이스든지 붙일 수 있기 때문에 누군가 엉뚱한 클래스에 붙이는 것을 막기 힘들다. - marker annotation은 이미 사용중인 api라도 annotation을 더 늘리는 것으로 정보 추가가 가능하다. interface는 불가능하다.
- Method, Field 등 class, interface 외 다른 요소에 써야한다면 marker annotation을 쓰는 것이 맞다.
- marker가 지정된 클래스 내지는 인터페이스만을 다루는 메소드가 하나 이상 존재한다면 marker interface가 유리하다