Java - Generics

iseon_u·2022년 5월 22일
0

Java

목록 보기
61/77
post-thumbnail

Generics 지네릭스


  • 컴파일시 타입을 체크해 주는 기능 (compile-time type check) - JDK 1.5
  • 런타임 에러 (ClassCastException) 를 방지하고 컴파일 에러에서 해결 가능하게 도와준다.
  • 지네릭스를 사용해야 좋은 코드
  • 객체의 타입 안정성을 높이고 형변환의 번거로움을 줄여준다. - 코드가 간결해진다
ArrayList<Tv> tvList = new ArrayList<Tv>();

tvList.add(new Tv()); // OK
tvList.add(new Audio()); // 컴파일 에러. Tv 외에 다른 타입은 저장 불가

ArrayList<Object> list = new ArrayList<Object>(); // 다양한 타입도 저장 가능

타입 변수

public class ArrayList<E> extends AbstractList<E> {
		public boolean add(E o) {}
	// 생략
}
  • 지네릭 클래스를 작성할 때, Object 타입 대신 타입 변수 (E) 를 선언해서 사용
// 타입 변수 E 대신에 실제 타입 Tv 를 대입
ArrayList<Tv> tvList = new ArrayList<Tv>();
  • 객체를 생성시, 타입 변수 (E) 대신 실제 타입을 지정 (대입)
ArrayList tvList = new ArrayList();
tvList.add(new Tv());
Tv t = (Tv)tvList.get(0);
// 형변환 필요 Object -> Tv
ArrayList<Tv> tvList = new ArrayList<Tv>();
tvList.add(new Tv());
Tv t = tvList.get(0);
// 형변환 불필요
  • 타입 변수 대신 실제 타입이 지정되면, 형변환 생략 가능

지네릭스 용어

💡
Box - 지네릭 클래스, T의 Box 또는 T Box 라고 읽는다.
T - 타입 변수 또는 타입 매개 변수 (T 는 타입 문자)
Box - 원시 타입 (raw type)

  • 생성할 때마다 다른 타입 대입 가능

지네릭 타입과 다형성

ArrayList<Tv> list = new ArrayList<Tv>(); // ✅ OK, 일치
ArrayList<Product> list = new ArrayList<Tv>(); // ❌에러, 불일치
  • 참조 변수와 생성자의 대입된 타입은 일치해야 한다.
  • 조상 자손 관계도 불가능
List<Tv> list = new ArrayList<Tv>(); // ✅ OK, ArrayList 가 List 구현
List<Tv> list = new LinkedList<Tv>(); // ✅ OK, LinkedList 가 List 구현
  • 지네릭 클래스간의 다형성은 성립
ArrayList<Product> list = new ArrayList<Product>();
list.add(new Product());
list.add(new Tv());
list.add(new Audio());

HashMap<K,V>

  • 여러 개의 타입 변수가 필요한 경우, , (콤마) 를 구분자로 선언

지네릭 타입 생략

HashMap<String, Student>map = new HashMap<>();
  • JDK 1.7 부터 생성자의 지네릭 타입 생략 가능

제한된 지네릭 클래스

  • extends 로 대입할 수 있는 타입을 제한
  • 해당하는 자신과 자손만 대입 가능
  • 인터페이스인 경우에도 extends 를 사용 (여러 인터페이스 & 로 구분)
class FruitBox<T extends Fruit> { // Fruit 의 자손만 타입으로 지정 가능
		ArrayList<T> list = new ArrayList<T>();
}
FruitBox<Apple> appleBox = new FruitBox<Apple>();
FruitBox<Toy> toyBox = new FruitBox<Toy>(); // ❌에러, Toy 는 Fruit 의 자손이 아니다.

지네릭스의 제약

  • 타입 변수에 대입은 인스턴스 별로 다르게 가능
  1. static 멤버에 타입 변수 사용 불가
  2. 배열 생성할 때 타입 변수 사용 불가, 타입 변수로 배열 선언은 가능

와일드 카드 <?>

ArrayList<? extends Product> list = new ArrayList<Tv>();
ArrayList<? extends Product> list = new ArrayList<Audio>();
ArrayList<Product> list = new ArrayList<Tv>(); // ❌에러, 대입된 타입 불일치
  • 하나의 참조 변수로 대입된 타입이 다른 객체를 참조 가능
💡 `` : 와일드 카드의 상한 제한, T 와 그 자손들만 가능 `` : 와일드 카드의 하한 제한, T 와 그 조상들만 가능 `` : 제한 없음, 모든 타입이 가능, `` 와 동일
static Juice makeJuice(FruitBox<? extends Fruit> box) {
		String tmp = "";
		for(Fruit f : box.getList()) tmp += f + " ";
		return new Juice(tmp);
}
makeJuice(new FruitBox<Fruit>())
makeJuice(new FruitBox<Apple>())
  • 메서드의 매개 변수에 와일드 카드 사용

지네릭 메서드

static <T> void sort(List<T> list, Comparator<? super T> c)
  • 지네릭 타입이 선언된 메서드 (타입 변수는 메서드 내에서만 유효)
class FruitBox<T> { // 타입 문자 일치하지만 다른 타입 변수
		static <T> void sort(){} // 타입 문자 일치하지만 다른 타입 변수
}
  • 클래스의 타입 매개변수 와 메서드의 타입 매개변수 는 별개
  • iv, lv 가 이름이 같은 경우와 동일
<Fruit>makeJuice();
<Apple>makeJuice();
  • 메서드를 호출할 때마다 타입을 대입
profile
🧑🏻‍💻 Hello World!

0개의 댓글