본문 바로가기

Java

[Java] 제네릭

1.1  왜 제네릭을 사용해야 하는가?

  • 컴파일 시 강한 타입 체크를 할 수 있다.
    • 실행 시 타입 에러가 나는 것보다는 컴파일 시에 미리 타입을 강하게 체크해서 에러를 사전에 방지하는 것이 좋다.
  • 타입 변환(casting)을 제거한다.
    • 비제네릭 코드는 불필요한 타입 변환을 하기 때문에 프로그램 성능에 악영향을 미친다.

 

 

1.2 제네릭 타입을 살펴보자

제네릭 타입은 타입을 파라미터로 가지는 클래스를 말한다. 타입을 파라미터로 가진다는 말이 무슨말일까? 

다음 예시를 보자.

 

다음과 같이 필드 타입을 Object로 가지고 있는 Main 메소드가 있다.

class Box {
    private Object object;
    public void set(Object object) { this.object = object; }
    public Object get() {return object;}
}

 

 

만일 필드에 저장된 원래 타입의 객체를 얻으려면 다음과 같이 강제 타입 변환을 해야 한다.

public class Main {
    public static void main(String[] args) {
        Box box = new Box();
        box.set("Hello");   // String 타입을 Object 타입으로 자동 타입 변환해서 저장
        String str = (String) box.get(); // Object 타입을 String 타입으로 강제 타입 변환해서 얻음
    }
}

 

 

이와 같이 Object 타입을 사용하면 모든 종류의 자바 객체를 저장할 수 있다는 장점은 있지만, 저장할 때 타입 변환이 발생하고, 읽어올 때에도 타입 변환이 발생한다. 이러한 타입 변환이 빈번해지면 전체 프로그램 성능에 좋지 못한 결과를 가져올 수 있다.

 

 

타입 변환이 발생하지 않도록 하는 방법은 제네릭을 이용하는 것이다.

다음은 제네릭을 이용해서 Box 클래스를 수정한 것이다.

타입 파라미터 T를 사용해서 Object 타입을 모두 T로 대체했다. T는 Box 클래스로 객체를 생성할 때 구체적인 타입으로 변경된다.

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

 

 

다음과 같이 객체를 생성한다면 타입 파라미터 T는 String 타입으로 변경되어 Box 클래스의 내부는 다음과 같이 자동으로 재구성 된다.

Box<String> box = new Box<String>();
class Box<String> {
    private String t;
    public String get() {return t;}
    public void set(String t) {this.t = t;}
}

 

 

그래서 다음 코드를 보면 저장할 때와 읽어올 때 전혀 타입 변환이 발생하지 않는다.

Box<String> box = new Box<String>();
box.set("Hello"); // 자동 Boxing
String str = box.get(); // 자동 UnBoxing

 

 

이와 같이 제네릭은 클래스를 설계할 때 구체적인 타입을 명시하지 않고, 타입 파라미터로 대체했다가 실제 클래스가 사용될 때 구체적인 타입을 지정함으로써 타입 변환을 최소화 시킨다.

 

예를 들어 ArrayList 객체도 다음과 같이 타입 파라미터를 사용한 걸 볼 수 있다.