BLOG

Java Generics - 4

May 03, 2020

Bounded Type Parameters

타입을 파라미터화 할때 제한된 type argument를 원하는 경우가 있습니다. 예를 들어 숫자에서 작동하는 메서드는 Number 혹은 그 하위 클래스만 인스턴스로 허용하려 합니다. 이것이 Bounded Type Parameter가 필요한 이유입니다.

bounded type parameter을 선언하기 위해서는 타입 파라미터의 이름을 extends 키워드와 함께 최상위 클래스를(여기서는 Number) 나열해야 합니다. 중요한 것은 여기서는 extends는 일반적인 의미로 클래스의 extends, 인터페이스의 implements 모두를 의미합니다.

public class Box<T> {

    private T t;          

    public void set(T t) {
        this.t = t;
    }

    public T get() {
        return t;
    }

    public <U extends Number> void inspect(U u){
        System.out.println("T: " + t.getClass().getName());
        System.out.println("U: " + u.getClass().getName());
    }

    public static void main(String[] args) {
        Box<Integer> integerBox = new Box<Integer>();
        integerBox.set(new Integer(10));
        integerBox.inspect("some text"); // error: this is still String!
    }
}

우리의 generic method를 이 bounded type parameter를 포함하도록 수정하면 inspect의 호출이 String을 포함하고 있기에 컴파일 에러가 발생하게 됩니다.

Box.java:21: <U>inspect(U) in Box<java.lang.Integer> cannot
  be applied to (java.lang.String)
                        integerBox.inspect("10");
                                  ^
1 error

또 타입을 제한하는 것은 generic type을 인스턴스화할 수 있게 해주는데, bounded type parameter이 bound 내에 정의되어 있는 메서드 호출을 허용하기 때문입니다.

public class NaturalNumber<T extends Integer> {

    private T n;

    public NaturalNumber(T n)  { this.n = n; }

    public boolean isEven() {
        return n.intValue() % 2 == 0;
    }

    // ...
}

isEven 메서드는 n을 통하여 Integer 클래스에서 정의되어 있는 intValue 메서드를 호출하는 것입니다.


Multiple Bounds


앞의 예는 단일 bound를 이용한 타입 파라미터를 사용하는 것을 보여주지만, type parameter은 여러 bound를 가질 수 있습니다.

<T extends B1 & B2 & B3>

여러 bound를 가진 타입 변수는 bound 에 있는 모든 타입 리스트의 subtype입니다. 이때 한 bound가 클래스라면 해당 bound가 가장 먼저 나타나야 합니다. 다음은 그 예시입니다.

Class A { /* ... */ }
interface B { /* ... */ }
interface C { /* ... */ }

class D <T extends A & B & C> { /* ... */ }

A가 가장 먼저 나오지 않았다면 compile-time error를 만나게 됩니다.

class D <T extends B & A & C> { /* ... */ }  // compile-time error


Generic Methods and Bounded Type Parameters

Bounded type parameter은 generic 알고리즘 구현에 있어 중요한 역할입니다. 다음과 같은 배열 T[]에서 elem 보다 큰 요소의 갯수를 세는 메서드를 보세요.

public static <T> int countGreaterThan(T[] anArray, T elem) {
    int count = 0;
    for (T e : anArray)
        if (e > elem)  // compiler error
            ++count;
    return count;
}

메서드 구현은 간단하지만 컴파일에는 실패하게됩니다. > 라는 연산자는 primitive 타입인 short, int, double, long, float, byte, char 등에서만 작동하기 때문입니다. 객체간 비교에 있어서는 > 를 사용할 수 없습니다. 이 문제를 해결하려면 Comparable<T> 인터페이스를 이용하여 type paramter bounded를 사용해야 합니다.

public interface Comparable<T> {
    public int compareTo(T o);
}

그 결과 다음과 같이 사용할 수 있습니다.

public static <T extends Comparable<T>> int countGreaterThan(T[] anArray, T elem) {
    int count = 0;
    for (T e : anArray)
        if (e.compareTo(elem) > 0)
            ++count;
    return count;
}


Reference


# Java# Generics


Written by Johnie Yeo