JAVA 제너릭, 지네릭 문법 요약 정리

seop
4 min readJan 29, 2022

지네릭에는 다양한 문법체계가 복합돼 있으므로, 명쾌하게 정리하기 보다는 중간에 여러 개념을 섞어 응용하는 방식으로 진행했습니다. 따라서 지네릭에 대한 기본 학습을 하고 오시는걸 추천드립니다.

기초 사용법

class Box<T> {
T item;
void setItem(T item) { this.item = item;
T getItem() { return item; }
// static void staticMethod(T arg) {} // Error!
// T[] tmpArr = new T[3]; // Error!
static <A> void correctStaticMethod(A arg) {}}Box<String> b = new Box<String>();

클래스를 위와같이 정의하고, 객체 생성시 <> 안에 지정하고 싶은 타입을 넣어주면 됩니다. 단, static 멤버에는 사용할 수 없습니다. 타입변수 T란, 인스턴스와 관련된 것이기 때문입니다. 또한, 지네릭 타입 배열 생성 또한 볼가능 합니다. new 연산으로 타입변수 T의 배열을 만드는 코드를 컴파일러가 컴파일 타임시 허용하지 않습니다. 타입이 정해져 있는 상태여야 합니다. 지네릭 메서드 라는 것도 있는데, 이것은 정의 하는 순간 지네릭 클래스의 타입변수와는 따로 노는 별개의 변수이기 때문에, T의 영향을 받지 않습니다. 변수명이 만약 같을지라도 혼동하지 않도록 주의합니다. 이와 같은 이유로 static 메서드는 지네릭 정의가 가능합니다.

동적으로 배열을 생성해야 할 경우, ‘Reflection API’의 newInstance() 를 사용하거나, Object 배열을 만든 후 복사하여 T[] 로 형변환 합니다.

타입 제한

class FruitBox<T extends Fruit> { … } 와 같이 클래스가 정의돼 있는 경우, 객체 생성 시 Fruit 의 자손으로만 타입 지정 할 수 있습니다. 인터페이스 구현을 조건으로 건다 하더라도 implements 가 아닌 extends 키워드를 사용합니다. 만약 여러개를 상속&구현 하는 것을 조건으로 추가하려면 class FruitBox<T extends Fruit & Eatable> 과 같이, & 기호를 사용하면 됩니다.

타입 생략

Box<Apple> appleBox = new Box<>(); 와 같이 왼쪽에서 참조변수의 지네릭변수에 들어갈 타입이 정해진 상태에서 생성자의 지네릭 변수에 들어갈 타입이 생략된다면, 참조변수의 것으로 추정됩니다.

Box<?> = new Box <>(); 와 같이 한다면, Box<? extends Object> = new Box<>();로 변환될 것이며, 오른쪽은 <Object> 로 추정될 것입니다.

만약 클래스에서 <T extends Fruit> 로 타입변수가 정의되어 있을 경우, <?>는 <? extends Fruit>가 될 것이며, 오른쪽은<Fruit> 로 추정될 것입니다.

타입 변환

아래와 같이 타입이 같으면서 지네릭클래스가 상속관계에 있다면 당연히 다형성이 적용될 것입니다. 하지만, 지네릭 클래스가 같으면서 타입이 다르다면 아무리 타입간에 상속관계가 있더라도 타입변환이 되지 않는것이 원칙입니다. (이럴때는 와일드카드를 이용하는 것이 좋습니다)

Box<Fruit> fruitBox = new Box<Apple>(); (X)
Box<? extends Fruit> fruitBox = new Box<Apple>(); (O)
Box<Apple> appleBox = new FruitBox<Apple>(); (O)

와일드카드

변수 선언, 매개변수 할당(엄밀히 말해서 이것도 변수 선언) 등 다양한 지네릭 타입에 유연하게 대응하기 위해, 참조변수에 ? 와 같은 기호로 와일드카드를 설정할 수 있습니다(?, ? extends Fruit, ? super Fruit 같은 형식으로 사용 가능). 참조변수 이외에 클래스 지네릭 변수(이미 변수가 있기에 당연), 생성자(타입을 확정해야 하기에)에는 사용할 수 없습니다.

Optional<? extends Object> EMPTY = new Optional<String>(); 과 같이, 여러 Optional 클래스를 받을 수 있습니다. 실제 사용할 때는 (Optional<String>)EMPTY 로 형변환하여 사용해야 합니다. 기본적으로 지네릭클래스는 타입변수에 들어갈 타입이 다른 형으로의 형변환이 금지돼 있지만, 와일드카드를 사용할 때는 그 타입이 상속관계에 있다면 얼마든지 형변환이 가능합니다.

Optional<?> EMPTY = new Optional<>(); 의 경우 <?>는 <? extends Object>가 생략된 형태입니다. 따라서 생성자는 new Optional<Object> 로 추정이 가능합니다.

--

--