※이 글은 Java SE 8 기준으로 작성되었습니다.

제네릭(Generics)

제네릭(Generics)은 어떤 타입 A(클래스나 인터페이스)나 메서드가 어떤 타입B를 다룰때 그 타입B를 컴파일러에 대하여 명시하는 구조를 말함. 즉 맘대로 갖다 쓸수있음

제네릭을 사용할 수 있는 타입을 제네릭 타입, 클래스를 제네릭 클래스, 인터페이스를 제네릭 인터페이스라고 부름.

제네릭의 정의

//제네릭 클래스 Foo 생성
class Foo<T> {
    //제네릭 타입 T가 사용가능하다.
    private T t;
    Foo() {}
    public T getT() {
        return t;
    }
    public void setT(T t) {
        this.t = t;
    }
}

public class Test{
    public static void main(String[] args) throws Exception {
        // 제네릭 클래스 인스턴스 선언
        Foo<Integer> f = new Foo<Integer>();
        // 제네릭 메서드 실행
        bar(f);
    }
    // 제네릭 메서드 bar 생성
    // 리턴 타입 앞에 <T>선언함으로서 T타입 사용이 가능해짐
    public static <T> Foo bar(T t) {
        Foo result = new Foo<T>();
        result.setT(t);
        return result;
    }
}

제네릭 클래스는 클래스명 뒤에 꺽쇠안에 제네릭 타입을 명시함.

제네릭 클래스를 사용할 때는 똑같이 꺽쇠안에 타입을 명시함(선언부에 명시 안하면 컴파일 에러)

제네릭 메서드는 리턴 타입앞에 꺽쇠안에 타입을 명시함.

위 소스에서는 제네릭 타입을 T라고 명시했는데, 이름은 맘대로 해도 됨.

제네릭 타입은 여러개 사용해도 무관.

타입 제한

// 제네릭 클래스 생성
// Number타입이거나 또는 서브 타입만 사용하도록 제한
class Foo<T extends Number> {
    private T t;
}

// &를 이용하여 부모클래스 & 인터페이스를 명시하여 제한이 가능
class Bar<T extends Number & Serializable> {
    private T t;
}

public class Test{
    public static void main(String[] args) throws Exception {
        Foo<Integer> f1 = new Foo<Integer>();
        Foo<String> f2 = new Foo<String>(); // String은 Number의 자식 클래스가 아니므로 컴파일 에러 발생
        Bar<Integer> b1 = new Bar<Integer>();
    }
}

T extends Number를 이용하여 타입제한이 가능함.

모든 클래스의 부모는 Object이기 때문에, T와 T extends Object는 같아보임 -> 결국 따로 명시안하면 Object형으로 인식해서 동작이 된 것을 확인

&기호를 이용하여 클래스 & 인터페이스 명시가 가능함.