제네릭(Generics)
: 제네릭은 ‘일반화’한다는 뜻을 담고 있다. 그리고 그 일반화의 대상은 자료형이다.
class Orange{
int sugarContent;
public Orange(int sugar) {
sugarContent = sugar;
}
public void showSugarContent() {
System.out.println("당도 : "+sugarContent);
}
}
class FruitBox{
Object item;
public void store(Object item) {
this.item = item;
}
public Object pullOut() {
return item;
}
}
public class Main {
public static void main(String[] args) {
FruitBox fBox1 = new FruitBox();
fBox1.store(new Orange(10));
Orange org1 = (Orange)fBox1.pullOut();
org1.showSugarContent();
FruitBox fBox2 = new FruitBox();
fBox2.store("오렌지");
Orange org2 = (Orange)fBox2.pullOut();
org2.showSugarContent();
}
}
위에서 fBox2에 오렌지를 넣고 뺄 때 당연히 오류가 나지만 컴파일에서 알 수 없다.
class Orange{
int sugarContent;
public Orange(int sugar) {
sugarContent = sugar;
}
public void showSugarContent() {
System.out.println("당도 : "+sugarContent);
}
}
class OrangeBox{
Orange item;
public void store(Orange item) {
this.item = item;
}
public Orange pullOut() {
return item;
}
}
public class Main {
public static void main(String[] args) {
OrangeBox fBox1 = new OrangeBox();
fBox1.store(new Orange(10));
Orange org1 = (Orange)fBox1.pullOut();
org1.showSugarContent();
OrangeBox fBox2 = new OrangeBox();
fBox2.store("오렌지");
Orange org2 = (Orange)fBox2.pullOut();
org2.showSugarContent();
}
}
위에서 처럼 Orange멤버 변수를 가진 클래스에 넣으면 컴파일에서 에러 메시지를 확인할 수 있다. 중요한 사실은 컴파일에서 에러 메시지를 확인할 수 있다는 것이다. 컴파일에서 확인할 수 있는 오류는 쉽게 해결이 가능하다. 자료에 대한 안정성이 보장된다고 볼 수 있다. Object멤버 변수를 가진 FruitBox로 클래스를 만들기보다 OrangeBox, AppleBox, … 자료형에 맞는 클래스가 안전성이 보장된다고 할 수 있다. 하지만! 각 자료형에 맞는 클래스를 만들면 같은 내용을 여럿 만드는 것이 번거롭지 않은가? 그래서~ 제네릭을 씀으로 일반화 하는 것이다~
-
제네릭 클래스의 설계
class FruitBox{ Object item; public void store(Object item) { this.item = item; } public Object pullOut() { return item; } }
여기서 대상에 따라 변경되어야 하는 자료현은 Object
class FruitBox<T>{ T item; public void store(T item){ this.item = item; } public T pullOut(){ return item; } }
이것이 바로 제네릭 클래스이다! 참고로 T는 type의 약자를 대문자로 한 것일 뿐 어떤 것이와도 상관 없다. J, H, M, V, …
-
한 번 FruitBox를 이용하여 만들어 볼까요?
package study_java; class Orange{ int sugarContent; public Orange(int sugar) { sugarContent = sugar; } public void showSugarContent() { System.out.println("당도 : "+sugarContent); } } class Apple{ int weight; public Apple(int weight) { this.weight = weight; } public void showWeight() { System.out.println("무게 : "+ weight); } } class FruitBox<T>{ T item; public void store(T item) { this.item = item; } public T pullOut() { return item; } } public class Main { public static void main(String[] args) { FruitBox<Orange> fBox1 = new FruitBox<Orange>(); fBox1.store(new Orange(10)); Orange org1 = (Orange)fBox1.pullOut(); org1.showSugarContent(); FruitBox<Apple> fBox2 = new FruitBox<Apple>(); fBox2.store(new Apple(20)); Apple app = (Apple)fBox2.pullOut(); app.showWeight(); } }
-
-
제네릭 메소드의 정의와 호출
class AAA{ public String toString() { return "Class AAA"; } } class BBB{ public String toString() { return "Class BBB"; } } class InstanceTypeShower { int showCnt = 0; public <T> void showInstType(T inst) { System.out.println(inst); showCnt++; } void showPrintCnt() { System.out.println("Show count: "+showCnt); } } public class Main { public static void main(String[] args) { AAA aaa = new AAA(); BBB bbb = new BBB(); InstanceTypeShower shower = new InstanceTypeShower(); shower.<AAA>showInstType(aaa); shower.<BBB>showInstType(bbb); shower.showPrintCnt(); } }
제네릭 메소드의 호출방법을 익히면 됩니다. AAA를 통해 T를 AAA클래스로 인식한다. 하지만
를 지운다고 못알아볼까? 자동으로 참조변수 aaa를 근거로 AAA클래스를 연결한다. 또한 둘 이상의 자료형 매개변수를 선언하고 각각에 다른 자료형 정보를 전달할 수 있다.
class InstanceTypeShower2 { public <T, U> void showInstType(T inst1, U inst2) { System.out.println(inst1); System.out.println(inst2); } } public class Main { public static void main(String[] args) { AAA aaa = new AAA(); BBB bbb = new BBB(); InstanceTypeShower2 shower = new InstanceTypeShowe2(); shower.<AAA, BBB>showInstType(aaa, bbb); } }
-
매개변수로 이 자료형만 전달하세요.
package study_java; interface SimpleInterface{ public void showYourName(); } class UpperClass{ public void showYourAncestor() { System.out.println("UpperClass"); } } class AAA extends UpperClass implements SimpleInterface{ public void showYourName() { System.out.println("Class AAA"); } } class BBB extends UpperClass implements SimpleInterface{ public void showYourName() { System.out.println("Class BBB"); } } class Main{ public static<T extends SimpleInterface> void showInstanceAncestor(T param) { param.showYourName(); } public static <T extends UpperClass> void showInstanceName(T param) { param.showYourAncestor(); } public static void main(String[] args) { AAA aaa = new AAA(); BBB bbb = new BBB(); showInstanceAncestor(aaa); showInstanceName(aaa); showInstanceAncestor(bbb); showInstanceName(bbb); } }
-
- 와일드카드와 제네릭 변수의 선언
- 와일드 카드란 이름 또는 문자열에 제한을 가하지 않음을 명시하는 용도로 사용되는 특별한 기호다.
class Fruit{ public void showYou() { System.out.println("나는 과일입니다."); } } class Apple extends Fruit{ public void showYou() { super.showYou(); System.out.println("나는사과입니다."); } } class FruitBox<T>{ T item; public void store(T item) { this.item = item; } public T pullOut() { return item; } } class Main{ public static void openAndShowFruitBox(FruitBox<? extends Fruit> box) { Fruit fruit = box.pullOut(); fruit.showYou(); } public static void main(String[] args) { FruitBox<Fruit> box1 = new FruitBox<Fruit>(); box1.store(new Fruit()); FruitBox<Apple> box2 = new FruitBox<Apple>(); box2.store(new Apple()); openAndShowFruitBox(box1); openAndShowFruitBox(box2); } }
-
- 자료형에 상관없이 참조변수 선언 와일드카드
- FruitBox<?> box
-
- 하위 클래스를 제한하는 용도의 와일드카드
- FruitBox<? super Apple> boundedBox —> 위의 경우 Object, Fruit, Apple 자료형 허용
-
제네렉 클래스 상속
class AAA<T>{ T itemAAA; } class BBB<T> extends AAA<T> { T itemBBB; } OR class BBB extends AAA<String>{ int itemBBB; } OR class BBB<T> extends AAA<String>{ T itemBBB; }
-
제네릭 인터페이스 구현
interface MyInterface<T> { public T myFunc(T item); } class MyImplement<T> implements MyInterface<T> { public T myFunc(T item){ return item; } } OR class MyImplement implements MyInterface<String> { public String myFunc(String item){ return item; } }
-
기본 자료형의 이름은 제네릭 클래스의 인스턴스 생성에 사용될 수 없습니다.
FruitBox<int> fb1 = new FruitBox<int>();
안돼요.
FruitBox<Integer> fb1 = new FruitBox<Integer>();
돼요.