본문 바로가기
won2dev-log
HomeArchiveTagsCategoriesAboutProjects
HomeArchiveTagsCategoriesAboutProjects
won2dev-logwon2dev-logwon2dev-log

비전공 개발자의 로그 | won2dev-log

Navigation
  • Home
  • Archive
  • About
  • Projects
Categories
  • Docs
  • TIL
  • Automation
  • Git · GitHub
  • Project
Tags
  • TIL
  • Java
  • Spring
  • Backend
  • n8n
더보기
About

기록을 거름 삼아 공유는 성장을 만든다.

LicensePrivacy
© won2dev 2026. All rights reserved.
Home›Docs›TIL - Java 기초: 제네릭(Generic) 이해와 활용
Docs

TIL - Java 기초: 제네릭(Generic) 이해와 활용

won2dev·2025년 03월 19일
#Java#TIL
TIL - Java 기초: 제네릭(Generic) 이해와 활용

💬 배운 기술 / 지식

  • 제네릭(Generic) 개념과 필요성
  • 타입 안정성 확보
  • 제네릭 클래스와 메서드 선언법
  • 와일드카드(?)와 제한된 타입 파라미터
  • 타입 소거(Type Erasure)의 개념과 영향
  • 장점과 주의사항

🎯 제네릭이란?

  • 클래스나 메서드를 선언할 때, 사용할 데이터 타입을 미리 지정하지 않고 나중에 지정할 수 있게 하는 기능
  • 코드 재사용성을 높이고, 컴파일 시 타입 오류를 잡아 안정성 향상
  • 예전에는 객체를 Object로 받고, 캐스팅으로 변환했지만 런타임 오류 위험과 코드가 복잡해짐
  • 제네릭 도입 후, 타입을 명확히 선언해 타입 체크를 컴파일 시 수행

🛠 제네릭 클래스 기본 구조

java
public class Box<T> {
    private T item;

    public void setItem(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }
}
public class Box<T> {
    private T item;

    public void setItem(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }
}
  • <T>: 타입 파라미터, 어떤 타입이든 들어올 수 있다는 의미
  • 객체 생성 시 구체적인 타입을 지정
java
Box<String> stringBox = new Box<>();
stringBox.setItem("Hello");
String s = stringBox.getItem();  // 캐스팅 불필요
Box<String> stringBox = new Box<>();
stringBox.setItem("Hello");
String s = stringBox.getItem();  // 캐스팅 불필요

🧩 제네릭 메서드 예제

java
public class Util {
    // 제네릭 메서드: 입력과 출력 타입 동일
    public static <T> void printArray(T[] array) {
        for (T item : array) {
            System.out.println(item);
        }
    }
}
public class Util {
    // 제네릭 메서드: 입력과 출력 타입 동일
    public static <T> void printArray(T[] array) {
        for (T item : array) {
            System.out.println(item);
        }
    }
}

사용 예

java
Integer[] intArr = {1, 2, 3};
Util.printArray(intArr);

String[] strArr = {"A", "B", "C"};
Util.printArray(strArr);
Integer[] intArr = {1, 2, 3};
Util.printArray(intArr);

String[] strArr = {"A", "B", "C"};
Util.printArray(strArr);

❓ 와일드카드(?)와 제한된 타입 파라미터

  • ?는 "어떤 타입이든 와일드카드"를 의미
  • 메서드 매개변수에 주로 사용하여 다양한 타입 허용
java
public static void printList(List<?> list) {
    for (Object elem : list) {
        System.out.println(elem);
    }
}
public static void printList(List<?> list) {
    for (Object elem : list) {
        System.out.println(elem);
    }
}
  • 제한된 타입: 특정 타입 혹은 그 하위 타입만 허용
java
public static void addNumbers(List<? super Integer> list) {
    list.add(10);
}
public static void addNumbers(List<? super Integer> list) {
    list.add(10);
}
  • 상한 제한: <? extends Number> → Number 또는 하위 타입만 가능
  • 하한 제한: <? super Integer> → Integer 또는 상위 타입만 가능

⚠️ 타입 소거(Type Erasure)

1. 타입 소거란?

  • 자바 컴파일러가 제네릭 코드를 컴파일할 때,
  • 모든 제네릭 타입 정보를 제거하고 내부적으로 원시 타입(Raw Type, 보통 Object)으로 변환하는 과정
  • 즉, 런타임에는 제네릭 타입 정보가 사라짐

2. 왜 타입 소거가 발생하는가?

  • 자바의 하위 호환성 유지를 위해 도입됨
  • 자바 5 이전 버전에서는 제네릭이 없었기 때문에
  • 기존 라이브러리와 호환 가능하도록 설계됨

3. 타입 소거의 영향

  • 런타임에 제네릭 타입을 알 수 없기 때문에 다음과 같은 제한이 발생함
    • 기본형(int, double 등)은 제네릭 타입으로 사용할 수 없음 (래퍼 클래스 사용)
    • 제네릭 배열 생성 불가 (new T[] 직접 생성 불가)
    • instanceof 연산자에 제네릭 타입 사용 불가 (if (obj instanceof List<String>) X)
    • 타입 안전성을 컴파일 시 보장하지만, 런타임에는 타입 체크를 하지 않음

