[12-1] 클래스 Box 가 다음과 같이 정의되어 있을 때, 다음 중 오류가 발생하는 문장은?
경고가 발생하는 문장은?
class Exercise12_1 {
public static void main(String[] args) {
}
}
class Box<T>{
T item;
void setItem(T item){this.item = item;}
T getItem(){return item;}
}
a. Box<Object> b = new Box<String>( );
b. Box<Object> b = (Object)new Box<String>( );
c. new Box<String>( ).setItem(new Object( ));
d. new Box<String>( ).setItem("ABC");
답
a,b,c
a. Box<Object> b = new Box<String>( );
-> Box<Object>를 Box<String>으로 초기화하려는 시도는 제네릭 타입 불일치로 오류가 발생함.
b. Box<Object> b = (Object)new Box<String>( );
-> Object로 캐스팅하여 초기화하려는 시도도 타입 불일치로 오류가 발생함.
c. new Box<String>( ).setItem(new Object( ));
-> Box<String>에 Object 타입의 값을 넣으려는 시도도 제네릭 타입 불일치로 오류가 발생함.
[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;
}
a.Juicer.<Apple>makeJuice(new FruitBox<Fruit>());
b.Juicer.<Fruit>makeJuice(new FruitBox<Grape>());
c.Juicer.<Fruit>makeJuice(new FruitBox<Fruit>());
d.Juicer.makeJuice(new FruitBox<Apple>());
e.Juicer.makeJuice(new FruitBox<Object>());
답
c,d
올바른 호출 조건
- <T extends Fruit> 제네릭 타입이기 때문에 T는 Fruit의 자손이어야 한다.
- FruitBox<T>는 Fruit의 자손 타입을 받아야 한다.
c. Juicer.<Fruit>makeJuice(new FruitBox<Fruit>());
올바른 호출: FruitBox<Fruit>는 Fruit 타입을 정확히 맞추고 있습니다.
d. Juicer.makeJuice(new FruitBox<Apple>());
올바른 호출: FruitBox<Apple>는 Apple이 Fruit의 자손이므로 허용됨.
----------------------------
a. Juicer.<Apple>makeJuice(new FruitBox<Fruit>());
잘못된 호출: FruitBox<Fruit>는 FruitBox<Apple>과 호환되지 않음.
b. Juicer.<Fruit>makeJuice(new FruitBox<Grape>());
잘못된 호출: FruitBox<Grape>는 FruitBox<Fruit>과 호환되지 않음.
e. Juicer.makeJuice(new FruitBox<Object>());
잘못된 호출: Object는 Fruit의 자손이 아니므로 허용되지 않음.
[12-3] 다음 중 올바르지 않은 문장을 모두 고르시오.
class Box<T extends Fruit> { // 지네릭 타입 T를 선언
T item;
void setItem(T item) {this.item = item;}
T getItem() {return item;}
}
a. Box<?> b = new Box( );
b. Box<?> b = new Box<>( );
c. Box<?> b = new Box<Object>( );
d. Box<Object> b = new Box<Fruit>( );
e. Box b = new Box<Fruit>( );
f. Box<? extends Fruit> b = new Box<Apple>( );
g. Box<? extends Object> b = new Box<? extends Fruit>( );
답
c,d,g
풀이
c. Box<?> b = new Box<Object>( );
올바르지 않음. Box<Object>는 Box<T extends Fruit>의 제약 조건을 충족하지 않음. Object는 Fruit의 자손이 아니므로 제네릭 타입 제약에 맞지 않음.
d. Box<Object> b = new Box<Fruit>( );
올바르지 않음. 제네릭 타입 간에는 상속 관계가 없으므로, 이는 타입 불일치로 컴파일 오류가 발생합니다. Box<Object>와 Box<Fruit>는 서로 호환되지 않음.
g. Box<? extends Object> b = new Box<? extends Fruit>( );
올바르지 않음. new Box<? extends Fruit>() 구문은 올바르지 않습니다. 와일드카드 타입은 인스턴스화에 사용할 수 없습니다.
----------------------------
a. Box<?> b = new Box( );
올바르지 않음. Box 클래스는 타입 매개변수를 명시적으로 제공해야 한다.
Box<?>로 선언했지만, Box를 타입 없이 인스턴스화하는 것은 허용되지 않음.
b. Box<?> b = new Box<>( );
올바르지 않음. Box<?>로 선언했지만, Box<>를 타입 없이 인스턴스화하는 것은 허용되지 않음.
e. Box b = new Box<Fruit>( );
올바름. 제네릭 타입을 사용하지 않는 원시 타입을 사용하는 것이며, 경고는 발생하지만 문법적으로는 문제가 없음.
f. Box<? extends Fruit> b = new Box<Apple>( );
올바름. Box<? extends Fruit>는 Fruit의 자손 타입을 허용하며, Box<Apple>은 이에 해당.
[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;
}
답
import java.util.ArrayList;
class Product {}
public class Excercise12_4 {
public static <T extends Product_2> ArrayList<T> merge(
ArrayList<T> list, ArrayList<T> list2) {
ArrayList<T> newList = new ArrayList<>(list);
newList.addAll(list2);
return newList;
}
public static void main(String[] args) {
ArrayList<Product_2> list1 = new ArrayList<>();
ArrayList<Product_2> list2 = new ArrayList<>();
// Example usage:
ArrayList<Product_2> mergedList = merge(list1, list2);
}
}
풀이 : 제네릭 메서드로 변경하려면, Product 타입에 의존하지 않고 어떤 타입의 ArrayList도 병합할 수 있도록 해야한다. 이를 위해 메서드에 제네릭 타입 파라미터를 추가하면 된다.
1. <T extends Product>: 제네릭 타입 파라미터 T를 선언하며, T는 Product의 서브타입이어야 함을 명시
2. ArrayList<T>: 반환 타입과 매개변수 타입을 T로 설정하여 동일한 타입을 처리할 수 있도록 함
3. ArrayList<T> newList = new ArrayList<>(list);: 첫 번째 리스트를 복사하여 새로운 리스트를 생성
4. newList.addAll(list2);: 두 번째 리스트의 모든 요소를 새로운 리스트에 추가
[12-5] 아래는 예제 7-3에 열거형 Kind와 Number를 새로 정의하여 적용한 것이다. (1)에 알맞은 코드를 넣어 예제를 완성하시오. (Math.random( )을 사용했으므로 실행결과가 달라질 수 있다.)
[CLOVER,ACE]
[HEART,TEN]
답
class DeckTest {
public static void main(String args[]) {
Deck d = new Deck(); // 카드 한 벌(Deck)을 만든다.
Card c = d.pick(0); // 섞기 전에 제일 위의 카드를 뽑는다.
System.out.println(c); // System.out.println(c.toString());과 같다.
d.shuffle(); // 카드를 섞는다.
c = d.pick(0); // 섞은 후에 제일 위의 카드를 뽑는다.
System.out.println(c);
}
}
class Deck {
final int CARD_NUM = Card.Kind.values().length * Card.Number.values().length; // 카드의 개수
Card cardArr[] = new Card[CARD_NUM]; // Card 객체 배열을 포함
Deck() {
/*
* (1) 알맞은 코드를 넣어서 완성하시오.
* Deck의 카드를 초기화한다.
*/
int index = 0;
for (Card.Kind kind : Card.Kind.values()) {
for (Card.Number num : Card.Number.values()) {
cardArr[index++] = new Card(kind, num);
}
}
}
Card pick(int index) { // 지정된 위치에 있는 카드 하나를 꺼내서 반환
return cardArr[index];
}
Card pick() { // Deck에서 카드 하나를 선택한다.
int index = (int) (Math.random() * CARD_NUM);
return pick(index);
}
void shuffle() { // 카드의 순서를 섞는다.
for (int i = 0; i < cardArr.length; i++) {
int r = (int) (Math.random() * CARD_NUM);
Card temp = cardArr[i];
cardArr[i] = cardArr[r];
cardArr[r] = temp;
}
}
}
// Card 클래스
class Card {
enum Kind { CLOVER, HEART, DIAMOND, SPADE }
enum Number { ACE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING }
Kind kind;
Number num;
Card() {
this(Kind.SPADE, Number.ACE);
}
Card(Kind kind, Number num) {
this.kind = kind;
this.num = num;
}
public String toString() {
return "[" + kind.name() + "," + num.name() + "]";
}
}
풀이
제네릭 클래스 Deck<T>: Deck 클래스는 제네릭 타입 T를 사용하며, 이 타입은 Card를 상속받아야 한다.
1. cardArr 배열은 T 타입의 카드 객체들을 저장
2. 제네릭 타입 배열을 생성할 때는 @SuppressWarnings("unchecked") 어노테이션을 사용하여 경고를 억제
3. 제네릭 메서드 사용: pick 메서드는 제네릭 타입 T를 반환
4. pick(int index): 지정된 위치에 있는 카드를 반환
5. pick(): 무작위로 선택된 카드를 반환
6. 카드 섞기: shuffle 메서드는 카드 배열의 순서를 무작위로 섞기
참고 : Excercise12_5 상속 관계도
사진 설명을 입력하세요.
[12-6] 다음 중 메타 애너테이션이 아닌 것을 모두 고르시오.
a. Documented
b. Target
c. Native
d. Inherited
답
c
메타 에너테이션은 java.lang.annotation 패키지에 포함되어 있다.
애너테이션을 위한 보조 애너테이션 역할을 한다.
[12-7] 애너테이션 TestInfo가 다음과 같이 정의되어 있을 때, 이 애너테이션이 올바르게 적용되지 않은 것은?
@interface TestInfo {
int count() default 1;
String[] value() default "aaa";
}
a. @TestInfoclass Exercise12_7 {}
b.@TestInfo(1)class Exercise12_7 {}
c.@TestInfo("bbb")class Exercise12_7 {}
d.@TestInfo("bbb","ccc") class Exercise12_7 {}
답
b, d
풀이
b. @TestInfo(1) class Exercise12_7 {}
올바르지 않음. count는 기본적으로 value가 아니므로 value 요소에 직접 전달 불가. 컴파일 오류 발생.
d. @TestInfo("bbb","ccc") class Exercise12_7 {}
올바르지 않음. value 요소가 문자열 배열이므로 여러 값을 지정할 수 있지만, 이 경우 배열 표기법을 사용해야 함. 따라서 컴파일 오류 발생. 올바른 표기는 @TestInfo({"bbb","ccc"}).
----------------------------
a. @TestInfo class Exercise12_7 {}
올바름. 애너테이션을 기본값으로 적용. count는 1, value는 ["aaa"]로 설정.
c. @TestInfo("bbb") class Exercise12_7 {}
올바름. 단일 값 요소가 value일 때 생략 가능. value 요소가 ["bbb"]로 설정되고 count는 기본값 1을 가짐.
'개념' 카테고리의 다른 글
[JAVA의 정석] Chapter14_연습문제 (1) | 2024.12.15 |
---|---|
[JAVA의 정석] Chapter13_연습문제 (0) | 2024.12.15 |
[JAVA의 정석] Chapter11_연습문제 (0) | 2024.12.15 |
[JAVA의 정석] Chapter10_연습문제 (0) | 2024.12.15 |
[JAVA의 정석] Chapter09_연습문제 (0) | 2024.12.15 |