일반화
클래스 정의 시 사용되어질 데이터타입을 미리 명시하지 않고 객체 생성 시 데이터 타입을 명시하여 사용하는 기법
주로 Collection API
클래스들에 제네릭이 적용되어 있으므로 인스턴스 생성 시 제네릭 타입으로 사용할 데이터 타입 지정
-> 지정된 데이터 타입이 클래스 내 임시 데이터 타입 대체함
⭐ Ex
import java.util.*;
List<String> list = new ArrayList<String>();
// 컬렉션 요소로 사용되는 데이터가 String 타입으로 고정
Set<Integer> set = new HashSet<Integer>();
// 컬렉션 요소로 사용되는 데이터가 Integer 타입으로 고정
Map<Integer, String> map = new HashMap<Integer, String>();
// 컬렉션 요소 중 키는 Integer, 값은 String 타입으로 고정
클래스 정의 시점에서 클래스명 뒤에 <>
기호 사용하여 가상의 자료형 명시
-> 보통 1글자 영어 대문자 사용(E 또는 T)
-> int형 대신 Integer, char형 대신 Character
가상의 자료형은 클래스 정의 시점에 정확한 자료형을 명시하지 않지만, 클래서 정의 시점에서 데이터타입 대신 사용 가능
해당 클래스 인스턴스 생성 시점에 실제 자료형을 지정하면 가상의 자료형이 실제 자료형으로 대체됨
-> 인스턴스 생성 시점에서 어떤 자료형으로도 변형 가능!
⭐ Ex
public static void main(String[] args) {
GenericClass<Integer> gc = new GenericClass<Integer>();
// T 타입이 Integer 타입으로 지정됨
gc.setMember(1);
// 정수형 데이터 전달 가능
gc.setMember("홍길동");
// String 타입 데이터 전달 불가능!
// 정수 타입 외 모두 컴파일 에러 발생!!!
int num = gc.getMember();
//===========================================
Person p = new Person("홍길동", 20);
// 같은 패키지에 Person 클래스 정의되어 있어야 함
GenericClass<Person> gc2 = new GenericClass<Person>();
gc2.setMember(p);
gc2.setMember(new Person("이순신", 45));
Person person = gc2.getMember();
// person 출력할 경우
// 이순신, 45 출력됨
}
class GenericClass<T> {
// 제네릭 타입으로 T 지정
T member;
// 멤버변수 member의 타입이 T로 지정됨
// 실제 데이터 타입X
public T getMember() {
return member;
}
public void setMember(T member) {
this.member = member;
}
// Getter/Setter 메서드에서 member를 다뤄야 하는 경우에도
// 실제 데이터 타입 대신 가상의 T 타입 사용 가능!
}
- static 멤버 내에서 제네릭 타입 파라미터 사용 불가
제네릭 타입은 인스턴스 생성 시점에서 실제 데이터타입으로 변환됨
하지만 static 멤버는 인스턴스 생성 시점보다 먼저 로딩되므로 데이터타입이 지정되지 않아서 사용이 불가능함!
class GenericClass<T> {
private T data; // 가능
private static T staticMember; // 불가능
// static 멤버변수에 제네릭 타입 파라미터 사용 불가!
public static void staticMethod(T data) {} // 불가능
// static 메서드에 제네릭 타입 파라미터 사용 불가
}
- new 연산자 사용 시 제네릭 타입 파라미터 사용 불가
class GenericClass<T> {
T instance = new T(); // 불가능
// 인스턴스 생성 시 제네릭 타입 파라미터로 생성자 호출 불가능!
}
- instanceof 연산자 사용 시 제네릭 타입 파라미터 사용 불가
public void compare() {
Object o = new Object();
if(o instanceof T) { // 불가능
// 컴파일 시점에서 T의 데이터타입 확인 불가능함!
}
}
부모 타입에 제네릭 타입이 지정되어 있을 경우 서브클래스에서 상속받을 때 부모의 파라미터를 서브클래스 타입 파라미터로 명시
자신만의 제네릭 타입도 추가 가능
⭐ Ex
class Class1<P> {}
interface Interface1<Q> {}
class SubClass<P, Q, R> extends Class1<P> implements Interface1<Q> {
// 부모 타입 제네틱 타입 먼저 명시 후
// 본인만의 제네릭 타입 R 추가
P var1; // 슈퍼클래스 Class1 타입 P
Q var2; // 슈퍼클래스 Interface1 타입 Q
R var3; // 자신의 타입 R
}
제네릭 타입에 사용 가능한 파라미터 제한할 수 있음
제네릭 타입 파라미터 선언 시 Object 타입과 그 자식 타입 모두 사용 가능
필요에 따라 파라미터 타입에 올 수 있는 데이터 타입 제한 가능
< 기본 문법 : 파라미터에 대한 서브클래스 타입으로 제한하는 경우 >
class 클래스명<타입파라미터 extends 클래스타입> {}
⭐ Ex
class GenericClass<E> {}
// 타입 파라미터 E는 어떤 타입으로도 변경 가능
class GenericClass2<E extends Number> {}
// Number 타입 또는 Number 클래스 하위 타입으로만 변환 가능
// Wrapper 클래스가 Number 클래스 하위 타입!
제네릭 타입을 사용해 Person 객체 저장
import java.util.*;
public static void main(String[] args) {
Person p1 = new Person("홍길동", 20);
Person p2 = new Person("강감찬", 44);
List<Person> list = new ArrayList<Person>();
list.add(p1);
list.add(p2);
list.add(new Person("이순신", 30));
// 제네릭 타입으로 지정했기 때문에 객체는 무조건 Person 타입이어야 함!
//list.add("강동원"); - String 타입 저장 불가!
for(int i = 0; i < list.size(); i++) {
Person p = list.get(i);
System.out.println(p.name + ", " + p.age);
}
for(Person p : list) {
System.out.println(p.name + ", " + p.age);
// 향상된 for문도 사용 가능
}
}