Post

제네릭

제네릭

다양한 타입의 객체들을 다루는 메서드나 클래스에 컴파일 시 타입 체크를 해주는 기능이다. 객체의 타입을 컴파일 시에 체크하기 때문에 객체의 타입 안정성을 높이고 타입 캐스팅을 제거할 수 있다.

제네릭 예시를 통해 살펴보자.

1
2
3
4
5
6
7
8
9
#### 제네릭 사용 전 ####
List myIntList = new LinkedList(); // 1
myIntList.add(new Integer(0)); // 2
Integer x = (Integer) myIntList.iterator().next(); // 3

#### 제네릭 사용 후 ####
List<Integer> myIntList = new LinkedList<Integer>(); // 1'
myIntList.add(new Integer(0)); // 2'
Integer x = myIntList.iterator().next(); // 3'
  • 제네릭은 매개변수로 타입을 전달할 수 있다. 이는 타입 파라미터라고 부르며 타입 파라미터는 <> 꺽쇠 안에 선언한다.
  • 사용 전후의 세번째 줄을 보면 제네릭을 사용했을 때 타입 캐스팅을 하지 않아도 되는 것을 확인할 수 있다.
  • 즉 제네릭을 사용하므로써 명시적으로 타입을 지정하게 되고 이는 컴파일 시 잘못된 타입으로 치환되는 것을 막을 수 있다. 따라서 런타임 시 다른 타입으로 잘못 형변환하여 예외가 발생하는 일이 없다.

제네릭 선언 방법

클래스에서 제네릭 사용하기

1
2
public class ClassName <T> { ... }
public Interface InterfaceName <T> { ... }
  • 여기서 T는 클래스 내부에서 타입 파라미터를 대표하는 값으로 사용된다.
  • 이 경우 T가 타입 파라미터로 넘겨준 실제 타입으로 대체되게 된다.

메소드에서 제네릭 사용하기

  • 메소드에 한정한 제네릭 즉 클래스와는 별도로 제네릭을 사용할 수 있다.
1
2
3
4
5
6
7
public <T> T genericMethod(T o) {	// 제네릭 메소드
  ...
}
 
[접근 제어자] <제네릭타입> [반환타입] [메소드명]([제네릭타입] [파라미터]) {
  ...
}
  • 클래스와는 다르게 반환타입 이전에 <> 제네릭 타입을 선언한다.

제네릭을 선언할 때 <>꺽쇠 안에는 어떤 단어가 들어가도 상관이 없지만 자바에서 정의한 기본 규칙은 있다.

제네릭 타입의 이름 규칙

  • E : 요소 (Element, 자바 컬렉션에서 주로 사용됨)
  • K : 키
  • N : 숫자
  • T : 타입
  • V : 값
  • S, U, V : 두번째, 세번째, 네번째에서 선언된 타입

제네릭 주요 개념

와일드 카드

1
2
3
4
5
6
7
8
9
10
11
12
13
void printCollection (Collection<Object> c)  {
  for (Object e : c ) {
    ...
  }
}

void printCollectionWithWildCard (Collection<?> c) {
 
  for (Object e : c) {
    ...
 
  }
}
  • 이 예시를 보면 <Object>라는 타입을 지정하는데 제네릭은 지정한 타입만 받을 수 있기 때문에 최상위 클래스 Object를 사용해 Collection<Object>로 선언하더라도 모든 Collection 타입을 허용할 수 있는 것이 아니다.
  • 그래서 collection of unknown이라고 하는 모든 element 타입에 매치될 수 있는 와일드 카드를 사용하는 이유이다.

그러나 Collection<?>로 와일드카드를 사용하면 Collection 타입은 안전하게 사용할 수 있지만 임의의 객체를 다 받을 수는 없다.

1
2
3
Collection<?> c = new ArrayList<String>();
 
c.add(new Object()); // 컴파일 에러
  • 위의 예제를 보면 c의 element type이 알수 없는 유형을 나타내므로 c에는 nulll 외에는 아무 것도 전달할 수 없다.

바운디드 와일드카드

제네릭으로 넘어오는 타입에 대해서 제한하고 싶을 경우 사용할 수 있는 방법이다.

이때 나오는 것이 extends, super, ? (와일드 카드) 이이며 다음과 같이 상한(upper bound)과 하한(lower bound)을 제한할 수 있다.

1
2
3
4
5
6
<K extends T>	// T와 T의 자손 타입만 가능 (K는 들어오는 타입으로 지정 됨)
<K super T>	    // T와 T의 부모(조상) 타입만 가능 (K는 들어오는 타입으로 지정 됨)
 
<? extends T>	// T와 T의 자손 타입만 가능
<? super T>	    // T와 T의 부모(조상) 타입만 가능
<?>		        // 모든 타입 가능.
This post is licensed under CC BY 4.0 by the author.