Java 입문 | 제네릭 (Generics)


Generics란?

제네릭스(Generics)는 Java5에서 도입된 것으로, 데이터형을 **일반화 한다(Generalize)**라는 의미이다.
제네릭스는 클래스 또는 메소드에서 사용하는 데이터형을 미리 지정하는 방법이다.
제네릭스를 사용하면 데이터 형식 불일치 문제를 컴파일 과정에서 예방할 수 있다.
그리고 제네릭스를 사용하면 형 캐스트를 할 필요가 없어져 프로그램의 성능이 향상된다.

BadGenericsSample.java

package com.devkuma.basic.generics;

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

public class BadGenericsSample {
    public static void main(String[] args) {
        List arrayList = new ArrayList();
        arrayList.add("test");

        String test = arrayList.get(0); // error
    }
}

위의 예제과 같이 제네릭으로 데이터 유형을 지정하지 않고, 데이터를 저장하게 되면 저장할 데이터는 Object 형식으로 저장된다. 그래서 가져올 시에는 형변환를 반듯시 해야 한다.

GoodGenericsSample.java

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

public class GoodGenericsSample {
    public static void main(String[] args) {
        List<String> arrayList = new ArrayList();
        arrayList.add("test");

        String test = arrayList.get(0); // ok
    }
}

제네릭으로 데이터형을 미리 지정하면 저장되는 데이터가 지정된 데이터로 저장되므로, 데이터를 가져올 시에는 형변환을 하지 않아도 된다.

제네릭스를 쓰는 법

제네릭스은 아래와 같이 선언한다.

public class 클래스명<T> 
public interface 인터페이스명<E>

<>괄호 안에는 정해진 규칙은 없지만, 일반적으로 대문자와 알파벳으로 표현한다.

클래스에 제네릭 사용

아래는 클래스에 제레릭을 사용한 예제이다.

package com.devkuma.basic.generics.ex2;

public class GenericsSampleClass<T> {

    private T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
}

위와 같이 작성한 클래스는 인스턴스를 생성해 사용할 때, 데이터 형식을 지정하여 사용한다.

package com.devkuma.basic.generics.ex2;

public class GenericsSample {

    public static void main(String[] args) {
        GenericsSampleClass<String> classSample = new GenericsSampleClass();
        classSample.setT("SetItem");
        String getItem = classSample.getT();
    }
}

인터페이스에 제네릭 사용

아래는 인터페이스에 제레릭을 사용한 예제이다.

package com.devkuma.basic.generics.ex3;

public interface GenericInterfaceSample<T> {
    T testMethod(T t);
}

위와 같이 작성된 인터페이스는 구현 클래스에서 데이터형을 지정할 수 있다.

package com.devkuma.basic.generics.ex3;

public class GenericInterfaceSampleImpl implements GenericInterfaceSample<String> {

    @Override
    public String testMethod(String t) {
        return null;
    }
}

제네릭스의 범위 지정

제네릭으로 지정하는 데이터형의 범위도 상속을 이용하면 제한할 수 있다.

package com.devkuma.basic.generics.ex4;

public class GenericClassSample<T extends Number>{
    private T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
}

위와 같이 작성되고 있는 클래스는 제네릭스로 데이터형을 지정할 때, Number 클래스를 상속하는 클래스만 지정할 수 있다.

package com.devkuma.basic.generics.ex4;

public class GenericSample {
    public static void main(String[] args) {
        GenericClassSample<String> classSample = new GenericClassSample(); // error
        classSample.setT("SetItem");
        String getItem = classSample.getT();
    }
}

위와 같이 제네릭로 데이터형을 String을 지정하였지만, StringNumber 클래스의 하위 클래스가 아니기 때문에 컴파일 에러가 발생한다.