위 코드 역시 이전 포스트에서 사용한 테스트 코드이다. 위 테스트 코드를 보면 제네릭 타입을 실제로 사용할 때에는 어떻게 사용하는지 알 수 있는데, 사실 여기서는 생략된 부분이 있다.
제네릭 타입 변수를 선언하고 인스턴스를 생성하는 코드인 GenericStorage<Integer> storage = new GenericStorage<>(); 에서 보면, 인스턴스를 생성할 때에 new GenericStorage<>(); 라는 코드로 인스턴스를 생성하고 있다. 원래는 제네릭 타입 변수를 선언할 때처럼 new GenericStorage<Integer>(); 와 같이 구체적 타입을 지정해야 하지만 자바 7부터 제네릭 타입의 인스턴스 생성 시 구체적 타입을 생략한 다이아몬드 연산자(diamond operator)를 지원한다.
멀티 타입 파라미터(Multi Type Parameter)
그런데 이 GenericStorage 클래스를 사용하다보니 또 다른 요구 사항이 발생했다, 라고 가정해보자. 이 하나의 클래스에 1종류의 데이터만 담고 있었는데, 또 다른 종류의 데이터도 함께 담고 싶다는 것이었다. 그럼 어떻게 해야 할까? 더군다나 지금은 제네릭 타입으로 바꾼 상태인데?
다행히도 제네릭의 타입 파라미터는 여러 개를 선언할 수 있으며, 이를 멀티 타입 파라미터라고 표현할 수 있다. 아래와 같이 변경해보자.
자, 이제 위의 요구 사항이었던 또 다른 종류의 데이터를 담을 수 있는 GenericStorage 클래스가 되었다.
제네릭 메서드(Generic Method)
반드시 클래스 또는 인터페이스 선언에서만 제네릭을 선언할 수 있는 것은 아니다. 메서드에도 제네릭을 적용하여 제네릭 메서드를 사용할 수 있다. 아래 예제를 보자.
1 2 3 4 5
public <E> List<E> wrapList(E e){ List<E> list = new ArrayList<>(); list.add(e); return list; }
위와 같이 파라미터 타입과 리턴 타입에 타입 파라미터를 가지는 메서드를 제네릭 메서드라고 한다.
리턴 타입 앞에 <>를 추가하고 타입 파라미터를 선언하며, 리턴 타입과 파라미터 타입에 선언한 타입 파라미터를 사용할 수 있다. 또한 제네릭 타입과 마찬가지로 멀티 타입 파라미터도 가능하다. 이렇게 제네릭 메서드에 선언한 타입 파라미터는 해당 메서드 내에서만 유효하다.
제네릭 메서드를 사용하는 테스트 코드는 아래와 같다.
1 2 3 4 5 6 7 8 9 10 11
@Test publicvoidwrapList(){
final String data = "this is data.";
GenericStorage<Integer, Double> storage = new GenericStorage<>(); final List<String> stringList = storage.<String>wrapList(data); // final List<String> stringList = storage.wrapList(data);
Assert.assertEquals(data, stringList.get(0)); }
위 코드에서 실제로 제네릭 메서드를 사용한 부분은 final List<String> stringList = storage.<String>wrapList(data); 인데, 여기서 제네릭 메서드를 호출할 때 구체적 타입을 명시함을 알 수 있다. 그러나 구체적 타입을 생략하고 final List<String> stringList = storage.wrapList(data); 와 같은 방법으로 호출하더라도 파라미터의 타입을 통해 구체적 타입을 추정한다.
한정적 타입 파라미터(Bounded Type Parameter)
필요에 따라 타입 파라미터의 범위를 한정해야 하는 경우가 있다. 그럴 때에는 한정적 타입 파라미터를 선언할 수 있다.
한정적 타입 파라미터를 사용하는 예제를 위해, Beverage 클래스와 그 하위 클래스를 보관할 수 있는 BeverageStorage 클래스를 만들어달라는 요구 사항이 생겼다고 가정해보자. 먼저 Beverage 클래스와 그 하위 클래스들부터 만들어보자.
여기서 핵심은 한정적 타입 파라미터를 사용한 <T extends Beverage> 부분이다. 타입 파라미터를 선언할 때 extends 키워드를 사용하여 상위 타입을 지정하면 해당 타입 파라미터의 구체적 타입은 지정한 상위 타입 및 상위 타입을 상속(또는 구현)하는 하위 타입으로만 한정할 수 있다.