4. 예시

컴파일 전 코드:

java
List<String> list = new ArrayList<>();
list.add("Hello");
String s = list.get(0);
List<String> list = new ArrayList<>();
list.add("Hello");
String s = list.get(0);

컴파일 후 바이트코드 내부 (의사 코드):

java
List list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0);
List list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0);
  • 타입 파라미터 <String>이 사라지고
  • 반환 시 캐스팅이 자동으로 추가됨

✅ 제네릭 장점

  • 컴파일 시 타입 안전 보장 → 런타임 오류 감소
  • 명확한 코드 작성 → 가독성, 유지보수 용이
  • 코드 재사용성 극대화
  • 기존 캐스팅 코드 간소화

⚠️ 주의사항

  • 기본형(int, double 등)은 제네릭 타입으로 직접 사용할 수 없음 → 래퍼 클래스 사용 (Integer, Double 등)
  • 제네릭 타입 정보는 컴파일 후 제거되어 런타임에 타입 정보를 알 수 없음 (타입 소거)
  • 정적(static) 멤버에서는 타입 파라미터 사용할 수 없음
  • 배열과 함께 쓰기 복잡 → 일반 배열 대신 컬렉션 권장

📝 활용 코드

java
import java.util.ArrayList;
import java.util.List;

public class Box<T> {
    private T item;

    // 아이템 저장
    public void setItem(T item) {
        this.item = item;
    }

    // 아이템 반환
    public T getItem() {
        return item;
    }

    public static void main(String[] args) {
        // String 타입 박스 생성
        Box<String> stringBox = new Box<>();
        stringBox.setItem("안녕하세요");
        System.out.println("stringBox에 담긴 값: " + stringBox.getItem());

        // Integer 타입 박스 생성
        Box<Integer> intBox = new Box<>();
        intBox.setItem(100);
        System.out.println("intBox에 담긴 값: " + intBox.getItem());

        // 제네릭 메서드 활용
        Integer[] numbers = {1, 2, 3};
        printArray(numbers);

        String[] words = {"Java", "Generics", "Easy"};
        printArray(words);
    }

    // 제네릭 메서드: 배열 내용 출력
    public static <T> void printArray(T[] array) {
        for (T item : array) {
            System.out.println(item);
        }
    }
}
import java.util.ArrayList;
import java.util.List;

public class Box<T> {
    private T item;

    // 아이템 저장
    public void setItem(T item) {
        this.item = item;
    }

    // 아이템 반환
    public T getItem() {
        return item;
    }

    public static void main(String[] args) {
        // String 타입 박스 생성
        Box<String> stringBox = new Box<>();
        stringBox.setItem("안녕하세요");
        System.out.println("stringBox에 담긴 값: " + stringBox.getItem());

        // Integer 타입 박스 생성
        Box<Integer> intBox = new Box<>();
        intBox.setItem(100);
        System.out.println("intBox에 담긴 값: " + intBox.getItem());

        // 제네릭 메서드 활용
        Integer[] numbers = {1, 2, 3};
        printArray(numbers);

        String[] words = {"Java", "Generics", "Easy"};
        printArray(words);
    }

    // 제네릭 메서드: 배열 내용 출력
    public static <T> void printArray(T[] array) {
        for (T item : array) {
            System.out.println(item);
        }
    }
}

💡 느낀 점 / 참고 사항

  • 제네릭은 자바에서 타입 안정성과 코드 재사용성의 핵심 도구
  • 타입 소거 개념을 이해해야 제네릭 제약사항의 이유를 알 수 있음
  • 컬렉션과 함께 쓰면 특히 효과적이며, 복잡한 타입 관계를 간단하게 표현 가능
  • 와일드카드 사용법과 타입 제한도 꼭 익혀두면 좋은 설계 역량
공유하기
이전 글TIL - Java 기초: 람다 표현식(Lambda Expression) 시작하기다음 글 TIL - Java 기초: 컬렉션(Collection) 프레임워크 이해하기

목차

  • 💬 배운 기술 / 지식
  • 🎯 제네릭이란?
  • 🛠 제네릭 클래스 기본 구조
  • 🧩 제네릭 메서드 예제
  • ❓ 와일드카드(?)와 제한된 타입 파라미터
  • ⚠️ 타입 소거(Type Erasure)
  • 1. 타입 소거란?
  • 2. 왜 타입 소거가 발생하는가?
  • 3. 타입 소거의 영향
  • 4. 예시
  • ✅ 제네릭 장점
  • ⚠️ 주의사항
  • 📝 활용 코드
  • 💡 느낀 점 / 참고 사항

카테고리

Docs

태그

#Java#TIL

최근 글

Git 요약 (1) - Rebase, Stash, Squash MergeTIL - MSA 핵심 요소 정리TIL - (4) Spring 어노테이션 정리: Mockito를 활용한 단위 테스트TIL - (3) Spring 어노테이션 정리: Lombok Getter, Setter와 생성자TIL - (2) Spring MVC와 WebFlux의 차이: 블로킹과 논블로킹