본문 바로가기

# 02/Java

[윤성우 열혈자바] 21-1. 제네릭의 이해

반응형

제네릭 이전의 코드


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; }

}








제네릭 이전의 코드의 사용의 예


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);

}


어쩔 수 없이 형 변환의 과정이 수반된다.


그리고 이는 컴파일러의 오류 발견 가능성을 낮추는 결과로 이어진다.


형 변환도 귀찮음....









제네릭 이전 코드가 갖는 문제점 1


프로그래머의 실수가 컴파일 과정에서 발견되지 않는다.


에러는 왠만하면 컴파일 과정에서 발견되는게 고치기 쉬움. 

그 다음 실행 결과에서 예외에서 발견하면 그나마 괜찮음. 

둘 다 안뜨는 경우 최악!!


public static void main(String[] args) {

Box aBox = new Box();

Box oBox = new Box();


// 아래 두 문장에서는 사과와 오렌지가 아닌 '문자열'을 담았다.

aBox.set("Apple");

oBox.set("Orange");


// 상자에 과일이 담기지 않았는데 과일을 꺼내려 한다.

Apple ap = (Apple) aBox.get();

Orange og = (Orange) oBox.get();


System.out.println(ap);

System.out.println(og);

}












제네릭 이전 코드가 갖는 문제점 2


프로그래머의 실수가 실행 과정에서 조차 발견되지 않을 수 있다. 정말 큰 문제!!!


public static void main(String[] args) {

Box aBox = new Box();

Box oBox = new Box();



// 다음 두 문장은 프로그래머의 실수이다!

aBox.set("Apple");

oBox.set("Orange");



System.out.println(aBox.get());

System.out.println(oBox.get());

}











제네릭 기반의 클래스 정의하기


인스턴스 생성시 결정이 되는 자료형의 정보를 T로 대체한다.


class Box {

private Object ob;


public void set(Object o) {

ob = o;

}


public Object get() {

return ob;

}

}



-> 변경



class Box<T> {

private T ob;


public void set(T o) {

ob = o;

}


public T get() {

return ob;

}

}








제네릭 클래스 기반 인스턴스 생성


class Box<T> {

private T ob;

public void set(T o) {


ob = o;


}


public T get() {


return ob;

}

}


  • 타입 매개변수 (Type Parameter)             Box<T>에서 T
  • 타입 인자 (Type Argument)                 Box<Apple>에서 Apple
  • 매개변수화 타입(Parameterized Type)         Box<Apple>

Box<Apple> aBox = new Box<Apple>();

-> T를 Apple로 결정하여 인스턴스 생성

-> 따라서 Apple 또는 Apple을 상속하는 하위 클래스의 인스턴스 저장 가능


Box<Orange> oBox = new Box<Orange>();

-> T를 Orange로 결정하여 인스턴스 생성

-> 따라서 Orange 또는 Orange를 상속하는 하위 클래스의 인스턴스 저장 가능








제네릭 이후의 코드 : 개선된 결과


class Box<T> {

private T ob;

public void set(T o) {

ob = o;

}


public T get() {

return ob;

}

}



public static void main(String[] args) {

Box<Apple> aBox = new Box<Apple>();                // T를 Apple로 결정

Box<Orange> oBox = new Box<Orange>();                // T를 Orange로 결정


aBox.set(new Apple());                                    // 사과를 상자에 담는다.

oBox.set(new Orange());                                   // 오렌지를 상자에 담는다.


Apple ap = aBox.get();                              // 사과를 꺼내는데 형 변환 하지 않는다.

Orange og = oBox.get();                            // 오렌지를 꺼내는데 형 변환 하지 않는다.


System.out.println(ap);

System.out.println(og);

}









실수가 컴파일 오류로 이어진다.


public static void main(String[] args) {

Box<Apple> aBox = new Box<Apple>();

Box<Orange> oBox = new Box<Orange>();


aBox.set("Apple");            // 프로그래머의 실수

oBox.set("Orange");            // 프로그래머의 실수


Apple ap = aBox.get();

Orange og = oBox.get();


System.out.println(ap);

System.out.println(og);

}






반응형