Java의 정석 - 제너릭스, 열거형, 에너테이션
예제 12-1
클래스 Box가 다음과 같이 정의되어 있을 때 다음 중 오류가 발생하는 문장은?
경고가 발생하는 문장은?
class Box<T> { // 지네릭 타입 T를 선언
T item;
void setItem(T item) {
this.item = item;
}
T getItem() {
return item;
}
}
- Box<Object> b = new Box<String>();
대입된 타입이 다르므로 에러
- Box<Object> b = (Object)new Box<String>();
(Object)Box<String>과 Box<Object>는 타입이 다르다.
정확히 뭐가 다른지는 모르겠으나 그렇게 이해하기로 했다. ..
- new Box()<String>.setItem(new Object());
대입된 타입이 String만 가능하므로, SetItem의 매개변수 역시 String만 가능하다.
- new Box()<String>.setItem(“ABC”);
“ABC”는 String타입이므로 OK
예제 12-2
지네릭 메서드 makeJuice() 가 아래와 같이 정의되어 있을 때 이 메서드를 올바르게 호출한 문장을 모두 고르시오 Apple과 Grape는 Fruit의 자손이라고 가정하자.
class Juicer {
static <T extends Fruit> String makeJuice(FruitBox<T> box) {
String tmp = "";
for(Fruit f : box.getList())
tmp += f + " ";
return tmp;
}
}
-
Juicer.<Apple>makeJuice(new FruitBox<Fruit>());
제너릭 메서드의 타입이 Apple이면
메서드의 매개변수는 FruitBox<Apple>이어야 한다.
-
Juicer.<Fruit>makeJuice(new FruitBox<Grape>());
자손관계에 있는 경우에도 타입이 다르면 안된다.
-
Juicer.<Fruit>makeJuice(new FruitBox<Fruit>());
OK
-
Juicer.makeJuice(new FruitBox<Apple>());
OK. 메서드의 타입 호출이 생략된 형태
-
Juicer.makeJuice(new FruitBox<Object>());
생략되지 않은 형태는
Juicer.<Object>makeJuice(new FruitBox<Object>());
T extends Fruit의 조건에 부합하지 않아 에러
예제 12-3
다음 중 올바르지 않은 문장을 모두 고르시오.
class Box<T extends Fruit> { // T 지네릭 타입 를 선언
T item;
void setItem(T item) {
this.item = item;
}
T getItem() {
return item;
}
}
-
Box<?> b = new Box();
Box<?> = Box<? extends Object>.
new Box()대신 new Box<>()를 사용해 주는게 좋긴 하지만 OK
-
Box b = new Box<>();
Box<>()는 타입을 생략한 것으로, 일반적으로 참조변수의 타입과 같은 타입으로 간주된다.
생략된 타입은 Object가 아니라 지네릭 클래스 Box의 타입인 T extends Fruit이 생략된 것이다. 그래서 new Box<Object>라고 하면 에러난다.
-
Box<?> b = new Box<Object>();
위의 이유로 에러
-
Box<Object> b = new Box<Fruit>();
자손관계라도 타입 불일치이면 에러
-
Box b = new Box<Fruit>();
OK. Box<?> b로 표기하는 게 낫다고 함.
-
Box<? extends Fruit> b = new Box<Apple>();
굿굿
-
Box<? extends Object> b = new Box<? extends Fruit>();
new 연산자는 타입이 명확해야 한다.
와일드 카드처럼 애매하게 사용 불가
예제 12-4
아래의 메서드는 두 개의 ArrayList를 매개변수로 받아서 하나의 새로운 ArrayList로 병합하는 메서드이다. 이를 지네릭 메서드로 변경하시오.
public static ArrayList<? extends Product> merge(
ArrayList<? extends Product> list,
ArrayList<? extends Product> list2) {
ArrayList<? extends Product> newList = new ArrayList<>(list);
newList.addAll(list2);
return newList;
}
를
public static <T extends Product> ArrayList<T> merge(
ArrayList<T> list, ArrayList<T> list2) {
ArrayList<T> newList = new ArrayList<>(list);
newList.addAll(list2);
return newList;
}
지네릭 메서드에 선언된 지네릭 타입은
지역 변수를 선언한 것과 같다고 보면 된다고 한다.
예제 12-6
애너테이션 TestInfo가 다음과 같이 정의되어 있을 때, 이 애너테이션이 올바르게 적용되지 않은 것은?
@interface TestInfo {
int count() default 1;
String[] value() default "aaa";
}
-
@TestInfo class Exercise12_7 {}
default값이 지정되어 있을 때는 값을 생략해도 된다.
-
@TestInfo(1) class Exercise12_7 {}
요소의 이름이 value가 아닌 경우에는 값을 생략할 수 없다.
@TestInfo(count=1) 가 정답.
-
@TestInfo(“bbb”) class Exercise12_7 {}
-
@TestInfo(“bbb”,”ccc”) class Exercise12_7 {}
요소의 타입이 배열이고, 지정하려는 값이 여러 개인 경우 괄호를 써주어야 한다.
@TestInfo({“bbb”, “ccc”}) @TestInfo(value={“bbb”,”ccc”})가 정답.