자바의 제네릭에 대해 학습하세요.
클래스나 메서드에서 사용할 내부 데이터 타입을 컴파일 시에 미리 지정하여 일반화 하는 것을 의미한다.
static class Champion {
Object passive;
public Object getPassive() {
return passive;
}
public void setPassive(Object passive) {
this.passive = passive;
}
}
위의 코드는 Object 타입을 가지는 클래스이다. 이러한 클래스를 제네릭을 이용해서 바꿔보겠다.
static class Champion <T> {
T passive;
public T getPassive() {
return passive;
}
public void setPassive(T passive) {
this.passive = passive;
}
}
보는거와 같이 Object가 T로 모두 치환되었다. T는 임의 참조형 변수로써 타입 변수라고 하며 실질적인 자료형을 나타내기보다 임의의 참조형 타입이라는 뜻이다.
//Object일 경우
public static void main(String[] args) {
Champion champion = new Champion();
champion.setPassive("패시브");
String str = (String) champion.getPassive();
System.out.println(str);
}
//제네릭 T일 경우
public static void main(String[] args) {
Champion<String> champion = new Champion<>();
champion.setPassive("패시브");
String str = champion.getPassive();
System.out.println(str);
}
위의 클래스를 둘다 실행해보면 getPassive()를 하는 과정에서 타입변환에 차이를 볼 수 있다. 이처럼 제네릭은 자동 타입변환이 된다는걸 확인할 수 있다.
T : 타입
E : 요소(element,컬렉션에서 주로 사용된다)
K : 키
V : 값
N : 숫자
S, U, V : 두번째,세번째,네번째 선언된 타입
제네릭 타입에 extends
키워드를 사용하면 특정 타입의 자손들만으로 타입을 제한 할 수 있다.
class FruitBox<T extends Fruit> extends Box{
}
과일만 담을 수 있는 박스가 있을때 만약 과일이 아닌 다른 장난감과 같은 클래스를 담으려할때 타입제한을 할 수 있다.
제네릭으로 구현된 메서드의 경우 선언된 타입으로만 매개변수를 입력해야 한다.
이와 상속관계에 있는 타입을 사용하려해도 불가능하다. 이러한 문제를 해결하는 것이 와일드 카드이다.
Unbounded WildCard
List<?> 와 같은 형태로 물음표를 이용해 정의하는 방법이다.
물음표는 내부적으로 Object로 정의되어 사용되어 모든 타입의 파라미터를 받을 수 있다.
Upper Bounded WildCard
List<? extends Parents>와 같은 형식으로 작성되며 보기와 같이 특정타입의 자식 클래스만 인자로 받을 수 있다.
Lower Bounded WildCard
List<? super Child>로 작성되며 특정 타입의 부모타입만 인자로 받겠다는 뜻이다.
제네릭 메서드는 매개 타입과 리턴 타입으로 타입 파라미터를 갖는 메서드를 말한다.
제네릭 메서드를 선언하는 방법은 리턴 타입앞에 <>를 이용하여 타입 파라미터를 기술한 다음 리턴 타입과 매개 타입으로 사용하면 된다.
// 정의
public <타입파라미터> 리턴타입 메서드명(매개변수){
}
//사용하는 방법
리턴타입 변수 = <구체적인 타입>메서드명(매개값);
리턴타입 변수 = 메서드명(매개값);
아래는 boxing이라는 메서드를 만들었다. 코드를 보면 T타입을 전달받아 Box 객체의 Type에 대입하는 메서드이다.
메서드를 실행하는 부분을 보면 형변환없이 대입되는걸 확인할 수 있다.
public class Box<T> {
T type;
public T getType() {return type;}
public void setType(T type) {this.type = type;}
}
// 제네릭메서드 정의하는 코드
public class Util {
public static <T> Box<T> boxing(T t){
Box<T> box = new Box<>();
box.setType(t);
return box;
}
}
//실행하는 코드
public class Main {
public static void main(String[] args) {
Box<Integer> numBox = Util.boxing(10);
int result = numBox.getType();
Box<String> strBox = Util.<String>boxing("str");
String str = strBox.getType();
}
}
Type Erasure은 제네릭에 사용된 타입 파라미터들이 컴파일이 되는 과정에서 검증을 마치고 나면 필요가 없어지기에 런타임시 타입 파라미터를 지우는 것을 말한다.
// 런타임 전
public class Box<T> {
T type;
public T getType() {return type;
}
public void setType(T type) {
this.type = type;
}
}
// 런타임 시
public class Box {
Object type;
public Object getType() {return type;
}
public void setType(Object type) {
this.type = type;
}
}
타입파라미터 T가 Object로 변환되는 것을 볼 수있다. 이처럼 타입파라미터가 변환되는 것을 말한다.