본문 바로가기
JAVA

[JAVA] 제네릭이란? (정의, 타입 파라미터, 사용하기)

by 정공자씨 2024. 4. 25.

 

 

 

제네릭(Generics)

데이터의 타입(data type)을 일반화한다(generalize)

개념

클래스나 메서드에서 사용할 내부 데이터의 타입을 미리 정하는 것

  • 내부 데이터 타입을 컴파일 시에 미리 지정하여,
  • 잘못된 타입이 사용될 수 있는 문제를 컴파일 과정에서 제거가 가능해짐

 

장점

  • 컴파일 시에 미리 타입 검사(type check)를 수행하기 때문에, 실행 중에 일어나는 에러 방지
  • 제네릭 사용 시에, 불필요한 타입 변환을 하지 않아도 됨

 

컬랙션(Collection) 클래스에서 불필요한 타입 변환 발생

- 기존의 컬렉션 클래스에는 모든 타입의 객체(Object형태로 저장)를 저장할 수 있음
- 따라서, 다른 타입의 객체들이 저장될 수 있음
- 반대로 컬렉션에 저장된 객체를 읽어올 때에 타입 변환이 필요함
- 즉, 원래의 클래스 타입으로 다운캐스팅 해줘야 하는데, 다운캐스팅이 잘못 되더라도 컴파일 시점에서는 알 수 없고, 실행된 후에야 알 수 있음

 

 

용어 정리하고 가기

1. Type Parameter (타입 파라미터)
- generic 타입을 명시하기 위한 곳
- 정의된 타입 파라미터가 내부적으로 실제 타입으로 변환
- 예제 : T

2. Type Argument (타입 인수)
- 제네릭 클래스 생성 시에, 사용할 타입을 작성하는 곳
- 예제 : Integer

3. Parameterized Type 
- Type Argument에 의해서 Type Parameter(T)가 치환된 전체 데이터 타입
- 예제 : Age<Integer>

 

 

 

 

 

제네릭의 선언과 생성

클래스, 인터페이스메서드에서 다음과 같은 방법으로 제네릭을 선언할 수 있음

 

1. 제네릭 선언하기

class GenericClass <T> {  // T: 타입 파라미터

    T element;

    void setElement(T element) { 
        this.element = element; 
    }

    T getElement() { 
        return element; 
    }
}

 

2. 제네릭 생성하기

클래스명<타입> 참조변수명 = new 클래스명<타입>();

클래스명<타입> 참조변수명 = new 클래스명<>();  // 생성자 다음의 타입은 생략 가능
  • 제네릭 클래스를 생성할 때에 사용할 타입을 명시하면, 정의된 타입 파라미터가 내부적으로 실제 타입으로 변환되어 처리가 됨
  • 클래스 내부에서 사용될 데이터의 타입을 외부에서 지정해 주는 것
  • 타입이 매개변수화 된 것

[ 예제 ]

ArrayList<Integer> list = new ArrayList<>();
  • 클래스를 생성할 때 사용할 타입(Integer)을 명시하면, 내부적으로는 정의된 타입 타라미터가 실제 타입으로 변환이 되어
  • 해당 ArrayList 클래스 내에서 사용하는 데이터는 Ingeter 타입만 저장할 수 있음

 

✔  <T> 자리에는 참조형 타입만 사용할 수 있음
- 타입 파라미터 자리에 사용할 타입을 작성할 때, 기본형 타입은 사용할 수 없음
- 기본형 타입은 래퍼(Wrapper) 클래스를 사용해야 함

 

 

 

 

 

제네릭 타입 파라미터

정의

  • 클래스나 메서드를 설계할 때에 제네릭 타입 파라미터를 사용

 

제네릭 타입 파라미터에 사용 가능한 타입

  • 제네릭 타입 파라미터로 사용할 수 있는 타입은 참조(reference) 타입
  • 기본형 타입(primitive type)은 제네릭의 타입 파라미터로 사용할 수 없음
    • 따라서, 기본형 타입을 사용하고 싶다면, 기본 Wrapper 클래스를 사용해야 함(Integer형, Double형)
 // 기본 타입은 제네릭의 타입 파라미터에 사용할 수 없음
