희렌버핏
13장 제네릭 본문
13.1 왜 제네렉을 사용해야 하는가?
- 제네릭 이란 : 타입을 파라미터와해서 컴파일시 구체적인 타입이 결정되도록 하는 것
- 자바 5부터 추가됨
- 컬렉션, 람다식(함수적 인터페이스), 스트림, NIO에서 널리 사용
- 제네릭을 모르면 도큐먼트 해석 불가
- 제네릭 사용 이점
- 컴파일시 강한 타입 체크 : 컴파일시 미리 타입을 강하게 체크해서 에러 사전 방지
- 타입변환 제거 가능
List list = new ArrayList();
list.add("hello"); //Object 타입으로 자동 타입 변환
String str = (String) list.get(0); //String 타입으로 강제 타입 변환
List<String> list = new ArrayList<String>(); //List에 String 타입만 저장하겠다.
list.add("hello"); //string 값만 줘야 되고 string 타입으로 저장된다.
String str = list.get(0);
13.2 제네릭 타입 (class<T>, interface<T>)
- 제네릭 타입이란
- 타입을 파라미터로 가지는 클래스와 인터페이스를 말한다.
- 선언시 클래스 또는 인터페이스 이름 뒤에 "<>" 부호가 붙는다.
- "<>" 사이에는 타입 파라미터가 위치한다.
public class 클래스명<T> {...}
public interface 인터페이스명<T> {...}
- 타입 파라미터
- 일반적으로 대문자 한개로 표현됨
- 개발 코드에서는 타입 파라미터 자리에 구체적인 타입 지정 필요
- 제네릭 타입을 사용할 경우의 효과
* 비제네릭 : Object 타입을 사용해서 빈번한 타입 변환 발생 -> 성능 저하
public class Box {
private Object object; //모든 자바 클래스의 최상위 부모 클래스
public void set(Object object) {this.object = object;} //매개 변수의 클래스 뿐만 아니라 자식 객체도 들어갈 수 있다. 모든 객체를 매개값으로 받아서 필드에 받을 수 있다. 최상위Object로 되어 있기때문에.
public Object get() {return object} //Object 타입으로 리턴
}
Box box = new Box( );
box.set("hello"); //String 타입을 Object 타입으로 자동 타입 변환해서 저장
String str = (String) box.get(); //Object 타입을 String 타입으로 강제 타입 변환해서 얻음
* 제네릭 : 클래스를 선언할 때 타입 파라미터로 기술. 컴파일시 작성한 타입 파라미터가 구체적인 클래스로 변환된다.
=> 다양한 객체를 받아올 수 있고, 타입 변환도 하지 않아도 된다.
public class Box<T> {
private T t;
public T get() {return t;}
public void set(T t) {this.t = t;}
}
Box <String> box = new Box <String> ();
box.set("hello"); //String 아닌 것이 들어오면 에러남
String str = box.get();
13.3 멀티 타입 파라미터 (class<K,V,...>, interface<K,V,...>)
- 두개 이상의 타입 파리미터 사용 선언 가능
- class<K, V, ...> {...}
- interface<K,V,...> {...}
public class Product <T,M> {
private T kind;
private M model;
public T getKind() {return this.kind;}
public M getModel() {return this.model;}
public void setKind(T kind) {this.kind = kind;}
public void setModel(M model) {this.model = model;}
}
- 중복된 타입 파라미터를 생략한 다이아몬드(<>) 연산자
Product<Tv, String> product = new Product<> ( );
13.4 제네릭 메소드(<T,R> R method(T t))
- 매개변수 타입과 리턴 타입으로 타입 파라미터를 갖는 메소드
- 선언 방법
public <타입파라미터,...> 리턴타입 메소드명(매개변수,...) {...}
public <T> Box<T> boxing(T t) {...}
- 제네릭 메소드를 호출하는 두가지 방법
Box <Integer> box = <Integer> boxing(100); //타입 파라미터를 명시적으로 Integer로 지정
Box <Integer> box = boxing(100); // 매개값을 보고 타입 파라미터를 Integer로 추정
13.5 제한된 타입 파라미터(<T extends 최상위타입>)
- 타입 파라미터에 구체적인 타입을 제한할 필요가 있을 경우
- 상속 및 구현 관계를 이용해서 타입 제한
public <T extends 상위타입> 리턴타입 메소드 (매개변수, ...) {...}
* 상위 타입은 클래스, 인터페이스 다 됨. 인터페이스는 implements가 아니라 extends를 쓴다.
- 주의
- 중괄호 안에는 파라미터에 지정한 클래스 안에 있는 필드와 메소드만 사용 가능
- 하위 타입에만 있는 필드와 메소드는 사용할 수 없다.
13.6 와일드 카드 타입 (<?>,<? extends ...>, <? super ...>)
- 제네릭 타입을 매개변수나 리턴타입으로 사용할 때 타입 파라미터를 제한할 목적
1. 제네릭타입<?> : Unbounded Wildcards (제한없음)
- 모든 클래스, 인터페이스 타입이 올 수 있음
2. 제네릭타입<? extends 상위타입> : Upper Bounded Wildcards (상위 클래스 제한)
- 상위 타입이거나 하위 타입만 올 수 있다.
3. 제네릭타입<? super 상위타입> : Lower Bounded Wildcards (하위 클래스 제한)
- 지정한 상위 클래스, 자유롭게 올 수 있음
13.7 제네릭 타입의 상속과 구현
- 제네릭 타입을 부모 클래스로 사용할 경우 : 부모도 동일하게 제네릭, 자식은 추가 파라미터 가질 수 있다.
public class ChildProduct<T,M,C> extends Product<T,M> {...}
- 제네릭 인터페이스를 구현할 경우 : 구현클래스에도 타입 파라미터 기술
pulic class StorageImpl <T> implements Storage<T> {...}