13-Generics
generics
제네릭은 클래스와 인터페이스, 그리고 메소드를 정의할 때 타입을 파라미터로 사용할 수 있도록 한다. 그리고 타입 파라미터는 코드 작성시 구체적인 타입으로 대체되어 다양한 코드를 생성하도록 해준다.
-
미리 사용 데이터 타입을 알려주고, 컴파일러가 타입을 체크해서 잘못된 형변환을 실행시 줄여준다.
Arraylist<A> HashMap<B, D> HashSet<C> // < > 꺽쇠 안의 타입만 쓰겠다는 선언
제네릭 타입(class, interface)
- object객체는 최상위 객체로, 모든 자바객체는 object 타입으로 자동 타입 변환이 가능하다.
- 타입 파라미터는 일반적으로 대문자 알파벳 한 글자로 표현한다.
public calss Box {
private Object object;
public void set(Object object) { this.object = object; }
public Object get() { return object; }
}
Box box = new Box();
box.set("hello");
String str = (String)box.get(); // Object 타입을 String 타입으로 강제 변환해서 값을 얻음
Object 타입을 사용하면 모든 종류의 자바객체 저장이 가능하지만, 젖아할 때 타입 변환이 발생, 읽을 때도 타입 변환이 발생한다. 다음은 제네릭을 이용해 수정한 결과이다.
//Object 타입을 모두 T로 대체
public class Box<T> {
privat T t;
public T get() { return t; }
public void set(T t) { this.t = t; }
}
//T는 Box 클래스로 객체를 생성할 때 구체적인 타입으로 변경된다.
Box<String> box = new Box<String>();
*/ 결과:
public class Box<String> {
privat String t;
public String get() { return t; }
public void set(String t) { this.t = t; }
}
/*
-
generic에는 참조형 변수만 올 수 있다. 즉 기본형 변수는 올 수 없다.
-
기본형 변수는 wrapper class를 이용: ex)int - Integer, double - Double
-
int나 double 타입의 경우 Integer와 Double이 온다.
EmployeeInfo e = new EmployeeInfo(1); Integer i = new Integer(10); Person<EmployeeInfo, Integer> p1 = new Person<EmployeeInfo, Integer>(e, i); ---- // 그러나 이는 다음과 같이 짧아질 수 있다. person p1 = new person(e, i);
-
example
package generic;
class Apple{
String origin = "대구";
}
class Paper{
String size = "A4";
}
// < > 는 어떤 타입이든 저장 가능, 호출시 지정 가능.
class Box<T>{
T o;
Box(T o){
this.o = o;
}
public T getO() {
return o;
}
public void setO(T o) {
this.o = o;
}
}
public class GenericTest {
public static void main(String[] args) {
Apple a = new Apple();
Paper p = new Paper();
Box<Apple> appleBox = new Box<Apple>(a);
Box<Paper> paperBox = new Box<Paper>(p);
System.out.println(appleBox.getO().origin);
System.out.println(paperBox.getO().size);
// 대구, 사과 출력
}
}
-
generic은 클래스 뿐 아니라 메소드에서도 쓸 수 있다.
public <U> void printInfo(U info){ System.out.println(info); }
example
package generic1;
class Apple{
String origin = "대구";
}
class Paper{
String size = "A4";
}
class Box<T, K>{
T t1;
K k1;
String name = "상자";
Box(T t1, K k1){ // 어떤 타입이든 저장 가능!
this.t1 = t1;
this.k1 = k1;
}
public T gett1() {
return t1;
}
public K getk1() {
return k1;
}
public void setT1(T t1) {
this.t1 = t1;
}
public void setK1(K k1) {
this.k1 = k1;
}
}
class BoxManager{
// 제네릭 메소드
public <P1, P2> Box<P1, P2> test(P1 a, P2 p) {
//Box<Apple, Paper> box2 = new Box<Apple, Paper>(a, p); // 타입파라미터가 없으면 엉뚱한 데이터가 와도 체킹이 안 됨.
Box<P1, P2> box2 = new Box<P1, P2>(a, p);
return box2;
}
}
public class GenericTest {
public static void main(String[] args) {
Apple a = new Apple();
Paper p = new Paper();
Box<Apple, Paper> box = new Box<Apple, Paper>(a, p);
System.out.println(box.gett1().origin);
System.out.println(box.getk1().size);
BoxManager manager = new BoxManager();
Box<Apple, Paper> box2 = manager.test(a, p);
}
}
제한된 타입 파라미터
- generic을 사용하며 object를 활용하게 되면, 모든 객체가 오기 때문에 제어의 필요성이 있다.
바로 이런 경우, extends를 활용하면 < >에 올 수 있는 변수로 사용 가능한 것은 상위 타입의 멤버(필드, 메소드)와 상위 타입의 자식들로 제한된다. 객체 데이터를 제한할 수 있다.
와일드카드 타입 ?
- 제네릭타입<?> : 제한 없음
- 타입 파라미터를 대치하는 구체적인 타입으로 모든 클래스나 인터페이스 타입이 올 수 있다.
- 제네릭타입<? extends 상위타입>: 상위 클래스 제한
- 타입 파라미터를 대치하는 구체적인 타입으로 상위 타입이나 하위 타입만 올 수 있다.
- 제네릭타입<? extends 하위타입>: 하위 클래스 제한
- 타입 파라미터를 대치하는 구체적인 타입으로 하위 타입이나 상위 타입만 올 수 있다.
package generic2;
class Fruit {
String name = "과일";
}
class Apple extends Fruit{
String origin;
Apple(String name, String origin){
this.name = name;
this.origin = origin;
}
}
class Orange extends Fruit{
String imported;
Orange(String name, String imported){
this.name = name;
this.imported = imported;
}
}
class Paper {
String size = "A4";
}
class Box<T extends Fruit> {
//멤버변수 생성자 메소드 순서상관 없음! 그러나, 박스 안에 있어야 함.
T o;
Box(T o){
this.o = o;
}
public T getO() {
return o;
}
public void setO(T o) {
this.o = o;
}
}
class BoxManager{
public void test(Box<? extends Fruit> b){ //전달
System.out.println(b.getO().name);
}
}
public class GenericTest {
public static void main(String[] args) {
Fruit f= new Fruit();
Apple a = new Apple("사과", "대구");
Orange o = new Orange("오렌지", "미국");
Paper p = new Paper();
Box<Apple> box1 = new Box<Apple>(a);
Box<Orange> box2 = new Box<Orange>(o);
Box<Fruit> box3 = new Box<Fruit>(f);
// extends 제한에 걸림 Box<Paper> box4 = new Box<Paper>(p);
BoxManager m = new BoxManager();
m.test(box1);
m.test(box2);
m.test(box3);
}
}