enums는 서로 관련된 상수를 편리하게 선언하기 위한 것으로, 여러 상수를 정의할 때 사용하면 유용하다. JDK1.5부터 추가되었다.
class Card {
enum Kind { CLOVER, HEART, DIAMOND, SPADE }
enum Value { TWO, THREE, FOUR }
final Kind kind;
final Value value;
}
enums를 위와 같이 선언하게 되면, 0부터 시작해서 차례대로 연속적인 값이 할당된다. enums를 선언했으면 다음과 같이 사용하면 된다.
System.out.println(kind.CLOVER);
System.out.println(kind.HEART);
System.out.println(value.TWO);
또한 enum의 중요한 특징은 값이 같아도 타입이 다르면 컴파일 에러가 발생한다는 점이다.
if(kind.CLOVER == kind.HEART) // false, 컴파일 OK
if(kind.CLOVER == value.TWO) // 의미상으론 true지만 컴파일 에러
만약 값을 저장하고 싶다면 다음과 같이 작성한다.
class Card {
enum Kind { CLOVER(2), HEART(4), DIAMOND(6), SPADE(8) } // 2, 4, 6, 8
enum Value { TWO(2), THREE(3), FOUR(4) } // 2, 3, 4
}
이후에 지정된 값을 저장할 수 있는 인스턴스 변수와 생성자를 새로 추가해 주어야 한다.
class Card {
enum Kind {
CLOVER(2), HEART(4), DIAMOND(6), SPADE(8);
private final int value;
Kind(int value) { this.value = value; }
public int getValue() { return value; }
}
enum Value {
TWO(2), THREE(3), FOUR(4)
private final int value;
Value(int value) { this.value = value; }
public int getValue() { return value; }
}
}
필요하다면 하나의 상수에 여러 값을 지정할 수도 있다. 대신 그만큼 인스턴스 변수와 생성자를 새로 추가해주어야 한다.
enum Direction {
EAST(1, ">"), SOUTH(2, "V"), WEST(3, "<"), NORTH(4, "^");
private final int value;
private final String symbol;
Direction(int value, String symbol) {
this.value = value;
this.symbol = symbol;
}
public int getValue() { return value; }
public String getSymbol() { return symbol; }
}
enums가 다음과 같이 정의되어 있을 때
enum Direction { EAST, SOUTH, WEST, NORTH }
enums 상수 하나하나가 Direction 객체다. 위 문장을 클래스로 정의하면 다음과 같다.
class Direction {
static final Direction EAST = new Direction("EAST");
static final Direction SOUTH = new Direction("SOUTH");
static final Direction WEST = new Direction("WEST");
static final Direction NORTH = new Direction("NORTH");
private String name;
private Direction(String name) {
this.name = name;
}
}
static 상수 EAST, SOUTH, WEST, NORTH 값은 객체의 주소이고, 이 값은 바뀌지 않는 값(final)이므로 '=='로 비교가 가능한 것이다.
어노테이션은 주석처럼 프로그래밍 언어에 영향을 미치지 않으면서 다른 프로그램 또는 프로그램 내에서 유용한 정보를 제공할 수 있는 방법이다.
@Override
public void print() {
...
}
어노테이션은 표준 어노테이션과 메타 어노테이션으로 나뉘는데, 전자는 주로 컴파일러를 위한 것으로 컴파일러에게 유용한 정보를 제공하고 후자는 새로운 어노테이션을 정의할 때 사용한다.
표준 어노테이션의 종류
@Override // 컴파일러에게 오버라이딩하는 메서드라는 것을 알린다. @Deprecated // 앞으로 사용하지 않을 것을 권장하는 대상에 붙인다. @SuppressWarnings // 컴파일러의 특정 경고 메시지가 나타나지 않게 한다. @SafeVarargs // 제네릭 타입의 가변인자에 사용한다. JDK1.7부터 @FuncionalInterface // 함수형 인터페이스라는 것을 알린다. JDK1.8부터 @Native // native메서드에서 참조되는 상수에 붙인다. JDK1.8부터
메타 어노테이션의 종류
@Target // 어노테이션이 적용가능한 대상을 지정하는데 사용한다. @Documented // 어노테이션 정보가 javadoc으로 작성된 문서에 포함되게 한다. @Inherited // 어노테이션이 자손 클래스에 상속되게 한다. @Retention // 어노테이션이 유지되는 범위를 지정하는데 사용한다. @Repeatable // 어노테이션을 반복해서 적용할 수 있게 한다. JDK 1.8부터
새로운 어노테이션을 정의하는 방법은 다음과 같다.
@interface 어노테이션이름 {
타입 요소이름();
...
}
어노테이션의 요소는 다음과 같은 규칙을 지켜야 한다.
@interface TestInfo {
int count();
String testedBy();
String[] testTools();
TestType testType(); // enum TestType { FIRST, FINAL }
DateTime testDate();
}
@interface DateTime {
String yymmdd();
String hhmmss();
}
어노테이션의 요소는 반환값이 있고 매개변수는 없는 추상 메서드의 형태를 가지며, 상속을 통해 구현하지 않아도 된다. 다만 어노테이션을 적용할 때 이 요소의 값을 빠짐없이 지정해주어야 한다.
@TestInfo(
count = 3, testedBy="kim",
testTools = {"JUnit", "AutoTester"},
testType = TestType.First,
testDate = @DateTime(yymmdd="160101", hhmmss="235959")
)
public class NewClass { ... }
어노테이션을 정의할 때 기본값을 설정할 수도 있다.
@interface TestInfo {
int count() default 1; // 기본값 설정
String testedBy() default "JUnit"; // 기본값 설정
String[] testTools() default {"JUnit", "AutoTester"};
TestType testType(); // enum TestType { FIRST, FINAL }
DateTime testDate();
}
@interface DateTime {
String yymmdd();
String hhmmss();
}
// count = 1, testedBy = "JUnit", testTools = {"JUnit", "AutoTester"} 과 같음
@TestInfo(
testType = TestType.First,
testDate = @DateTime(yymmdd="160101", hhmmss="235959")
)
public class NewClass { ... }