1. 제네릭
1. 제네릭의 이해
제네릭 이전 코드의 문제점(1)
class Apple{
@Override
public String toString() {
return "나는 사과입니다.";
}
}
class Orange{
@Override
public String toString() {
return "나는 오렌지입니다.";
}
}
class Box{
private Object obj;
public void set(Object obj) {
this.obj = obj;
}
public Object get() {
return obj;
}
}
public class testGeneric {
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);
}
}
Plain Text
복사
•
위 코드에서 처럼 Object 클래스를 사용할 경우 어쩔 수 없이 형 변환 과정이 수반된다.
•
또한 컴파일 과정에서 실수가 발견되지 않을 수 있다.
제네릭 이전 코드의 문제점(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());
}
Plain Text
복사
•
위 코드는 프로그래머가 실수를 해도 제대로 출력이 되기 때문에 더욱 문제가 크다.
•
object에서 모든 함수를 받아버려서 생기는 문제이다.
•
따라서 이러한 문제를 해결하기 위해 제네릭 클래스가 나타나게 되었다.
2. 제네릭 클래스란
•
제네릭 클래스란 클래스 내부에서 타입을 지정하는 것이 아닌 외부에서 사용자에 의해 지정되는 것을 의미한다.
•
예제) 제네릭 클래스(1)
class Box<T> {
private T obj;
public void set(T obj) {
this.obj = obj;
}
public T get() {
return obj;
}
}
public class testGeneric2 {
public static void main(String[] args) {
//T자리에 Apple이 들어가 인스턴스를 생성.
//Apple 또는 Apple을 상속하는 하위 클래스의 인스턴스를 저장할 수 있다.
Box<Apple> aBox = new Box<Apple>();
Box<Orange> oBox = new Box<Orange>();
aBox.set(new Apple()); //사과를 상자에 담는다.
oBox.set(new Orange()); //오렌지를 상자에 담는다.
// aBox.set("apple"); ->Object쓸때는 안났는데 제네릭에선 컴파일 에러가남.
Apple ap = aBox.get(); //사과를 꺼내는데 형 변환 하지 않는다.
Orange og = oBox.get(); //오렌지를 꺼내는데 형 변환 하지 않는다.
System.out.println(ap);
System.out.println(og);
Box<String> sBox = new Box<>();
sBox.set("스트링");
System.out.println(sBox.get());
}
}
Plain Text
복사
//실행 결과
나는 사과입니다.
나는 오렌지입니다.
스트링
Plain Text
복사
•
위 예제 처럼 제네릭 문법을 쓰면 컴파일시 타입이 이미 결정되어 진다.
•
타입 매개변수: Box<T>에서 T
•
타입 인자: Box<Apple>에서 Apple
•
매개변수화 타입: Box<Apple>
•
예제) 제네릭 클래스(2)
class DBox<L,R>{
private L left;
private R right;
public void set(L left, R right) {
this.left = left;
this.right = right;
}
@Override
public String toString() {
return left + "&" + right;
}
}
public class testGeneric3 {
public static void main(String[] args) {
DBox<String, Integer> box = new DBox<String, Integer>();
//DBox<String, Integer> box = new DBox<>(); ->뒤에 생략 가능
box.set("Apple", 25);
System.out.println(box);
DBox<String, Integer> box2 = new DBox<String, Integer>();
box2.set("Orange", 28);
System.out.println(box2);
}
}
Plain Text
복사
//실행 결과
Apple&25
Orange&28
Plain Text
복사
•
위의 코드처럼 두개의 타입을 지정해 줄수도 있다.
3. 제네릭 클래스의 타입 인자 제한하기
타입 인자 제한
class Box<T extends Number>{ //인스턴스 생성 시 타입 인자로 Number 또는 이를 상송하는 클래스만 올수 있음.
private T ab;
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);
...
}
Plain Text
복사
•
위 예제에서 처럼 기본적으로 Integer와 Double이 Number를 상속받고 있으므로 사용 할 수 있다.
타입 인자 제한의 효과
•
상속받은 클래스의 메소드를 사용하기 위해 타입 인자를 제한한다.
•
예제) 타입 인자를 제한하지 않았을때
class Box<T>{
private T ob;
...
public int toIntValue(){
return ob.intValue(); //컴파일 에러
}
}
Plain Text
복사
•
위 예제는 intValue가 타입인자를 제한하지 않았기 때문에 intValue를 사용할 수 없다.
•
intValue를 사용하기 위해선 Number클래스로 타입 인자를 제한해야 한다.
•
예제) 타입 인자를 제한했을 때
class Box<T extends Number>{
private T ob;
...
public int toIntValue(){
return ob.intValue();
}
}
Plain Text
복사
•
위 예제는 Number의 안에 있는 intValue()를 사용하기 때문에 에러가 나지 않는다.