제네릭

황희윤·2023년 11월 19일

제네릭 (Generic)

다양한 타입의 객체들을 다루는 클래스나 메소드에 컴파일 시 타입 체크를 해주는 기능

  • 일반적으로 유지 보수가 발생하는 경우를 살펴보면 자료형(type)의 수정이 주요 원인이다.

  • 제네릭(Generic)은 클래스를 정의하면서 사용할 변수의 자료형을 설계할(코드 작성 시) 때 검사하는게 아니라, 컴파일할 때 자료형을 검사해서 자료형에 유연하면서도 안정성까지 고려할 수 있도록 제공하는 기능이다.

  • JDK 1.5 이후부터 지원한다.


장점

  1. 자료형에 유연한 구조를 만들 수 있다.

  2. 컴파일할 때 미리 자료형을 강하게 검사해서 오류를 방지한다.

    • 실행 시 자료형 불일치 오류 발생을 사전에 방지 한다.
    • 제네릭을 사용하지 않으면 컴파일 단계에서 타입 체크를 하지 못해서 런타임에 에러가 난다.
  3. 형변환 시 발생할 수 있는 속도 저하를 방지한다.


자료형 표기

표기의미
EElement
KKey
NNumber
TType (미결정 상태)
VValue
S, U, V2, 3, 4번째 타입

T 자료형

T는 미결정 상태이기 때문에 어떤 클래스형도 가능하고 자료형에 대한 정보를 컴파일할 때 최종적으로 결정한다.

제네릭을 사용하지 않은 경우(Object 클래스형 사용)

class Example {
	// Object 클래스형은 어떠한 클래스형이라도 모두 할당할 수 있다. 
	private Object data = null;
    
    public void setData(Object data) {
    	this.data = data;
    }
    
    public Object getData() {
    	return this.data = data;
    }
}

public class Main {
	public static void main(String[] args) {
    	String str = "제네릭을 사용하지 않은 경우";
        Example ex = new Example();
        ex.setData(str); 
        // setData() 메서드의 매개 변수로 String이 지정되지만,
        // 내부에서 Object 클래스형으로 업캐스팅되기 때문에 본래의 클래스형을 잃어버린다. 
        // 따라서 getData() 메서드가 반환하는 클래스형은 Object가 된다.
        String str2 = (String)ex.getData(); // String으로 캐스팅해서 사용해야 한다.
    }
}

제네릭을 사용한 경우 (T 자료형 사용)

class Example<T> {
	private T data = null;
    
    public void setData(T data) {
    	this.data = data;
    }
    
    public T getData() {
    	return this.data = data;
    }
}

public class Main {
	public static void main(String[] args) {
    	String str = "제네릭을 사용한 경우";
        Example<String> ex = new Example<String>();
        ex.setData(str); 
        String str2 = ex.getData(); 
    }
}

배열 VS 제네릭

// 배열
Object[] objectArray = new Integer[1]; // 성공!

// 제네릭
List<Object> objectArray = new ArrayList<Integer>(); // 컴파일 에러

제한된 제너릭 타입

FoodCategory 클래스에는 음식 종류 타입의 객체만 생성하고 싶으면 T extends Food 코드를 추가해서 타입을 제한하면 된다.

class FoodCategory<T extends Food> {
	private T t;
    
    public void set(T t) {
    	this.t = t;
    }
    
    public void get(T t) {
    	return t;
    }
}

FoodCategory<Food> foodCategory = new FoodCategory<>(); // 성공!

FoodCategory<Pizza> pizzaCategory = new FoodCategory<>(); // 성공!

FoodCategory<Car> carCategory = new FoodCategory<>(); // 컴파일 에러

제네릭과 다형성

제네릭은 자료형을 미지정하는 것이지만, 제네릭을 사용하면 배열처럼 자료형에 대한 안정성을 확보하는 장점을 가질 수 있다.


와일드카드(WildCard)

물음표(?)는 와일드카드로 불리며, 알 수 없는 타입을 뜻한다.

상위 제한 와일드카드(Upper Bounded Wildcards)

변수의 제한을 완화하기 위해 상위 제한 와일드카드를 사용할 수 있다.

자신과 자신의 하위 타입 사용 가능

// List<Integer>, List<Double>, List<Number>를 인수로 받을 수 있는 메서드
public static void process(List<? extends Number> list) {
	... 
}

무제한 와일드카드(Unbounded Wildcards)

List<?>
  • 물음표만으로 정의되어서 모든 타입을 인수로 받을 수 있다.

  • Object 클래스에서 제공되는 기능만을 사용할 때 사용

  • 제너릭 클래스의 메서드 중에 List.size(), List.clear()처럼 매개 변수의 자료형에 의존하지 않는 메서드만을 사용할 때 사용

하위 경계 와일드카드(Lower Bounded Wildcards)

하위 경계 와일드카드는 <? super A>처럼 물음표super 키워드로 정의한다.

상위 경계 와일드카드와는 반대로 지정된 타입과 그 상위 타입만을 허용

예를 들어 List<? super Integer>로 정의하면 Integer의 상위인 Number와 Object가 사용 가능하다.

profile
HeeYun's programming study

0개의 댓글