제네릭 그게 뭔데?
타입에 <T> 로 주어, 타입을 제네릭 하게 사용할 수 있다.
Object 로 쓰는것과 유사한 효과이며, 아래 왜 써야하는지 설명하겠다.
타입 안정성 & 코드 가독성
•
컴파일 타임에 ClassException 휴먼 에러를 잡을 수 있다
•
강제 형변환이 필요없다.
•
코드로 보자
시나리오
•
세상에는 총과 검 두개의 무기가있다.
•
내 주머니에는 총 홀스터와 검케이스 두개가 있다
•
그걸 각각 꺼내는 시나리오다
•
MyPocket 클래스에서 Gun과 Sword 둘 다 꺼낼거다.
따라서 Sword , Gun 둘 다 하나의 메소드로 꺼낼거다. getWeapon
따라서, 매개변수를 Gun, Sword 상위 타입인 Object 로 선언했다.
공통
class Gun {
void shot(){
System.out.println("쏴");
}
}
class Sword {
}
class MyPocket{
private Object weapon;
public MyPocket(Object weapon) {
this.weapon = weapon;
}
public Object getWeapon() {
return weapon;
}
}
Java
복사
Object Class 사용
public class TypeSafeTest {
public static void main(String[] args) {
MyPocket gunHolster = new MyPocket(new Gun());
MyPocket swordCase = new MyPocket(new Sword());
Gun gun = (Gun) gunHolster.getWeapon();
Sword sword = (Sword) gunHolster.getWeapon(); // 런타임 에러
}
}
Java
복사
•
Object 를 썼을때 ⇒ 런타임 에러
•
건 홀스터에서 검을 꺼낸다고 선언했으니, 에러가 난다 하지만 런타임 에러
Exception in thread "main" java.lang.ClassCastException:
class test.generic.typesafe.Gun cannot be cast to class test.generic.typesafe.Sword (test.generic.typesafe.Gun and test.generic.typesafe.Sword are in unnamed module of loader 'app')
Java
복사
Generic Class 사용
class MyDoramonPocket<T>{
private T weapon;
public MyDoramonPocket(T weapon) {
this.weapon = weapon;
}
public T getWeapon() {
return weapon;
}
}
public class TypeSafeTest {
public static void main(String[] args) {
MyDoramonPocket<Gun> gunHolster = new MyDoramonPocket<Gun>(new Gun());
MyDoramonPocket<Sword> swordCase = new MyDoramonPocket<Sword>(new Sword());
Gun gun = gunHolster.getWeapon();
Sword sword = gunHolster.getWeapon();
}
Java
복사
⇒ 컴파일 타임에 에러가 잡힌다
⇒ 강제 형변환 필요가 없다
메서드에서 사용한 제네릭
•
사용 방법은 class 와 같이 <T> 를 붙여준다
public class GenericMethods {
class Printer<T> {
public <T> void printClassType(T e) {
System.out.println(e.getClass().getSimpleName());
}
}
public static void main(String[] args) {
GenericMethods m = new GenericMethods();
Printer<String> printer = m.new Printer<String>();
printer.printClassType(1234);
printer.printClassType(3.14);
printer.printClassType("HI");
}
}
Java
복사
// 실행결과
Integer
Double
String
Java
복사
•
제네릭 메소드의 타입 매개변수가 같다면 제네릭 메소드의 타입 매개변수를 우선한다.
◦
Printer 는 String 선언했지만, 사용되는 메소드에서 Integer Double 로 사용된다.
? WildCard 와일드카드
List<Object> 타입을 사용한 매개변수
public class WildCard {
public static void printing(List<Object> list){
for (Object object : list) {
System.out.println(object);
}
System.out.println();
}
public static void main(String[] args) {
List<Object> objectList = new ArrayList<>();
objectList.add("a");
objectList.add("b");
objectList.add("c");
printing(objectList);
List<String> stringList = new ArrayList<String>();
printing(stringList); // 컴파일 에러
//java: incompatible types: java.util.List<java.lang.String> cannot be converted to java.util.List<java.lang.Object>
}
}
Java
복사
•
String 은 Object 의 자식이지만
List<String> 은 List<Object>의 자식이 아니다.
•
따라서 매개변수가 안맞는다.
List<Object> 에 List<String> 은
Integer 매개변수에 String 을 넣은것과 다를바 없는것
List<?> 와일드카드 타입을 사용한 매개변수
public static void printingWithWild(List<?> list){
for (Object object : list) {
System.out.println(object);
}
System.out.println();
}
public static void main(String[] args) {
List<String> stringList = new ArrayList<String>();
stringList.add("x");
stringList.add("y");
stringList.add("z");
printingWithWild(stringList);
}
Java
복사
•
List<String> 혹은 List<T> 는 List<?> 의 자식이다.
•
제네릭 계의 Object 인 셈이다.
정확히는
몰?루 타입 혹은 모든 타입이라고 한다.
•
List<?> 에서 get 한 객체는 Object 다
⇒ ? 는 최상위 이기때문에
제한된 제네릭
상한 경계 제네릭 upper bound
<T extends Type>
Type 과 Type 의 자손 타입만 가능하도록 제한한다.
with Wildcard
<? extends Type>
Type 의 자손타입을 보장하나 어떤 타입인지 알 수 없다.
<T> SimpleList<T> filterNegative(SimpleList<? extends Number> simpleList){
SimpleList<T> tempList = new SimpleArrayList<>();
for (int i = 0; i < simpleList.size(); i++) {
if (simpleList.get(i).doubleValue() >= 0)
tempList.add((T)simpleList.get(i));
}
return tempList;
}
Java
복사
•
simpleList 는 read 만 가능 Number의 하위 타입 가능 ⇒ Integer, Double
•
tempList 는 T 이면 add 가 가능
하한 경계 제네릭 lower bound
<? super Type>
Type 과 Type의 부모/조상 타입만 대입 가능하도록 제한한다.
class Printer { }
class LaserPrinter extends Printer{}
List<Printer> printers = new ArrayList<>();
LaserPrinter laserPrinter = new LaserPrinter();
List<LaserPrinter> laserPrinters = new ArrayList<>(laserPrinter);
~.copy(laserPrinters, printers);
void copy(List<? extends T> laserPrinters, List<? super T> printers){
for (int i = 0; i < laserPrinters.size(); i++)
printers.add(laserPrinters.get(i));
}
Java
복사
하한과 상한을 모두 사용한 예제
하한에서는 T (Printer 클래스)의 add 사용
상한에서는 T (LaserPrinter 클래스 ) get사용
•
참고자료