지네릭스(Generics)

지네릭스

  • 컴파일 시, 타입을 체크해주는 기능 : compile-time type check

  • 객체의 타입 안정성 제공. (Class CastException 방지)

  • 타입 체크와, 형변환을 생략할 수 있으므로, 코드가 간결해짐.

  • 런타임 에러, 실행 중 발생 에러를 어떻게 Compile-time error 로 바꿀 수 있을까? 에서 착안하여 만든게 지네릭스임. 지네릭스로, ClassCastException(Runtime error)을 compile-time error로 끌고온것임.

// Tv 객체만 저장할 수 있는 ArrayList 생성
ArrayList<Tv> tvList = new ArrayList<Tv>();	// 타입변수 E 대신에, 실제 타입 Tv 대입

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

// 장점
ArrayList tvList = new ArrayList();
tvList.add(new Tv());
Tv t = (Tv) tvList.get(0);	// Tv - Object 간 타입 불일치로, 형변환 필요

// ↓ 아래와 같이 간소화 가능
ArrayList<Tv> tvList = new ArrayList<Tv>();
tvList.add(new Tv());
Tv t = tvList.get(0);  // 형변환 불필요 : 반환타입이 Tv 이기 때문. (타입 일치)

타입 변수

  • 지네릭 클래스 작성 시, Object 타입 대신, 타입 변수(E)를 선언하여 사용.

  • Object를 포함한 클래스는 대부분 지네릭 클래스로 변경되었음.

  • 객체 생성 시, 타입변수 E 대신, 실제 타입을 지정해줘야 함(대입)

  • 타입변수 대신, 실제 타입이 지정되면, 형변환 생략이 가능하다.


지네릭스 용어

  • Box<T> : 지네릭 클래스. T의 Box 또는, T Box 라고 읽는다. "타입변수 T 선언"

  • T : 타입 변수 또는 타입 매개변수. (T는 타입 문자)

  • Box : 원시 타입 (raw type). 일반 클래스일 떄의 타입. 원래 타입.

// 지네릭 클래스 선언
class Box<T> {}

//    ↓   지네릭 타입 호출   ↓  : 타입이 서로 일치해야 함.
Box<String> b = new Box<String>();
//	 ↑ 대입된 타입 (매개변수화 된 타입) : 생성할 때마다, 다른 타입을 넣을 수 있음.

지네릭 타입의 다형성

  • 참조변수와 생성자의 대입된 타입은 일치해야 함.
//    ↓ 지네릭 타입 호출 ↓  : 타입이 서로 일치해야 함.
Box<조상> b = new Box<자손>(); 	// 이렇게는 안됨! 에러남!! 반드시 일치해야 됨.
  • 지네릭 클래스 간의 다형성은 성립. (대입된 타입은 반드시 일치)
List<Tv> list = new ArrayList<Tv>();	// OK. 다형성. ArrayList가 List 구현

List<Tv> list = new LinkedList<Tv>();	// OK. 다형성. LinkedList가 List 구현
  • 매개변수의 다형성도 성립
class Product {}
class Tv extends Product {}
class Audio extends Product {}

ArrayList<Product> list = new ArrayList<Product>();

list.add(new Product());
list.add(new Tv());		// Product의 자손도 add 가능.!
list.add(new Audio());

// add method 상황 설명
boolean add (E e) {...} 
-> 
boolean add(Product e) {...}
//		       ↑ 다형성에 의해, Product와 그 자손 객체가 오는 것이 가능하다.


// get method도 같다
E get(int index) {...}
->
Product get(int index) {...}	// get의 반환 타입이 Product임.

Product p = list.get(0);
Tv t = (Tv) list.get(1);	// Product를 반환하므로, Tv로 형변환 필수

Iterator<E>

  • 클래스를 작성할 때, Object 타입 대신 T와 같은 '타입 변수'를 사용
public interface Iterator<E> {
	boolean hasNext();
    E next();	// 원래 iterator는 object를 반환해서, 형변환이 필요했음
    void remove();
}

