자바[JAVA] 제네릭(Generic)이란?

Programmingzi·2024년 11월 18일

제네릭(Generic)이란?

하나의 값이 여러 타입을 가질 수 있도록 하는 방법을 의미한다. 메서드나 *컬렉션 클래스에 컴파일 시의 타입 체크를 해주기 때문에, 객체의 *타입 안정성을 높이고 형변환의 번거로움이 줄어든다.

컬렉션 클래스?
데이터를 효율적으로 저장하고 관리할 수 있도록 제공되는 자료구조 클래스를 의미한다.
대표적으로 자바에서 List, Set, Queue 등이 존재한다.

타입 안정성을 높인다?

  • 의도하지 않은 타입의 객체가 저장되는 것을 막을 수 있다.
    저장된 객체를 꺼내올 때 원래의 타입과 다른 타입으로 잘못 형변환되어 발생할 수 있는 오류를 줄여준다.

제네릭 타입은 클래스와 메서드에서 선언할 수 있다.

제네릭 클래스의 선언

class Test {
	Object item;
    
    void setItem(Object item) { this.item = item; }
    Object getItem() { return item; }
}

위 클래스를 제네릭 클래스로 바꿔보자.

class Test<T> {
	T item;
    void setItem(T item) { this.item = item; }
    T getItem() { return item; }
}

Test<T>에서 T를 타입 변수라고 하며, 타입 변수는 T가 아닌 다른 것을 사용해도 된다

제네릭 클래스의 사용

제네릭 클래스를 사용하여 객체를 생성할 때는 참조변수와 생성자에 타입 T 대신 사용할 실제 타입을 지정해주어야 한다.

Test<String> t = new Test<String>();
t.setItem(new Object());			// error! String이외의 타입 지정 불가
t.setItem("item1");					// OK
//String item = (String) t.getItem();	// 형변환이 필요 없음
String item = t.getItem();

와일드 카드

매개변수에 과일을 대입하면 잼을 만들어서 반환하는 Maker라는 클래스가 있고, 이 클래스는 과일을 잼으로 만들어서 반환하는 makeJam()이라는 static 메서드가 다음과 같이 정의되어 있다고 하자.

class Maker {
	static Jam makeJam(FruitBox<Fruit> box) {
    	String tmp = "";
        for(Fruit f : box.getList()) tmp += f + " ";
        
        return new Jam(tmp);
    }
}

static 메서드에는 타입 매개변수 T를 매개변수에 사용할 수 없다.

  • T는 인스턴스변수로 간주되고, static 멤버는 인스턴스변수를 참조할 수 없기 때문!

해당 코드를 통해서는 Fruit 타입의 클래스만 makeJam 메서드를 사용할 수 있다. 따라서, 여러 가지 타입의 매개변수를 갖는 makeJam()을 만들 수밖에 없다.

static Jam makeJam(FruitBox<Fruit> box) {
	String tmp = "";
	for(Fruit f : box.getList()) tmp += f + " ";

	return new Jam(tmp);
}

static Jam makeJam(FruitBox<Apple> box) {
	String tmp = "";
	for(Fruit f : box.getList()) tmp += f + " ";

	return new Jam(tmp);
}

그러나 위 코드에서는 컴파일 에러가 발생한다.

제네릭 타입이 다른 것만으로는 오버로딩이 성립하지 않기 때문!

이럴 때 사용하기 위해 고안된 것이 바로 '와일드 카드' 이다. 와일드 카드는 ?로 표현하며, 어떠한 타입도 될 수 있다.

makeJam을 와일드 카드를 사용하여 여러 개의 타입을 매개변수로 받을 수 있다.

static Jam makeJam(FruitBox<?> box extends Fruit) {
	String tmp = "";
	for(Fruit f : box.getList()) tmp += f + " ";

	return new Jam(tmp);
}

와일드 카드를 통해 Friut 타입과 Fruit의 자손들을 매개변수로 사용가능하다.

  • <? extends T>: T와 그 자손들만 가능
  • <? super T> : T와 그 조상들만 가능
  • <?>: 모든 타입이 가능 (<? extends Object>와 동일)

제네릭 메서드의 선언

메서드의 선언부에 제네릭 타입이 선언된 메서드를 제네릭 메서드라 한다.

static <T> void osrt(List<T> list, Comparator<? super T> c)

static 멤버에는 타입 매개변수를 사용할 수 없지만, 이처럼 메서드에 제네릭 타입을 선언하고 사용하는 것은 가능하다.

  • 메서드에 선언된 제네릭 타입은 지역 변수를 선언한 것과 같다

앞서 봤던 makeJam()을 제네릭 메서드로 바꾸면 다음과 같다.

static <T extends Fruit> Jam makeJam(Fruit<T> box) {
	String tmp = "";
	for(Fruit f : box.getList()) tmp += f + " ";

	return new Jam(tmp);
}

참고
남궁 성. 『Java의 정석』. 도우출판, 2016
https://st-lab.tistory.com/153

0개의 댓글