제네릭 타입은 클래스나 인터페이스 뒤에 <> 다이아몬드 부호를 넣고, 타입 변수가 들어가는 것입니다.
class<T>, interface<T> 일반적으로 대문자 하나가 들어갑니다.
● 제네릭은 그 자체로 자료형이기 때문에 매개변수화 타입을 타입 인자로 전달할 수 있습니다.
즉 상자 속에 상자를 넣을 수 있습니다. 마트료시카처럼요.
class Box<T> { // 박스 T를 만듭니다.
private T ob;
public void set(T o) { // 인자를 가질 수 있는 셋팅 메소드를 만들고
ob = o;
}
public T get() { // 인자를 반환하는 반환 메소드를 만들어요.
return ob;
}
}
public static void main(String[] args) {
Box<String> sBox = new Box<>(); // 문자열을 담는 박스를 만듭니다.
sBox.set("I am so happy."); // 박스 안에 ' 나는 행복합니다' 문자를 넣어요.
Box<Box<String>> wBox = new Box<>(); // 문자열을 넣은 박스를 다른 박스 안에 타입 인자로 넣습니다.
wBox.set(sBox);
Box<Box<Box<String>>> zBox = new Box<>(); // 문자열을 넣은 박스를 넣은 박스를 다른 박스 안에 넣습니다...
zBox.set(wBox);
System.out.println(zBox.get().get().get()); // 꺼내고 꺼내고 꺼내면 문자열이 나옵니다.
}
● 제네릭 클래스의 타입 인자 제한
class Box<T extends Number> {...}
→ 인스턴스 생성 시 타입 인자로는 Number 또는 이를 상속하는 클래스만 올 수 있습니다.
Number와 Number의 자식들만 올 수 있습니다. = 제네릭의 타입 인자 상한제한.
public static void peekBox(Box<? extends Number> box) {
System.out.println(box);
}
box는 Box<T> 인스턴스의 참조 값을 전달받는 매개변수입니다.
→ 단 전달되는 인스턴스의 T는 Number 또는 이를 상속하는 하위 클래스이어야 합니다.
public static void peekBox(Box<? super Integer> box) {
System.out.println(box);
}
box는 Box<T> 인스턴스의 참조 값을 전달받는 매개변수입니다.
→ 단 전달되는 인스턴스의 T는 Integer 또는 Integer가 상속하는 클래스이어야 합니다.
즉 위 메소드의 인자로 전달 가능한 인스턴스는 Box<Integer>, Box<Number>, Box<Object>으로 제한됩니다.
class Box<T extends Number> { private T ob; public void set(T o) { ob = o; } public T get() { return ob; } } public static void main(String[] args) { Box<Integer> iBox = new Box<>(); iBox.set(24); Box<Double> dBox = new Box<>(); dBox.set(5.97); . . . . } |
|
<오류인 예> class Box<T> { private T ob; .... public int toIntValue() { return ob.intValue(); // ERROR! } } |
<오류가 나지 않는 예> class Box<T extends Number> { private T ob; .... public int toIntValue() { return ob.intValue(); // OK! } } |
사이 배운 것을 바탕으로 다중 매개변수를 받는 제네릭으로
제네릭을 타입 인자로 넣는 코드를 작성해볼게요.
package day18;
class DBox<L, R>{ //L과 R을 받는 제네릭 박스를 만듭니다.
private L left;
private R right;
void set(L left, R right) {
this.left = left;
this.right = right;
}
}
public class Generic01 {
public static void main(String[] args) {
DBox<String, Integer> box = new DBox<>(); // 각각 문자열과 정수형을 받는 box 인스턴스를 생성
box.set("백백백", 100);
DBox<String, DBox<String, Integer>> bbox = new DBox<>(); // box 인스턴스를 매개변수로 받는 bbox 생성
bbox.set("또, 문자열", box);
}
}
설명은 주석을 참고해주세요!
그럼 이것을 바탕으로 제네릭 클래스의 타입 인자를 인터페이스로 제한하는 것을 한 번 더 구현해볼게요.
interface Eatable { // '먹을 수 있는' 인터페이스 클래스를 생성합니다.
public String eat(); // 그 안의 추상 메소드는 문자열 eat()을 반환하는 메소드입니다. 이것을 정의해주어야만 이 인터페이스를 완성할 수 있습니다.
}
class Apple implements Eatable { // '먹을 수 있는' 클래스를 상속한 사과 클래스를 만들고
public String toString() {
return "I am an apple."; // '나는 사과야'를 출력하는 문자열을 추가합니다.
}
@Override // implements 는 반드시 메소드를 부모의 메소드를 오버라이딩 해야하므로
public String eat() { // eat() 이라는 추상 메소드에
return "It tastes so good!"; // 무척 맛있지! 라는 문자열을 오버라이딩 합니다.
}
}
class Box<T extends Eatable> { // '먹을 수 있는' 클래스를 상속하는 박스 제너럴 클래스를 생성합니다.
private T ob;
public void set(T o) {
ob = o;
}
public T get() { //get() 제너럴 메소드를
System.out.println(ob.eat()); // Eatable 인터페이스로 제한하였기에 eat()함수의 호출이 가능합니다.
return ob; // 즉 여기에서 It tastes so good! 이 출력됩니다.
}
}
class BoundedInterfaceBox {
public static void main(String[] args) {
Box<Apple> box = new Box<>(); // 박스 제네럴 클래스에 나는 사과라고 주장하는 Apple 클래스를 담아 새로운 인스턴스를 만듭니다.
box.set(new Apple()); // 제너럴 set의 자리에 사과를 넣습니다.
Apple ap = box.get(); // Apple 클래스의 인스턴스 ap를 겟 박스 안에 넣어줍니다.
System.out.println(ap); // ap클래스를 출력합니다. 여기에서 I am an apple.이 출력 됩니다.
}
}
'자바 > 자바 입문 공부일지' 카테고리의 다른 글
자바 기초 공부 일지 38. 컬렉션 프레임 워크 (1) Lise<E> 인터페이스 (0) | 2022.11.02 |
---|---|
자바 기초 공부 일지 37. 제네릭generic 메소드 (0) | 2022.11.02 |
자바 기초 공부 일지 35. 제네릭generic 의 기본 문법 (0) | 2022.11.02 |
자바 기초 공부 일지 34. 문자열 토큰 (0) | 2022.11.01 |
자바 기초 공부 일지 33. 난수(Random) (0) | 2022.11.01 |