프로그래밍 언어/Java

제네릭(Generic)

pine tree root 2022. 6. 8. 22:40

제네릭이란?

요구사항의 변화

자료구조를 만들어 배포하고자 할때, String, Integer등 많은 type을 지원하려고 한다. 그럼 각각의 case들에 대해서 자료구조를 만들것인가.

예를들어, 그렇다면 다음과 같이 무수히 많은 클래스들이 생겨날수 있다.

public class ArrayList<Integer>;
public class ArrayList<String>;
public class ArrayList<Long>;
public class ArrayList<Double>;

이를 해결하기 위해서 나온 방식이 Generic이다.

 

제네릭의 등장

제네릭은 직역하면, '일반적인'이라는 뜻이고 데이터 형식에 의존하지 않고, 하나의 값이 여러개의 데이터 타입들을 가질수 있게 하는 방법이다.

우리가 사용하고 있는 ArrayList나 LinkedList역시 제네릭을 사용해서 구현하고 있다.

ArrayList<Integer> arrayList = new ArrayList<>();

public class ArrayList<E> extends AbstractList<E>

 위처럼 Integer을 인자로 받을수 있는 arrayList는 내부적으로는 E라는 (E는 element이다) 인자를 받고 있다.

제네릭의 타입들은 보통 위와 같은 타입들이 많이 사용된다.

 

제네릭의 장점

그렇다면, 이런 제네릭형을 사용해서 얻을 수 있는 장점은 무엇일까?

  • 컴파일 단계에서 인자를 체크하므로, 컴파일 단계에서 잘못된 인자에 대한 체크가 가능하다.
  • 위와 같이 클래스 외부에서 타입을 설정해주므로, 따로 타입에 대한 체크가 필요하지 않다.
  • ArrayList의 사례에서도 볼수 있듯이, 비슷한 기능을 제공하는 경우 코드의 재사용성이 높다.

제네릭의 사용

1. 클래스 및 인터페이스

제네릭의 타입들은 보통 위와 같은 타입들이 많이 사용된다.

public class myClass <T> {  ...  };

public class myClass <T, K> { ... };

클래스 인자로 받을 타입을 T로 선언이 가능하고, <T, K>로 둠으로써, 제네릭 타입을 두개로 둘 수도 있다.

 

2. 제네릭 클래스 심화

class ClassName<E> {
    private E element; // 제네릭 타입 변수

    void set(E element) {  // 제네릭 파라미터 메소드
        this.element = element;
    }

    E get() {  // 제네릭 타입 반환 메소드
        return element;
    }
}

제네릭 클래스이고, 제네릭 element를 가지며, 제네릭 파라미터 메소드, 제네릭 타입 반환 메소드를 가질수 있다.

 

3. 제네릭 메소드

public <T> T myGenericMethod(T o);

위와 같이 제네릭 메소드를 통하여서도 사용이 가능하다.

 

4. 제네릭 메소드 ? 

2번의 경우에서 볼수 있듯이, 이미 E라는 반환타입을 두면 제네릭 메소드를 만들수 있을텐데 왜 반환 타입앞에 <T>라는 키워드가 필요할까?

이는 '정적 메소드로 선언할때 필요' 하기 때문이다.

 

static type이 붙은 메소드들을 생각해보면 이해가 갈수 있다. static 키워드가 붙은 것들은 기본적으로 프로그램 실행시 메모리에 이미 올라가 있다.

이 말은 객체 생성을 통해 접근할 필요 없이 이미 메모리에 올라가 있기 때문에 클래스 이름을 통해 바로 쓸 수 있다는 것이다.

 

그렇다면, 거꾸로 생각해보자면 static 메소드는 객체가 생성되기 전에 이미 메모리에 올라가는데 타입을 어디서 얻어올 수 있을까?

public static <T> T myGenericMethod(T o);

System.out.println("<E> returnType : " + ClassName.myGenericMethod("ABCD").getClass().getName());

new 라는 키워드가 아닌 ClassName으로 접근을 하기때문이다.

그렇기 때문에 제네릭이 사용되는 메소드를 정적메소드로 두고 싶은 경우 제네릭 클래스와 별도로 독립적인 제네릭이 사용되어야 한다는 것이다.