앞서 제네릭에 대한 상한, 하한을 먼저 보기전에 [Java] 제네릭 (공변, 반공변, 불공변)에 대한 내용을 미리 익히면 좀더 수월하게 이해가 될꺼 같습니다.
한 마디로 말하자면 코드 안정성을 높이기 위해 상한, 하한에 대한 개념을 배운다.
// 박스
class Box<T> {
private T ob;
public void set(T o) { ob = o; }
public T get() { return ob; }
}
// 장난감
class Toy {
@Override
public String toString() {
return "I am a Toy";
}
}
// 박스 input, output
class BoxController {
public static void outBox(Box<Toy> box) {
Toy t = box.get(); // 박스에서 꺼내기
System.out.println(t);
}
public static void inBox(Box<Toy> box, Toy n) {
box.set(n); // 박스에 넣기
}
}
class Main {
public static void main(String[] args) {
Box<Toy> box = new Box<>(); // 박스 생성
BoxHandler.inBox(box, new Toy()); // 박스에 장난감 넣기
BoxHandler.outBox(box); // 장난감 꺼내기
}
}
항상 책이나 블로그 등에서 잘 만들어진 코드는 다음과 같다고 강조한다.
"필요한 만큼만 기능을 허용하며, 코드의 오류가 컴파일 과정에서 최대한 발견하도록 한다."
그럼 위에 예제의 내용은 잘 만들어진 코드일까? 물론 코드에는 문제는 없다. 하지만 잘 만들어진 코드는 아니다. 그럼 다시 BoxController 클래스를 보자.
// 박스 input, output
class BoxController {
public static void outBox(Box<Toy> box) {
Toy t = box.get(); // 박스에서 꺼내기
System.out.println(t);
}
public static void inBox(Box<Toy> box, Toy n) {
box.set(n); // 박스에 넣기
}
}
여기서 박스에 넣는 코드 outBox를 보자.
// 매개변수 box가 참조하는 상자에서 인스터스를 꺼내는 기능
public static void outBox(Box<Toy> box) {
Toy t = box.get(); // 박스에서 꺼내기
System.out.println(t);
}
이 코드에서는 box.get()을 하고 있다. 하지만 box.set()도 가능하다.
그럼 다음과 같은 대참사가 일어날 수 있다.
public static void outBox(Box<Toy> box) {
box.get();
bpx.set(new Toy);
};
이러한 실수를 하게 되면 컴파일 과정에서 발견되지 않는다. 때문에 box 대상으로 get은 가능하지만 set은 불가능한 제한을 거는것이 좋다.
그러면 다음과 같이 정의하는 것이 좋다.
// (Box<Toy> box) -> (Box<? extends Toy> box)
public static void outBox(Box<? extends Toy> box) {
box.get(); // OK
box.set(new Toy); // Error
}
이렇게 하면 outBox 메소드는 "읽기"만 가능하다.
이 코드의 문제점도 똑같다. set, get이 전부 된다.
class BoxController {
...
public static void inBox(Box<Toy> box, Toy n) [
box.set(n); // 상자에 넣기
}
}
하한 제한을 거는 법은 다음과 같다.
class BoxController {
...
public static void inBox(Box<? super Toy> box, Toy n) [
box.set(n); // 상자에 넣기
Toy myToy = box.get(); // 안됨 Error!
}
}
즉 하한 제한은 쓰기만 가능하다.
상한, 하한을 거는 이유는
상한 제한은 "읽기"만 가능(<? extends T>)
하한 제한은 "쓰기"만 가능(<? super T>)
참고: 윤성우의 열혈 Java 프로그래밍