자바에서 사용되는 다양한 컬렉션 클래스나 함수형 프로그래밍 관련 클래스들에서는 제네릭 타입으로 많은 파라미터들이 선언되어 있다. 따라서 자바를 제대로 이해하려면 제네릭에 대해서 확실히 알고 넘어가야 한다.
그렇다면 제네릭을 사용하는 이유는 무엇일까? 첫 번째로 제네릭을 사용하면 더 강한 데이터 타입 체크
를 할 수 있다. 제네릭을 활용한 코드에서 타입 에러가 발생하는 경우 checked exception이 발생하여 컴파일 단계에서 개발자가 타입을 체크할 수 있다. 두 번째로는 캐스팅을 줄여 성능상 이점
을 가질 수 있다. 컬렉션 클래스를 사용하는 경우 제네릭으로 선언하여 타입 변환을 줄일 수 있다. 이 부분은 조금 넘어가서 다시 알아보자.
그래서 결국 제네릭 타입은 무엇인가? 제네릭 타입은 타입을 파라미터로 전달받은 클래스와 인터페이스를 말한다. 예를 들어 내가 생성한 Student라는 클래스가 있다고 하자. 이 클래스의 리스트를 만들고자 한다면 List<Student>
와 같이 <>
안에 리스트에 담고 싶은 타입을 파라미터로 넘겨주어 리스트를 생성한다.
List 인터페이스가 선언된 코드를 보면 List<E>
로 어떤 타입을 리스트에 담을지는 파라미터로 넘겨 받도록 선언된 것을 확인할 수 있다.
이 부분은 코드로 보는게 훨씬 이해가 쉬울 것 같아서 먼저 코드를 보자.
public class FirstDeveloper {
private Object object;
public void set(Object object) { this.object = object; }
public Object get() { return this.obj; }
}
public class SecondDeveloper<T> {
private T t;
public void set(T t) { this.t = t; }
public T get() { return this.t; }
}
public class Example {
public static void main(String[] args){
FirstDeveloper developer1 = new FirstDeveloper();
developer1.set("고재석"); // 자동 타입 변환 (String -> Object)
String name = (String) developer1.get(); // 강제 타입 변환 (Object -> String)
SecondDeveloper<String> developer2 = new SecondDeveloper<>();
developer2.set("고재석");
String name = (String) developer2.get();
}
}
위의 코드를 보면 일반 타입인 FirstDeveloper와 제네릭 타입인 SecondDeveloper라는 두 클래스를 선언했다. 그리고 각 클래스의 필드 변수를 set, get하며 타입 변환이 일어나는지 보았다. 일반 타입의 클래스에는 set하는 경우 String 타입의 변수가 Object 타입으로 자동 타입 변환이 일어났고, get하는 경우 Object 타입의 변수가 String 타입으로 강제 타입 변환이 발생했다. 하지만 제네릭 타입의 클래스에서는 어떠한 타입 변환이 발생하지 않는다.
어차피 한 타입으로만 특정 변수를 사용하는 경우라면 위와 같이 선언 시에 제네릭 타입으로 줄이는 것이 훨씬 효율적이다. 그렇다면 한 타입으로만 사용하는 것이 아니라면 어떨까? 이러한 경우 타입을 제한하는 방법을 사용할 수 있다.
타입 파라미터를 제한하는 방법으로 상속관계를 표현해서 명시한 클래스의 하위 클래스만 파라미터로 받을 수 있게 제한하는 방법이 있다.
public <T extends Number> int compare(T t1, T t2) { ... }
위의 코드와 같이 T라는 타입 파라미터 변수가 Number의 하위 클래스만 오도록 제한할 수 있다. 따라서 compare 메소드의 파라미터는 Integer, Double등의 Number 클래스의 하위 클래스의 타입 객체만 가능하다.
타입 파라미너를 조금 더 세세하게 제한하는 방법이 있는데 와일드카드 타입으로 선언하는 것이다. 코드에서 ?를 와일드카드라고 부른다. 이 와일드카드를 활용하여 제네릭 타입을 선언하는 방법은 크게 세 가지가 있다.
이와 같이 와일드카드 타입을 사용하면 super로 특정 클래스의 상위 타입으로 제네릭 타입을 제한할 수도 있다.