자바칩

[자바의 신] 21장. 제네릭: 문제 풀이 본문

Study/Java

[자바의 신] 21장. 제네릭: 문제 풀이

아기제이 2024. 5. 15. 20:21
728x90

정리해 봅시다

 

1. 제네릭이 자바에 추가된 이유는 무엇인가요?

타입 형 변환에서 발생할 수 있는 문제점을 "사전"에 제거하기 위해 추가되었다. 여기서 "사전"이라고 하는 것은 실행 시에 예외가 발생하는 것을 처리하는 것이 아니라, 컴파일할 때 점검할 수 있도록 한 것을 말한다.

 

 

2. 제네릭 타입의 이름은 T나 E처럼 하나의 캐릭터로 선언되어야 하나요?

제네릭 타입의 이름은 어떤 것으로 선언하든 상관이 없다. 하지만 다른 어떤 사람이 보더라도 쉽게 이해할 수 있도록 하려면 자바에서 정의한 기본 규칙을 따르는 것이 좋다. 자바에서 정의한 기본 규칙은 다음과 같다.

* 자바에서 정의한 기본 규칙
E: 요소(Element, 자바 컬렉션에서 주로 사용됨)
K: 키
N: 숫자
T: 타입
V: 값
S, U, V: 두 번째, 세 번째, 네 번째에 선언된 타입

*사용 예시
public class WildcardGeneric<T> {
    T wildcard;
    public void setWildcard(T wildcard) {
        this.wildcard = wildcard;
    }
    public T getWildcard() {
        return wildcard;
    }
}

 

 

3. 메소드에서 제네릭 타입을 명시적으로 지정하기 애매할 경우에는 <> 안에 어떤 기호를 넣어 주어야 하나요?

? 기호를 사용한다. 이것을 wildcard 타입이라고 한다.
매개변수에 제네릭 타입을 와일드카드 타입으로 선언했다면, 매개변수로 모든 제네릭 타입을 넘겨줄 수 있다. 하지만 와일드카드 타입으로 선언된 객체의 값을 얻어올 수는 있어도, 값을 지정할 수는 없다. 따라서, 조회용 매개변수로 사용해야만 한다.

*사용 예시
public class WildcardSample {
    public static void main(String[] args) {
        WildcardSample sample = new WildcardSample();
        // 2번에 선언된 WildcardGeneric 클래스 참고
        WildcardGeneric<String> wildcard = new WildcardGeneric<String>();
        wildcard.setWildcard("Hello");
        sample.wildcardMethod(wildcard);
    }
    
    public void wildcardMethod(WildcardGeneric<?> c) {   
        // 와일드카드 타입으로 선언된 객체의 값을 얻어올 때에는 
        // 어떤 타입인지 정확히 알 수 없으므로 Object 타입으로 얻어와야 한다.
        Object value = c.getValue();    
        System.out.println(value);
     }
}

*출력 결과
Hello

 

 

4. 메소드에서 제네릭 타입을 명시적으로 지정하기에는 애매하지만, 어떤 클래스의 상속을 받은 특정 타입만 가능하다는 것을 나타내려면 <> 안에 어떤 기호를 넣어 주어야 하나요?

? extends 부모클래스 기호를 사용한다. 이것을 "Bounded Wildcards" 라고 부른다.
매개변수에 제네릭 타입을 Bounded Wildcards로 선언했다면, 매개변수로 부모 클래스를 상속받은 제네릭 타입만 넘겨줄 수 있다. 하지만 와일드카드 타입과 마찬가지로 Bounded Wildcards로 선언된 객체의 값을 얻어올 수는 있어도, 값을 지정할 수는 없다. 따라서, 조회용 매개변수로 사용해야만 한다.

*사용 예시
public class Car {
    protected name;
    public Car(String name) {
        this.name = name;
    }
    public String toString() {
        return "Car name = " + name;
    }
}

public class Bus extends Car {
    public Bus(String name) {
        super(name);
    }
    public String toString() {
        return "Bus name = " + name;
    }
}

public class WildcardSample {
    public static void main(String[] args) {
        WildcardSample sample = new WildcardSample();
        // 2번에 선언된 WildcardGeneric 클래스 참고
        WildcardGeneric<Bus> wildcard = new WildcardGeneric<Bus>();
        wildcard.setWildcard(new Bus("1164"));
        sample.wildcardMethod(wildcard);
    }

    public void wildcardMethod(WildcardGeneric<? extends Car> c) {   
        // Bounded WildCards로 선언된 객체의 값을 얻어올 때에는 
        // 부모 클래스 타입으로 얻어와야 한다.   
        Car value = c.getValue();    
        System.out.println(value);
    }
}

*출력 결과
Bus name = 1164

 

 

5. 제네릭 선언 시 wildcard라는 것을 선언했을 때 어떤 제약 사항이 있나요?

wildcard로 선언한 객체의 값을 얻어오는 것은 가능하지만, 값을 지정하는 것은 불가능하다.
이러한 단점을 해결하기 위한 방법은 리턴 타입 앞에 제네릭한 타입을 선언하고, 매개변수에 해당 타입을 사용하면 된다.

 

 

6. 메소드를 제네릭하게 선언하려면 리턴 타입 앞에 어떤 것을 추가해 주면 되나요?

리턴 타입 앞에 제네릭 타입을 선언해주면 된다. 그리고, 매개변수에 해당 타입을 사용하면 된다.
이렇게 하면 wildcard의 값을 지정해주는 것도 가능하다.

*사용 예시
public <T> void genericMethod(WildcardGeneric<T> c, T addValue) {
        c.setWildcard(addValue);
        T value = c.getWildcard();
        System.out.println(value);
}

직접해 봅시다

package d.generic.practice;

public class MaxFinder {
    public static void main(String[] args) {
        MaxFinder maxFinder = new MaxFinder();
        maxFinder.testGetMax();     // 4번
        maxFinder.testGetMin();      // 6번, 7번
    }

    // 1번
    public void testGetMax() {
        System.out.println(getMax(1, 2, 3));
        System.out.println(getMax(3, 1, 2));
        System.out.println(getMax(2, 3, 1));
        System.out.println(getMax("a", "b", "c"));
        System.out.println(getMax("b", "c", "a"));
        System.out.println(getMax("a", "b", "c"));
    }

    // 6번
    public void testGetMin() {
        System.out.println(getMin(1, 2, 3));
        System.out.println(getMin(3, 1, 2));
        System.out.println(getMin(2, 3, 1));
        System.out.println(getMin("a", "b", "c"));
        System.out.println(getMin("b", "c", "a"));
        System.out.println(getMin("a", "b", "c"));
    }

    // 3번
    public <T extends Comparable<T>> T getMax(T... a) {
        T maxT = a[0];
        for (T tempT : a) {
            if (tempT.compareTo(maxT) > 0) maxT = tempT;
        }
        return maxT;
    }

    // 5번
    public <T extends Comparable<T>> T getMin(T... a) {
        T minT = a[0];
        for (T tempT : a) {
            if (tempT.compareTo(minT) < 0) minT = tempT;
        }
        return minT;
    }
}