List<int> aList = new ArrayList<>();   -- (X)

// Wrapper 클래스 사용
List<Integer> aList2 = new ArrayList<>();    -- (O)

 

예제

// 제네릭을 이용하여 클래스 설계
class Box<T> {
    List<T> fruits = new ArrayList<>();

    public List<T> getFruits() {
        return fruits;
    }

    public void add(T fruit) {
        fruits.add(fruit);
    }
}

// 클래스
class Apple { }
public class Main {
    public static void main(String[] args) {
    
        // 1. 제네릭 타입 매개변수에 integer를 할당
        Box<Integer> box = new Box<>();
        box.add(10);
        box.add(20);
        
        for (Object obj : box.getFruits()) {
            System.out.println(obj);
        }

        // 2. 제네릭 타입 매개변수에 String 할당
        Box<String> box2 = new Box<>();
        box2.add("String");
        box2.add("타입만 저장");

        for (Object obj : box2.getFruits()) {
            System.out.println(obj);
        }

        // 3. 제네릭 타입 매개변수에 class도 할당 가능
        Box<Apple> box3 = new Box<>();
        box3.add(new Apple());
        box3.add(new Apple());

        for (Object obj : box3.getFruits()) {
            System.out.println(obj);
        }
    }
}

 

 

 

 

 

 

제네릭을 사용하는 이유

1. 컴파일 시점에 타입 검사를 하여 에러 방지

  • 여러 타입을 다루기 위해서 return 타입(리턴 타입)이나 파라미터(매개변수)에 Object 타입을 사용함
  • Object으로 타입을 선언할 경우, 반환된 Object 객체는 다시 원래의 타입으로 형 변환 해야 하는데
  • 컴파일 시에는 형변환 오류가 확인 되지 않고, 런타임(실행 시)에 형 변환의 오류가 발견되어 에러가 발생함

 

[ 예제 ]

1-1. 제네릭을 사용하지 않았을 때

[실행 결과]

  • Cat 타입의 배열(Catarr)에 Cat객체를 생성하여 저장했고
  • 배열의 요소를 가지고 오는 메서드 home.getAnimalHome(1)를 호출하여, Cat객체 타입의 값을 가지고 왔고
  • Cat 객체 타입의 값을 Dog 타입으로 변경하려는 코드를 작성하여, 형 변환 오류가 있음에도 불구하고
    • 컴파일 시에는 에러가 확인되지 않음
  • 런타임 시(실행 시)에 ClassCastExcepton (형 변환 오류) 에러를 확인할 수 있음

 

 

1-2. 제네릭을 사용했을 때

  • <T>는 매개변수로 전달 되는 객체(참조 타입)가, 곧 클래스 타입이 된다는 것

  • 클래스를 정의할 때, 제네릭 타입 파라미터를 <Cat>으로 지정하여, 해당 클래스에 저장될 수 있는 데이터 타입을 Cat타입으로 정함
  • 따라서, 해당 배열은 Cat 객체 저장이 가능하므로 Cat만 저장되어 있음
    • 따라서, 해당 데이터 값을 Dog 타입으로 형 변환 하려고 하면, 컴파일 에러가 발생
  • 잘못된 타입이 사용될 경우에, 컴파일 과정에서 문제를 확인하여 제거할 수 있음

 

 

2. 불필요한 형 변환(캐스팅)을 없앰

  • Object 배열에 하위 타입의 데이터를 저장할 수 있음
    • Object 클래스는 가장 상위 클래스이므로 
    • 하위 클래스는 Object 클래스로 업캐스팅이 가능
  • Object 타입의 요소를 가지고 올 때, 반드시 다운 캐스팅을 하여 원래 가지고 있는 타입으로 변환시켜 줘야 함
  • 이는 곧 추가적인 오버헤드가 발생하는 것

 

