데이터 타입을 일반화한다(generalize)는 것을 의미한다.
클래스나 메소드에서 사용할 내부 데이터 타입을 컴파일 시에 미리 지정하도록 해준다.
컴파일 시에 미리 타입 검사(type check)를 수행하면 다음과 같은 장점을 가진다
// 선언
public class MyGeneric<T> { ... }
public Interface MyGenericInterface<T> { ... }
// 생성
...
MyGeneric<String> = new MyGeneric<>();
...
T 타입은 해당 블럭 { ... } 안에서 사용 가능하다.
또한 제네릭 타입의 갯수를 N 개로 선언해도 무관하다.
Generic 의 < > 괄호안에 어떠한 단어가 들어가도 상관이 없으나, 보통 한글자를 사용하고 통용되는 의미의 알파벳이 있다.
타입 | 설명 |
---|---|
Type | |
Element | |
Key | |
Value | |
Number |
이제 클래스에서 Generic을 사용하여 구성하는 법을 알아보자
class MyGeneric<E> {
private E element; // 제네릭 타입 변수
void set(E element) { // 제네릭 파라미터 메소드
this.element = element;
}
E get() { // 제네릭 타입 반환 메소드
return element;
}
}
제네릭으로 선언된 E
타입을 가진 변수와 E
를 반환하는 메소드를 선언할 수 있다.
MyGeneric 클래스를 사용하는 객체에 따라서, E
제네릭에 원하는 type 을 넣어 해당 객체를 원하는 대로 사용할 수 있다.
MyGeneric<String> str = new MyGeneric<>();
MyGeneric<Integer> integer = new MyGeneric<>();
str.set("Hiroo");
integer.set(10);
String strClass = str.get().getClass().getName(); //java.lang.String
String integerClass = integer.get().getClass().getName(); // java.lang.Integer
public <T> T genericMethod(T o) { // 제네릭 메소드
...
}
// 선언 형식 은 다음과 같다
[접근 제어자] <제네릭타입> [반환타입] [메소드명]([제네릭타입] [파라미터]) {
// 텍스트
}
처음 객체를 생성할때 <> 괄호안에 타입 파라미터로 전달한 타입에 의해 메소드의 Type T
또한 결정이 된다.
<> 에 넣은 파라미터에 관계없이 작동하는 메소드를 만들고 싶다면 static 메소드로 구현하면된다.
이때 class 에서 사용하는 Generic Name 과 겹칠경우 <> 를 통해 독립적인 타입임을 알려줘야한다.
class MyGeneric<E>{
...
static E genericMethod(E o) { // compile Error
return o;
}
static <E> E genericStaticMethod(E o) { // compile OK
return o;
}
}
만약 제네릭 타입을 특정 범위 내로 좁혀서 제한하고 싶다면 어떻게 해야할까?
이 때 필요한 것이 바로 extends 와 super, 그리고 ?(물음표)다.
? 는 와일드 카드
라고 해서 쉽게 말해 '알 수 없는 타입'이라는 의미다.
크게 세 가지 방식이 있다. 바로 super 키워드와 extends 키워드, 마지막으로 ? 하나만 오는 경우다. 코드로 보자면 다음과 같다.
<K extends T> // T와 T의 자손 타입만 가능 (K는 들어오는 타입으로 지정 됨)
<K super T> // T와 T의 부모(조상) 타입만 가능 (K는 들어오는 타입으로 지정 됨)
<? extends T> // T와 T의 자손 타입만 가능
<? super T> // T와 T의 부모(조상) 타입만 가능
<?> // 모든 타입 가능. <? extends Object> 와 같은 의미
보통 이해하기 쉽게 다음과 같이 부른다.
? extends T : 상한 경계
? super T : 하한 경계