Box<Apple> appleBox = new Box<Apple>();Box<Grape> grapeBox = new Box<Grape>();
타입 변수에 대입은 인스턴스 별로 다르게 가능
static 멤버에 타입 변수 사용 불가 (인스턴스 공통이기 때문에)
class Box<T> { static T item; // 에러. static int compare(T, t1, T t2) { ... } // 에러.}
배열 생성 시 타입 변수 사용불가. 타입 변수로 배열 선언은 가능
class Box<T> { T[] itemArr; // OK. T타입의 배열을 위한 참조변수 ... T[] toArray() { T[] tmpArr = new T[itemArr.length]; // 에러. 지네릭 배열 생성불가 ... }}
12~14. 와일드 카드, 지네릭 메서드
와일드 카드 <?>
하나의 참조 변수로 대입된 타입이 다른 객체를 참조 가능
ArrayList<? extends Product> list = new ArrayList<Tv>(); // OK.ArrayList<? extends Product> list = new ArrayList<Audio>(); // OK.ArrayList<Product> list = new ArrayList<Tv>(); // 에러. 대입된 타입 불일치
<? extends T> 와일드 카드의 상한 제한. T와 그 자손들만 가능
<? super T> 와일드 카드의 상한 제한. T와 그 조상들만 가능
<?> 제한 없음. 모든 타입이 가능. <? extends Object> 와 동일
static Juice makeJuice(FruitBox<? extends Fruit> box) { String tmp = ""; for (Fruit f : box.getList()) { tmp += f + " "; } return new Juice(tmp);}
메서드의 매개변수에 와일드 카드를 사용
지네릭 메서드
static <T> void sort(List<T> list, Comparator<? super T> c)
지네릭 타입이 선언된 메서드 (타입 변수는 메서드 내에서만 유효)
class FruitBox<T> { ... static <T> void sort(List<T> list, Comparator<? super T> c) { ... }}
System.out.println(<Fruit>makeJuice(fruitBox)); // 에러. 클래스 이름 생략불가System.out.println(this.<Fruit>makeJuice(appleBox)); // OK.System.out.println(Juicer.<Fruit>makeJuice(fruitBox)); // OK.
메서드를 호출할 때 타입을 생략하지 않을 때는 클래스 이름 생략 불가
15~16. 지네릭 형변환
지네릭 타입의 형변환
Box<Object> objBox = null;Box box = (Box)objBox; // OK. 지네릭 타입 -> 원시 타입. 경고 발생objBox = (Box<Object>)box; // OK. 원시 타입 -> 지네릭 타입. 경고 발생objBox = (Box<Object>)strBox; // 에러. Box<String> -> Box<Object>strBox = (Box<String>)objBox; // 에러. Box<Object> -> Box<String>
지네릭 타입과 원시 타입 간의 형변환은 바람직 하지 않음
Box<Object> objBox = (Box<Object>)new Box<String>(); // 에러. 형변환 불가능Box<? extends Object> wBox = (Box<? extends Object>)new Box<String>(); // OK.Box<? extends Object> wBox = new Box<String>(); // 위 문장과 동일// 매개변수로 FruitBox<Fruit>, FruitBox<Apple>, FruitBox<Grape> 등이 가능static Juice makeJuice<FruitBox<? extends Fruit> box) { ... }FruitBox<? extends Fruit> box = new FruitBox<Fruit>(); // OK.FruitBox<? extends Fruit> box = new FruitBox<Apple>(); // OK.
와일드 카드가 사용된 지네릭 타입으로는 형변환 가능
지네릭 타입의 제거
컴파일러는 지네릭 타입을 제거하고, 필요한 곳에 형변환을 넣음
지네릭 타입의 경계(bound)를 제거
지네릭 타입 제거 후 타입이 불일치하면, 형변환 추가
와일드 카드가 포함된 경우, 적절한 타입으로 형변환 추가
17~20. 열거형
열거형(enum)
관련된 상수들을 같이 묶어 놓은 것
class Card { enum Kind { CLOVER, HEART, DIAMOND, SPADE } enum Value { TWO, THREE, FOUR } final Kind kind; // 타입이 int가 아닌 Kind final Value value;}
class Unit { int x, y; // 유닛의 위치 Direction dir; // 열거형 인스턴스 변수 선언 void init() { dir = Direction.EAST; // 유닛의 방향을 EAST로 초기화 }}
열거형 타입의 변수를 선언하고 사용하는 방법
if (dir == Direction.EAST) { x++;} else if (dir > Direction.WEST) { // 에러. 열거형 상수에 비교연산자 사용 불가 ...} else if (dir.compareTo(Direction.WEST) > 0) { ...}
열거형 상수의 비교에 == 와 compareTo() 사용 가능
열거형의 조상 - java.lang.Enum
모든 열거형은 Enum의 자손이며, 아래의 메서드를 상속받음
// 열거형 상수의 이름을 문자열로 반환String name()// 열거형 상수가 정의된 순서를 반환int ordinal()// 지정된 열거형에서 name과 일치하는 열거형 상수를 반환T valueOf(Class<T> enumType, String name)// 모든 열거형 상수를 배열로 반환static E[] values()// 열거형 상수 이름으로 값 반환static E valueOf(String name)
21~22. 열거형 멤버 추가하기
열거형 멤버 추가하기
enum Direction { EAST(1), SOUTH(5), WEST(-1), NORTH(10) }
불연속적인 열거형 상수의 경우, 원하는 값을 괄호 안에 적음
enum Direction { EAST(1), SOUTH(5), WEST(-1), NORTH(10); private final int value; Direction(int value) { this.value = value; } public int getValue() { return value; }}
괄호를 사용하려면, 인스턴스 변수와 생성자를 새로 추가해 줘야 함
Direction d = new Direction(1); // 에러. 열거형의 생성자는 외부에서 호출불가
@interface TestInfo { String[] testTools();}@TestInfo(testTools = {"JUnit", "AutoTester"})@TestInfo(testTools = "JUnit")@TestInfo(testTools = {}) // 값이 없을 경우 빈괄호 필요
요소의 타입이 배열인 경우, 중괄호 사용
모든 애너테이션의 조상
Annotation은 모든 애너테이션의 조상이지만 상속 불가
사실 Annotation은 인터페이스
마커 애너테이션
요소가 하나도 정의되지 않은 애너테이션
public @interface Override{}public @interface Test{}