HashMap<K, V>

  • 여러 개의 타입 변수가 필요한 경우, 콤마(,)를 구분자로 선언.
//		  key	 value
HashMap<String, Student> map = new HashMap<String, Student>();
map.put("자바왕", new Student("자바왕",1,1,100,100,100)); 

제한된 지네릭 클래스

  • extends를 이용해서, 대입할 수 있는 타입을 제한할 수 있다.

  • 인터페이스의 경우에도 extends를 사용해야 한다.

// 인터페이스도 타입 제한에서는 extends를 씁니다.
interface Eatable {}
class FruitBox<T extends Eatable> {}

class FruitBox<T extends Fruit> {	// Fruit의 자손 type만 대입 가능
	ArrayList<T> list = new ArrayList<T>();
    ...
}

FruitBox<Apple> appleBox = new FruitBox<Apple>(); // OK
FruitBx<Toy>	toyBox = new FruitBox<Toy>();	  // Error. Toy는 Fruit의 자손이 아님.

지네릭스의 제약

  • 타입 변수에 대입은, 인스턴스 별로 다르게 할 수 있다.

    Box<Apple> appleBox = new Box<>(); // Apple 객체만 저장 가능
    Box<Grape> grapeBox = new Box<>(); // Grape 객체만 저장 가능

  • Static 멤버에 타입 변수 사용 불가
    Static : 모든 인스턴스의 공통이므로, 인스턴스마다 다르게 설정할 수 있는 타입변수를 사용할 수 없다.

  • 객체 혹은 배열 생성 시, 타입 변수 사용 불가. (new 연산자는 뒤에 오는 타입이 "확정"되어있어야한다. T는 확실하지 않기 때문에 사용 불가! = new 연산자 다음에는 T를 사용할 수 없다!)

  • 타입 변수로 배열 선언은 가능.

class Box<T> {
	static T item;  // 에러
    static int compare(T t1, T t2) { ... } // 에러
}

class Box<T> {
	T[] itemArr;	// OK. T 타입 배열을 위한 참조 변수
    ...
    T[] toArray() {
    	T[] tmpArr = new T[itemArr.length];	// 에러. 지네릭 배열 생성불가
    }
}

와일드카드 <?>

  • 하나의 참조 변수로, 대입된 타입이 다른 객체를 참조할 수 있음

  • < ? extends T > : 와일드 카드의 상한 제한. T와 그 자손들만 가능 -> 주로 이걸 많이 씀!

  • < ? extends T > : 와일드 카드의 하한 제한. T와 그 조상들만 가능

  • < ? > : 제한 없음. 모든 타입이 가능. < ? extends Object >와 동일함

  • 메서드의 매개변수에 와일드카드 사용 가능

// 대입된 타입이 일치하지 않아도 ok
// 참조변수 하나로 대입된 타입이 여러개가 될 수 있도록 유연하게 만들어줌
ArrayList<? extends Product> list = new ArrayList<Tv>();  // ok
ArrayList<? extends Product> list = new ArrayList<Audio>();  // ok

ArrayList<Product> list = new ArrayList<Tv>();  // 에러. 대입된 타입 불일치

// ex
static Juice makeJuice(FruitBox<? extends Fruit> box) { ... }

// 둘다 가능
Juicer.makeJuice(new FruitBox<Fruit>()));
Juicer.makeJuice(new FruitBox<Apple>()));

지네릭 메서드

  • 지네릭 타입이 선언 된 메서드 (타입 변수는 메서드 내에서만 유효)
    static < T > void sort(List< T > list, Comparator< ? super T > c)

  • 클래스의 타입 매개변수 < T >와 메서드의 타입 매개변수 < T >는 별개이다.

class FruitBox<T> {
	...
	static <T> void sort(List<T> list, Comparator<? super T> c) {
	...
	}
}

좋은 웹페이지 즐겨찾기