지네릭스(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) {
...
}
}
Author And Source
이 문제에 관하여(지네릭스(Generics)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@kimsy8979/지네릭스Generics저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)