6장 제네릭 클래스

Jasik·2021년 12월 11일
0

제네릭 클래스

타입 파라미터를 한 개 이상 받는 클래스
example

@Getter
public class Entry<K, V> {
    private final K key;
    private final V value;

    public Entry(K key, V value) {
        this.key = key;
        this.value = value;
    }
}

Entry<String, Integer> 클래스는
String getKey(), Integer getValue() 메서드를 가진다.

Entry<String, Integer> entry = new Entry<>("salary", 100);

와 같이 객체를 생성할 때 생성자에서 타입 파라미터를 생략할 수 있다.
다이아몬드 문법이라고 한다. 타입을 추론한다.

제네릭 메서드

public class Arrays {
    public static <T> void sort(T[] a, Comparator<? super T> c) {
        if (c == null) {
            sort(a);
        } else {
            if (LegacyMergeSort.userRequested)
                legacyMergeSort(a, c);
            else
                TimSort.sort(a, 0, a.length, c, null, 0, 0);
        }
    }
    ...
}

타입 파라미터를 받는 메서드. 일반 클래스나 제네릭 클래스의 멤버가 될 수 있다.

선언 시 타입 파라미터를 제어자와 반환타입 사이에 둔다.

메서드 선언 시 타입 파라미터를 명시하지 않아도 컴파일러가 추론해준다.

원한다면 메서드 이름 앞에 명시한다.

Arrays.<String>sort(stringList, null);

타입 경계

public static <T extends AutoCloseable> void closeAll(ArrayList<T> elements) throws Exception {
    for (T element : elements) {
        element.close();
    }
}

위 타입 경계 지정은 T 가 AutoCloseable의 서브타입임을 보장한다. 인터페이스, 클래스 상관없이 extends 키워드만 쓴다.
ArrayList<PrintStream> 은 전달할 수 있지만, ArrayList<String> 은 전달할 수 없다.

T extends Runnable & AutoCloseable 과 같이 다중 경계도 지정 가능하다.

서브타입 와일드카드

    public static void printNames(ArrayList<? extends Employee> staffs) {
        for (Employee employee : staffs) {
            System.out.println(employee.getName());
        }
    }

슈퍼타입 와일드카드

    public static void printAll(Employee[] employees, Predicate<? super Employee> filter) {
        for (Employee employee : employees) {
            if (filter.test(employee)) {
                System.out.println(employee.getName());
            }
        }
    }

JVM 에서의 제네릭

타입 소거

제네릭 타입을 정의하면 해당 타입은 로(raw) 타입으로 컴파일된다.
ex)

@Getter
public class Entry<K, V> {
    private final K key;
    private final V value;

    public Entry(K key, V value) {
        this.key = key;
        this.value = value;
    }
}

to

public class Entry {
    private final Object key;
    private final Object value;

    public Entry(Object key, Object value) {
        this.key = key;
        this.value = value;
    }
    
    public Object getKey() {
        return key;
    }
    
    public Object getValue() {
        return value;
    }
}

제네릭의 제약

primitive 인자가 없다

실행 시간에는 모든 타입이 raw 형태다

ArrayList<String> list = (ArrayList<String>) result;
와 같은 경우 result가 raw ArrayList 인지만 검사한다.

타입 변수의 인스턴스를 만들 수 없다

T(...), new T[...] 와 같은 표현식 사용 불가

파라미터화된 타입의 배열을 생성할 수 없다

Entry<String, Integer>[] entries = new Entry<String, Integer>[100]; 불가

정적 컨텍스트에서는 클래스 타입 변수가 유효하지 않다

예외와 제네릭

제네릭 클래스의 객체는 예외로 던지거나 잡아낼 수 없다.
public class Problem<T> extends Exception 불가. 제네릭 클래스는 Throwable의 서브타입이 될 수 없다

profile
가자~

0개의 댓글