이 노트는 "윤성우의 열혈 java 프로그래밍" 책을 공부하면서
내가 이해한대로 다시 정리하면서 작성되었다.
🤔 제네릭?
제네릭이 갖는 의미는 일반화이다. 여기서 일반화의 대상은 자료형이다.
제네릭이 등장하면서 자료형에 의존적이지 않은 클래스를 정의할 수 있게 되었다.
class Apple {
public String toString() {
return "I am an apple.";
}
}
class Orange {
public String toString() {
return "I am an orange.";
}
}
class Box {
private Object ob;
public void set(Object o) {
ob = o;
}
public Object get() {
return ob;
}
}
class FruitAndBox2 {
public static void main(String[] args) {
Box aBox = new Box();
Box oBox = new Box();
// 상자에 과일을 담는다.
aBox.set(new Apple());
oBox.set(new Orange());
// 상자에서 과일을 꺼낸다.
Apple ap = (Apple)aBox.get();
Orange og = (Orange)oBox.get();
System.out.println(ap);
System.out.println(og);
}
}
위의 코드에서 Box 내에서 인스턴스를 저장하는 참조변수가 Object이기 때문에 내용물을 꺼낼 때는 형 변환이 필요하다.
또는 담은 자료형과 꺼내는 자료형이 다르다면 컴파일 에러가 난다.
제네릭을 사용하지 않는다면,
위의 Box 클래스를 변경해본다.
class Box<T> {
private T ob;
public void set(T o) {
ob = o;
}
public T get() {
return ob;
}
}
T
는 인스턴스를 생성할 때 결정하면 된다.
인스턴스 생성 시 T
의 자료형을 결정하는 것이 '제네릭'이다.
Box<Apple> aBax = new Box<Apple>();
Box<T>
에서 T
Box<Apple>
에서 Apple
Box<Apple>
칸이 둘로 나뉘어 진 상자를 표현한 제네릭 클래스 정의이다.
class DBox<L, R> {
private L left; // 왼쪽 수납 공간
private R right; // 오른쪽 수납 공간
public void set(L o, R r) {
left = o;
right = r;
}
@Override
public String toString() {
return left + " & " +right;
}
}
class MultiTypeParam {
public static void main(String[] args) {
DBox<String, Integer> box = new DBox<String, Integer>();
box.set("Apple", 25);
System.out.println(box);
}
}
매개변수화 타입을 구성할 때 기본 자료형의 이름은 타입 인자로 쓸 수 없다.
하지만 기본 자료형에 대한 래퍼 클래스가 존재하고, 필요 상황에서 박싱과 언박싱이 자동으로 이뤄지니,
다음과 같이 정의할 수 있다.
class Box<T> {
private T ob;
public void set(T o) {
ob = o;
}
public T get() {
return ob;
}
}
class PrimitivesAndGeneric {
public static void main(String[] args) {
Box<Integer> iBox = new Box<Integer>();
iBox.set(125);
int num = iBox.get();
System.out.println(num);
}
}
컴파일러는 제네릭 관련 문장에서 자료형의 이름을 추론하는 능력을 가지고 있다.
따라서
Box<Apple> aBox = new Box<Apple>();
를 Box<Apple> aBox = new Box<>();
와 같이 쓸 수 있다.
Box<T>
에는 무엇이든 담을 수 있었지만, 클래스는 그 특성과 용도가 있으니 자료형을 제한할 수 있어야 한다.
이때 extends
를 사용해 타입 인자를 제한한다.
class Box<T extends Number> {...}
클래스 뿐만 아니라 일부 메소드에 대해서만 제네릭으로 정의하는 것도 가능하다.
static 선언의 유무에 상관없이 제네릭 메소드의 정의가 가능하다.
public static <T> Box<T> makeBox(T o) {...}
makeBox
이고, 반환형은 Box<T>
이다.Box<T>
사이에 위치한 <T>
는 T가 타입 매개변수임을 알리는 표시다.