Search
Duplicate

아이템 25.5 - 제네릭

작성자
챕터
5장 - 제네릭
최종 편집
2023/07/22 05:18
생성 시각
2023/07/21 17:43

제네릭 그게 뭔데?

타입에 <T> 로 주어, 타입을 제네릭 하게 사용할 수 있다.
Object 로 쓰는것과 유사한 효과이며, 아래 왜 써야하는지 설명하겠다.

타입 안정성 & 코드 가독성

컴파일 타임에 ClassException 휴먼 에러를 잡을 수 있다
강제 형변환이 필요없다.
코드로 보자

시나리오

세상에는 총과 검 두개의 무기가있다.
내 주머니에는 총 홀스터와 검케이스 두개가 있다
그걸 각각 꺼내는 시나리오다
MyPocket 클래스에서 GunSword 둘 다 꺼낼거다. 따라서 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
복사
StringObject 의 자식이지만 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>
TypeType 의 자손 타입만 가능하도록 제한한다.

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>
TypeType의 부모/조상 타입만 대입 가능하도록 제한한다.
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사용
참고자료