Generic
: 모든 종류의 타입을 다룰 수 있도록, 클래스나 메소드를 일반화된 타입 매개 변수를 이용하여 선언하는 기법
- C++의 템플릿과 유사
cf) 템플릿(Template)
: 메소드나 클래스 코드를 찍어내듯이 일반화시키는 도구
사용법
- 컬렉션 클래스에서 타입 매개 변수로 사용하는 문자는 다른 변수와 혼동을 피하기 위해 일반적으로 하나의 대문자를 사용
- 관례적으로 타입 매개 변수에 많이 사용하는 문자이며, 반드시 일치할 필요는 없음
타입 | 설명 |
< T > | Type |
< E > | Element |
< K > | Key |
< V > | Value |
< N > | Number |
1. generic 선언
1) 클래스 및 인터페이스 선언
public class ClassName <T> { ... }
public Interface InterfaceName <T> { ... }
2) 제네릭 타입 두개
public class ClassName <T, K> { ... }
public Interface InterfaceName <T, K> { ... }
// HashMap의 경우
public class HashMap <K, V> { ... }
3) 객체 생성
public class MyClass <T, K> { ... }
public class Main {
public static void main(String[] args) {
MyClass<String, Integer> a = new MyClass<Strnig, Integer>();
}
}
- 생성된 제네릭 클래스를 사용해 객체를 생성할 때, 구체적인 타입을 명시해주어야 함
- T는 String, K는 Integer가 됨
- 주의해야 할 점 : 파라미터로 명시할 수 있는 것은 참조타입밖에 올 수 없음
- primitive type(int, doubld, char..)은 올 수 없음
- Wrapper Class를 사용해야 primitive type을 쓸 수 있음
- 참조 타입이 올 수 있다는 것은, 사용자가 정의한 클래스도 타입으로 올 수 있음
2. 제네릭 클래스
class MyClass<K, V> {
private K first;
private V second;
void set(K first, V second) {
this.first = first;
this.second = second;
}
K getFirst() {
return first;
}
V getSecond() {
return second;
}
}
public class Main {
public static void main(String[] args) {
MyClass<String, Integer> a = new MyClass<String, Integer>();
a.set("hi",10);
System.out.println("first data : " + a.getFirst());
System.out.println("K Type : " + a.getFirst().getClass().getName());
System.out.println("second data : " + a.getSecond());
System.out.println("V Type : " + a.getSecond().getClass().getName());
}
}
- 객체를 생성할 때 < > 안에 타입 파라미터를 지정함
3. 제네릭 메소드
public <T> T genericMethod(T o) {
...
}
[접근제어자] <제네릭타입> [반환타입] [메소드명] ([제네릭타입] [파라미터]) {
...
}
- 클래스와 달리 반환타입 이전에 < > 제네릭 타입을 선언
- genericMethod() 는 파라미터 타입에 따라 T 타입이 결정됨
- 즉, 클래스에서 지정한 제네릭 유형과 별도로 메소드에서 독립적으로 제네릭 유형을 선언하여 쓸 수 있음
- 위와 같은 방식이 필요한 이유는 정적 메소드로 선언할 때 필요하기 때문
- 해당 클래스 객체가 인스턴스와 했을 때, < > 안에 파라미터로 넘겨준 타입이 지정된다는 뜻
- 객체 생성을 통해 접근할 필요 없이 프로그램 실행 시 이미 메모리에 올라가있기 때문에 클래스 이름을 바로 쓸 수 있음
cf) Static vs Generic
▶ Generic
- 제네릭 메소드의 제네릭 타입은 지역변수처럼 사용되기 때문에, 프로그램이 실행되어 static 메소드가 메모리에 올라갈 때 타입 지정 없이 메소드의 틀만 공유될 수 있음
- 이후, 메소드 호출 시 타입을 지정하면 됨
=> 제네릭 메소드는 제네릭 클래스 타입과 별도로 지정됨
제한된 Generic과 와일드 카드
1. PECS(Producer Extends, Consumer Super)
: 외분에서 데이터를 생산한다면 extends를, 외부에서 데이터를 소모한다면 super를 사용
<K extends T> // T와 T의 자손 타입만 가능 (K는 들어오는 타입으로 지정 됨)
<K super T> // <T super [타입]> 은 존재하지않음
<? extends T> // T와 T의 자손 타입만 가능
<? super T> // T와 T의 부모(조상) 타입만 가능
<?> // 모든 타입 가능 <? extends Object>랑 같은 의미
- extends T : 상한 경계, 뒤에 오는 타입이 최상위 타입으로 한계가 정해짐
- < T extends Fruit > : Fruit, Apple 타입만 올 수 있음
- < T extends Beef > : Beef 타입만 올 수 있음
- < T extends Food > : Food, Fruit, Apple, Meat, Beef 타입이 올 수 있음
- 여러 개의 타입을 동시에 상속한 경우로 제한하고 싶다면 & 기호를 사용할 수 있음
- super T : 하한 경계, 뒤에 오는 타입이 최하위 타입으로 한계가 정해짐
- < ? super Fruit > : Fruit, Food 타입만 올 수 있음
- < ? super Beef > : Beef, Meat, Food 타입만 올 수 있음
- < ? super Food > : Food 타입만 올 수 있음
2. 와일드 카드
< ? >
: 제네릭 파라미터의 타입보다 제네릭 파라미터를 사용하는 방법이 더 중요할 때 사용
- Unbounded Wildcard라 부르며, 특정 타입에 종속되지 않고, 어떠한 타입이든 올 수 있음
- 와일드 카드가 any type이 아닌, unknown type임
- < ? extends 타입 >
- 매개변수의 자료형을 특정 클래스를 상속받은 클래스로만 제한함
- < ? super 타입 >
- 매개변수의 자료형을 특정 클래스와 그 클래스의 상위 클래스로만 제한함
- < T extends 타입 >
- 상속을 이용해서 T의 자료형을 제한함
- 클래스 선언 시 사용하며, 인스턴스 생성 시 특정 클래스를 상속받은 클래스형만 인스턴스 내부에서 사용할 수 있도록 함
- 특정 인터페이스를 구현한 클래스만 사용하려는 경우에도 사용 가능
cf) < T super 타입 >
- EX) < T super HashMap >
- Type Erasure를 통해 Object로 변환되기 때문에, 어떤 타입인지 추론되지 않는 T는 결국 Object와 다르지 않음
- 만일 타입 정보 소거가 이루어지지 않는다고 해도, 타입 파라미터는 클래스와 인터페이스를 가지리 않으므로, T가 어떤 타입이 되는지 모호해지는 문제가 발생
- < T super HashMap > : 계층구조를 올라가면 T는 AbstractMap, Map, Object가 모두 올 수 있게 됨
-> 이러한 경우 T를 특정할 수 없게 되니 쓸모가 없는 코드가 됨
cf) Generic - Type Erasure
: 자바 코드를 컴파일할 때, 타입을 검사하고, 런타임 시 해당 타입을 삭제하는 절자 컴파일 시 안정성을 보장받을 수 있음
Generic 장점
- 잘못된 타입이 들어올 수 있는 것을 컴파일 단계에서 방지할 수 있음
- 클래스 외부에서 타입을 지정해주기 때문에 따로 타입을 체크하고 변환해줄 필요가 없음 -> 관리가 편함
- 비슷한 기능을 지원하는 경우 -> 코드 재사용성이 높아짐
'JAVA' 카테고리의 다른 글
18. Collection Framework(List, Map, Set...) (0) | 2025.04.29 |
---|---|
17. Reflection(Annotation) (0) | 2025.04.29 |
15. Default Method & 추상 클래스 & 인터페이스 (0) | 2025.04.29 |
14. Lambda & Functional Interface (0) | 2025.04.17 |
13. 자바 8의 특징 (1) | 2025.04.17 |