[자바의 정석] Chapter 12. 제네릭, 열거형, 어노테이션
제네릭
제네릭은 다양한 타입의 객체들을 다루는 메서드나 컬렉션 클래스에 컴파일 시의 타입 체크를 해주는 기능이다.
- 타입 안정성을 제공
- 타입체크와 형변환을 생략할 수 있어 코드가 간결
간단히 정리하자면, 다룰 객체의 타입을 미리 명시해 줌으로써 번거로운 형변환을 줄일 수 있다.
class Box<T> {
T item;
void setItem(T item) { this.item = item; |
T getItem() { return iterm; }
}
Box <T>
에서 T를 '타입 변수'라고 한다. 물론 T가 아니라 상황에 맞게 다른 변수로 작성해도 된다.f(x, y) = x + y와
f(k, v) = k + v
가 다르지 않은 것처럼 타입 변수명도 상관없다.
제네릭을 실제로 사용하기 위해선 아래처럼 코드를 작성해 주면 된다.
Box<String> b = new Box<String>(); // 타입 T 대신, 실제 타입을 지정
b.setItem(new Object()); // Error. String 이외의 타입은 지정 불가
b.setItem("ABC"); // OK. String 타입이므로 가능
String item = b.getItem(); // 형변환이 필요없음
저렇게 작성하면 제네릭 클래스 Box <T>는
class Box<String> {
String item;
void setItem(String item) { this.item = item; |
String getItem() { return iterm; }
}
위와 같이 정의된 것이랑 같다.
모든 객체에 대해 동일하게 동작해야 하는 static 멤버에는 타입 변수 T를 사용할 수 없다. T는 인스턴스 변수로 간주되기 때문이다.
그리고 제네릭은 new 연산자 때문에 배열을 생성할 수 없다.
왜냐하면 new 연산자는 컴파일 시점에 타입 T가 뭔지 정확히 알아야 한다. 그런데 Box T 클래스는 컴파일하는 시점에서 T가 어떤 타입이 될지 전혀 알 수 없다.
이러한 이유로 instansof 연산자도 T를 피연산자로 사용할 수 없다.
제네릭 클래스의 객체 생성과 사용
- 참조변수와 생성자에 대입된 타입이 일치해야 한다.
Box <Apple> appleBox = new Box<Apple>(); // OK Box<Apple> appleBox = new Box <Grape>(); // 에러에러
- 두 타입이 상속관계에 있어도 대입된 타입이 일치해야 한다.
Apple이 Fruit의 자손이라고 가정하자 Box <Fruit> appleBox = new Box <Apple>(); // 에러에러
//선언할 때만 에러가 뜨는 것이고, 객체를 추가할 때는 괜찮다.
Box fruitBox = new Box();
fruitBox.add(new Fruit());
fruitBox.add(new Apple()); // 이건 OKOK
3. 두 제네릭 클래스의 타입이 상속관계에 있고 대입된 타입이 같으면 OK
FruitBox가 Box의 자손이라고 가정하자
```java
Box<Apple> appleBox = new FruitBox<Apple>(); // OK
열거형
요소, 멤버라 불리는 명명된 값의 집합을 이루는 자료형이다. 열거자 이름들은 일반적으로 해당 언어의 상수 역할을 하는 식별자이다.
class Card {
enum Kind { CLOVER, HEART, DIAMOND, SPADE }
enum Value { TWO, THREE, FOUR }
final Kind kind;
final Value value;
}
if(Card.CLOVER == Card.TWO) // true지만 false이어야 의미상 맞음
if(Card.Kind.CLOVER==Card.Value.TWO) // false
열거형의 정의와 사용
열거형을 정의하는 방법은 간단하다.
enum 열거형 이름 { 상수명1, 상수명2 }
// 동서남북
enum Direction { EAST, SOUTH, WEST, NORTH }
열거형에 정의된 상수를 사용하는 방법은 열거형이름. 상수명
Direction.EAST
열거형들의 값의 일치 여부를 비교할 때는 == 를 이용하지만 대소비교는 compareTo()를 이용한다.
열거형에 멤버 추가하기
Enum 클래스에 정의된 ordinal()이 열거형 상수가 정의된 순서를 반환하지만, 이 값을 열거형 상수의 값으로 사용하지 않는 것이 좋다.
열거형 상수의 값이 불규칙적인 경우에는
enum Direction { EAST(1), SOUTH(5), WEST(-1), NORTH(10) }
이와 같이 열거형 상수의 이름 옆에 원하는 값을 괄호와 함께 적어주면 된다.
열거형의 이해부터
// 열거형
enum Direction { EAST, SOUTH, WEST, NORTH }
// 클래스
class Direction {
static final Direction EAST = new Direction("EAST");
static final Direction EAST = new Direction("SOUTH");
static final Direction EAST = new Direction("WEST");
static final Direction EAST = new Direction("NORTH");
private String name;
private Direction(String name) {
this.name = name;
}
}
어노테이션 (annotation)
어노테이션은 메타 데이터로서, 프로그램 그 자체의 일부분은 아니지만 프로그램에 대한 데이터를 제공한다. 그래서 어노테이션 자체는 어노테이션을 붙은 코드 동작에 영향을 미치지는 않는다. 참고로 어노테이션은 다음과 같은 상황에 사용된다.
- 컴파일러에게 필요한 정보를 제공
컴파일러가 에러를 감지하거나, 경고를 띄우지 않게 하기 위함. - 컴파일/배포 시에 필요한 처리 기능
SW 개발 툴에서 어노테이션의 정보를 통해 특정 코드를 추가할 수 있음. - 런타임 처리 제공
런타임에도 어노테이션의 정보를 통해 필요한 처리를 할 수 있음. (Java Reflection)