- 제네릭이란 클래스 내부에서 사용할 데이터 타입을 외부에서 지정하는 기법이다.
제네릭 선언 및 생성
// 선언
class MyClass<T> {
T element;
void setElement(T element) { this.element = element; }
T getElement() {
return element;
}
}
public class Main {
public static void main(String[] args) {
// 생성: 예시로 Integer타입 명시함
MyClass<Integer> mc = new MyClass<Integer>();
mc.setElement(777);
System.out.println(mc.getElement()); // 777
}
}
// Java SE 7부터 인스턴스 생성 시 타입을 추정할 수 있는 경우에는 타입을 생략할 수 있습니다.
MyClass<Integer> mc = new MyClass<>();
- 'T'는 타입 변수라고 하며, 임의의 참조형 타입을 의미한다.
- 타입 변수는 클래스, 메소드의 매개변수나 반환값으로도 사용할 수 있다.
- 생성 시 타입변수 자리에 사용할 실제타입을 명시해야 한다.
꼭 'T'뿐만 아니라 어떤 문자를 사용해도 상관은 없다.
다만 아래처럼 암묵적(?)인 규칙이 있다 한다.
타입 | 설명 |
---|---|
<T> | Type |
<E> | Element |
<K> | Key |
<V> | Value |
<N> | Number |
멀티 타입 파라미터
- 여러 개의 타입 변수는 쉼표(,)로 구분하여 명시할 수 있다.
class MyClass<T, E> {
T num;
E str;
void setElement(T num, E str) {
this.num = num;
this.str = str;
}
T getNum() {
return num;
}
E getStr() {
return str;
}
}
public class Main {
public static void main(String[] args) {
MyClass<Integer, String> mc = new MyClass<Integer, String>();
mc.setElement(777, "칠칠칠");
System.out.println(mc.getNum()); // 777
System.out.println(mc.getStr()); // 칠칠칠
}
}
제한된 제네릭
- 타입을 구체적으로 제한하고 싶을 때
- extends, super, 와일드카드로 제한할 수 있다.
- 와일드카드는 물음표(?) 기호를 말하며 '알 수 없는 타입'을 의미한다.
- 메섣, 인터페이스, 클래스에서 동일하게 적용
<T extends 상위타입>
- T를 대체할 수 있는 타입은 상위타입이나 상위타입을 상속받는 하위타입들만 가능하다.
class Bag<T> {
T thing;
Bag(T thing) {
this.thing = thing;
}
}
Bag에 담을 수 있는 객체에 제한을 두려고 한다.
책, 필통, 노트처럼 고체인 물건은 담을 수 있지만,
물이나 커피처럼 엑체인 물건은 담을 수 없도록 제한해보자.
class Solid{}
class Liquid{}
class Book extends Solid{} // 고체클래스를 상속받은 하위클래스
class NoteBook extends Solid{} // 고체클래스를 상속받은 하위클래스
class Water extends Liquid{} // 액체클래스를 상속받은 하위클래스
// 고채만 넣을 수 있게 제한
class Bag<T extends Solid> {
T thing;
Bag(T thing) {
this.thing = thing;
}
}
// 메인
public class Main {
public static void main(String[] args) {
Bag<Book> bag1 = new Bag<Book>(new Book());
Bag<NoteBook> bag2 = new Bag<NoteBook>(new NoteBook());
// Bag<Water> bag3 = new Bag<Water>(new Water()); // 오류 발생
}
}
액체클래스의 하위클래스인 Water는 T타입으로 들어갈 수 없다.
와일드 카드 <?>
- 어떤 타입이든 올 수 있다. <? extends Object>와 마찬가지다.
물품 주인이 같은지 확인해보는 메서드를 추가하였다.
class Bag<T extends Solid> {
T thing;
String owner;
Bag(T thing, String owner) {
this.thing = thing;
this.owner = owner;
}
// 물품 주인이 같은지 확인
void isSameOwner(Bag<T> obj) {
if (this.owner.equals(obj.owner)) {
System.out.println("true");
} else {
System.out.println("false");
}
}
}
// 메인
public class Main {
public static void main(String[] args) {
Bag<Book> bag1 = new Bag<Book>(new Book(), "김오너");
Bag<NoteBook> bag2 = new Bag<NoteBook>(new NoteBook(), "김오너");
// bag1.isSameOwner(bag2); // 오류 발생
}
}
오류가 발생한 이유에 대해 알아보자.
bag1의 타입은 Book이고, bag2의 타입은 NoteBook이다.
bag1의 isSameOwner() 메소드에서 사용하는 T와 인자로 전달된 obj의 T값이 달라서 오류가 발생한 것이다.
이럴 때 사용할 수 있는 것이 와일드카드다.
아래와 같이 와일드카드를 사용하면 오류가 발생하지 않는다.
void isSameOwner(Bag<?> obj) {...}
와일드 카드 제한
- 와일드카드에도 타입을 제한할 수 있다.
- <? extends 상위타입>: 상위타입 이하로 제한한다.
- <? super 하위타입>: 하위타입 이상으로 제한한다.
'Java' 카테고리의 다른 글
[Java] 컬렉션 프레임워크 (0) | 2022.09.19 |
---|---|
[Java] 람다식과 함수형 인터페이스 (0) | 2022.09.17 |
[Java] 예외 처리 (0) | 2022.09.15 |
[Java] 입출력 스트림 (0) | 2022.09.15 |
[Java] 내부 클래스와 익명 클래스 (0) | 2022.09.14 |