본문 바로가기
개념

[JAVA의 정석] Chapter12_연습문제

by cook_code 2024. 12. 15.
 

[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

 

올바른 호출 조건

  1. <T extends Fruit> 제네릭 타입이기 때문에 T는 Fruit의 자손이어야 한다.
  2. 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을 가짐.
 

 

 

반응형