Notice
Recent Posts
Recent Comments
Link
«   2026/03   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Tags more
Archives
Today
Total
관리 메뉴

프로그래밍 공부

[자바] #07. 제네릭 (Generics) 본문

자바

[자바] #07. 제네릭 (Generics)

하 냥 2025. 2. 15. 12:45

📢 제네릭이란 ?

데이터 타입을 매개변수화 할 수 있는 기능.

클래스 내부에서 사용할 데이터 타입을 미리 지정하지 않고, 인스턴스를 생성할 때 타입을 정할 수 있다.

형 변환(다운캐스팅)이 필요 없고, 컴파일 시점에 타입을 검사하여 안정성이 높아진다.

 

👀 제네릭 클래스 예제

class Apple {
	public String toString() { return "I am apple."; }
}

class Grape {
	public String toString() { return "I am grape."; }
}

class Box<T> { // 제네릭으로 클래스 만들기
	private T o;
    
	public void set(T o) { this.o = o; }
}

public class Main {
    public static void main(String[] args) {
    	Box<Apple> appleBox = new Box<>(); // Apple이나 Apple을 상속하는 클래스의 인스턴스 저장 가능
        Box<Grape> grapeBox = new Box<>(); // Grape나 Grape를 상속하는 클래스의 인스턴스 저장 가능
    }
}

🔔 용어 정리

  • 타입 매개변수 (Type Parameter) : Box<T>에서의 T
  • 타입 인자 (Type Argument) : Box<Apple>에서의 Apple
  • 매개변수화 타입 (Parameterized Type) : Box<Apple> 이라는 새로운 자료형

 

👀 제네릭 메서드 예제

제네릭은 클래스뿐만 아니라 메서드에도 적용 가능하다.

class Util {
    public static <T> void printArray(T[] array) {  // 제네릭 메서드
        for (T element : array) {
            System.out.print(element + " ");
        }
        System.out.println();
    }
}

public class Main {
    public static void main(String[] args) {
        Integer[] intArray = {1, 2, 3, 4, 5};
        String[] strArray = {"A", "B", "C"};

        Util.printArray(intArray); // 1 2 3 4 5
        Util.printArray(strArray); // A B C
    }
}

 

 

메서드에 <T>를 선언하여, 호출 시점에 타입을 지정할 수 있다.

 


🚨 제네릭의 제한 사항

1. 기본형(Primitive Type) 사용 불가

Box<int> intBox = new Box<int>();  // ❌

제네릭은 객체 타입(Reference Type)만 사용 가능하다.

기본형을 사용하려면 Wrapper 클래스(Integer, Double 등)를 사용해야.

 

2. 타입 매개변수로 static 변수 사용 불가

class Test<T> {
    // static T value;  // ❌
}

T는 인스턴스 생성 시에 정해지기 때문에, 클래스 레벨의 static 변수에 사용할 수 없다.

 

3. 타입 매개변수로 객체 생성 불가

class Box<T> {
    private T value;

    public Box() {
        // value = new T();  // ❌
    }
}

제네릭에서 T는 컴파일 시점에 실제 타입이 결정되는 것이 아니라, 런타임 시점에 특정 타입으로 치환된다.

하지만 자바에서는 new 키워드를 사용할 때, 반드시 컴파일 시점에 타입이 명확해야 한다.

 

  • T가 어떤 타입인지 컴파일 타임에 알 수 없음.
  • T가 기본형(int, double)인지, 객체(String, Integer)인지 알 수 없음.
  • 생성자 호출 방식(T가, 매개변수가 없는 기본 생성자가 있는지) 또한 모름.

 

📢 '매개변수화 타입'도 타입 인자로 전달 가능

상자를 하나 생성해서 그 안에 문자열을 저장한 후 이 상자를 다른 상자에 넣고,

이 상자를 한 번 더 다른 상자에 넣을 수도 있다. 즉 문자열을 세 개의 상자로 겹겹이 두르는 것이다.

 

class Box<T> {
    private T o;
    public void set(T o) { this.o = o; }
    public T get() { return o; }
}

class BoxInBox {
	public static void main(String[] args) {
    	Box<String> sBox = new Box<>();
        sBox.set("I am happy");
        
        Box<Box<String>> wBox = new Box<>();
        wBox.set(sBox);
        
        Box<Box<Box<String>>> zBox = new Box<>();
        zBox.set(wBox));
        
        System.out.println(zBox.get().get().get()); // I am happy 출력
    }
}​

 

 

📢 제네릭 클래스의 타입 인자를 제한할 수도

class Box<T extends Eatable>

이는 인스턴스 생성 시 T 타입 인자를 받을 때

Eatable이 인터페이스라면 T는 반드시 그 인터페이스를 구현해야 하고,

Eatable이 클래스라면 T는 그 클래스를 상속해야 한다.

 

이와 마찬가지로 제네릭 메서드 또한 호출 시 타입 인자를 제한할 수 있다.

public static <T extends Eatable> Box<T> makeBox(T o)

이는 Box<T>형을 반환하는 메서드이다.

반환형 앞의 꺽쇠 <T extends Eatable>은 T가 타입 매개변수이고, 이 T는 Eatable을 구현 또는 상속해야 한다는 의미이다.