새로운 블로그로 이전하였습니다!

제네릭

제네릭은 클래스나 메서드에서 사용할 데이터 타입을 컴파일 과정에서 미리 지정하는 방법입니다.

컴파일 시에 미리 타입 검사를 수행함을 통해 러닝 과정에서 오류를 사전에 방지할 수 있습니다.

보통 대표적인 제네릭 사용으로 List<String> list = new ArrayList<>(); 등을 알고 있을탠데

여기선 <Object>, <T>, <?> 등에 대해 알아보고자 합니다.

 

< Object > : raw type

Element를 받을 때 Object에 포함되는 모든 클래스를 받을 수 있습니다.

가장 유연성이 높지만 컴파일 과정에서 타입 체크가 불가능하여 러닝중 오류가 발생할 확률이 높기 때문에

구현 과정에서 신중할 필요가 있고, 가급적 사용하지 않는 것이 좋습니다.

 

< T > : 타입 변수

<String>이나 <Integer> 등으로 지정할 경우 해당 블럭 내에선 지정한 Type 밖에 쓸 수 없지만

<T> 와 같은 타입 변수를 사용하여 클래스나 메서드에서 사용될 타입을 외부에서 지정한 타입으로 사용할 수 있습니다.

가장 많이 접하는 클래스로 Collection Framework 가 있는데

List와 Map을 디컴파일 해보면 List<E> / Map<K, V> 로 구현되어 있는 것을 볼 수 있습니다.

아래는 Class<T> 의 사용예시 입니다.

class GenericTest<T> {
    private T element; // 제네릭 타입

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

    T getElement() {
        return element;
    }
}

class Main {
    public static void main(String[] args) {

        GenericTest<String> stringFunc = new GenericTest<>();
        GenericTest<Integer> integerFunc = new GenericTest<>();

        stringFunc.setElement("10");
        integerFunc.setElement(10);

        System.out.println("stringFunc.value : " + stringFunc.getElement());
        System.out.println("stringFunc.type : " + stringFunc.getElement().getClass());

        System.out.println("integerFunc.value : " + integerFunc.getElement());
        System.out.println("integerFunc.type : " + integerFunc.getElement().getClass());
    }
}

결과

stringFunc.value : 10
stringFunc.type : class java.lang.String
integerFunc.value : 10
integerFunc.type : class java.lang.Integer

타입 변수 표기의 대표적인 것들은 아래와 같습니다.

<T> Type
<E> Element
<K> Key
<V> Value

물론 위에 제한된 것이 아닌 개발자가 원하는 대로 지정하여 사용하는 것도 가능합니다.

또한 extends 를 사용하면 특정 클래스나 인터페이스를 상속받은 타입으로만 제한하는 것도 가능합니다.

< ? > : 와일드 카드

<Object> 와 같이 타입 변수에 모든 타입을 넣을 수 있습니다.

하지만 데이터의 가공이 불가능하여 put(), add() 등을 사용할 수 없다는 점에 차이점이 있습니다.

보통 데이터를 사용하기 보단 기능을 활용할 때 주로 사용합니다.

import java.util.*;

class LandAnimal { public void crying() { System.out.println("육지동물"); } }
class Cat extends LandAnimal { public void crying() { System.out.println("냐옹냐옹"); } }
class Dog extends LandAnimal { public void crying() { System.out.println("멍멍"); } }
class Sparrow { public void crying() { System.out.println("짹짹"); } }

class AnimalList<T> {
    ArrayList<T> al = new ArrayList<T>();

    public static void cryingAnimalList(AnimalList<? extends LandAnimal> al) {
        LandAnimal la = al.get(0);
        la.crying();
    }
    
    void add(T animal) { al.add(animal); }
    T get(int index) { return al.get(index); }
    boolean remove(T animal) { return al.remove(animal); }
    int size() { return al.size(); }
}

public class Generic03 {
    public static void main(String[] args) {
        AnimalList<Cat> catList = new AnimalList<Cat>();
        catList.add(new Cat());
        AnimalList<Dog> dogList = new AnimalList<Dog>();
        dogList.add(new Dog());

        AnimalList.cryingAnimalList(catList);
        AnimalList.cryingAnimalList(dogList);
    }
}

결과

냐옹냐옹
멍멍
복사했습니다!