[ 예제 ]

2-1. Object 타입의 요소를 가지고 올 때, 다운캐스팅 하지 않으면 컴파일 오류 발생

  • 배열(catArr)에 Cat 객체가 저장되어 있고
  • AnimalHome 클래스 인스턴스를 생성하며, 배열(catArr)를 넣어서 값 초기화를 함
  • AnimalHome 생성자 메서드가 실행되면서
    • this.animal = catArr
    • Object [ ] animal = catArr로 멤버변수의 값이 초기화 됨
    • animal 배열은 Object 타입이고, animal 참조변수에 catArr 객체의 주소값이 저장됨
  • 따라서, home.getAnimalHome(0)의 리턴 값은 Object 타입이고
    • Object 타입을 Cat 타입에 넣으려고 해서 컴파일 오류가 발생함

 

 

2-2. 제네릭은 미리 타입을 지정해놓기 때문에, 형 변환(캐스팅)을 하지 않아도 됨

  • T는 매개변수로 전달되는 객체가 곧 클래스 타입이 됨
    • (예) class AnimalHome<Cat>
    • Cat 객체가 클래스의 타입이 됨
    • Cat 객체만 저장이 됨

  • 배열(catArr)에 Cat 객체가 저장되어 있고
  • AnimalHome 클래스 타입 파라미터 < >에 Cat을 할당하고, 클래스의 인스턴스를 생성하기 위해 배열(catArr)를 넣어서 값 초기화를 함
    • AnimalHome<Cat> home = new AnimalHome<>(catArr)
  • class AnimalHome<T>의 T가 Cat이 되고 
    • class AnimalHome<Cat> { }
    • Cat 객체가 AnimalHome 클래스의 타입이 됨
    • 즉, 해당 클래스에 Cat 객체만 저장이 됨
  • AnimalHome 생성자 메서드가 실행되면서
    • 생성자 AnimalHome(T [ ] animal )의 T가 Cat이 되고
    • this.animal = catArr로 멤버변수(animal)의 값이 초기화 됨
      • Cat [ ] animal = catArr
    • animal 배열은 Cat 타입이고, 참조변수(animal )에 catArr 객체의 주소값이 저장됨
  • 따라서, home.getAnimalHome(0)의 리턴 값은 Cat타입
    • public Cat getAnimalHome(int index)의 리턴 값은 Cat타입이고 
    • Cat 타입의 데이터를 Cat 타입 참조변수에 넣기 때문에, 형 변환이 필요 없음

 

 

클래스에 제네릭을 사용한다고 해서 클래스가 여러개 만들어지는 것이 아님

클래스 선언문 옆에 제네릭 타입 파라미터를 주어 제네릭을 선언하고 생성한다고 해서 클래스가 여러 개 만들어지는 게 아니라, 해당 클래스에서 사용하는 데이터 타입이 정해지는 것
class Box<T> {
	T value;
}

public class Test {
    public static void main(String[] args) {
        Box<String> box1 = new Box<String>();
        Box<String> box2 = new Box<>();
    }
}

 

// 제네릭 타입 매개변수를 사용하여 객체를 생성했다고 해서, 새로운 클래스가 생성되는 것이 아님
class Box <String> {  //(x)
	String Value;
} 

class Box <String> {  //(x)
	String Value;
}

 

 

 

 

 

 

 

 

 

 

출처

 

☕ 자바 제네릭(Generics) 개념 & 문법 정복하기

제네릭 (Generics) 이란 자바에서 제네릭(Generics)은 클래스 내부에서 사용할 데이터 타입을 외부에서 지정하는 기법을 의미한다. 객체별로 다른 타입의 자료가 저장될 수 있도록 한다. 자바에서 배

inpa.tistory.com

 

코딩교육 티씨피스쿨

4차산업혁명, 코딩교육, 소프트웨어교육, 코딩기초, SW코딩, 기초코딩부터 자바 파이썬 등

tcpschool.com