Generics
안들어도 되는분
Generics
1.5 이전
List list = new ArrayList();
list.add("hello");
// get() 메서드가 Object 를 반환하므로 컴파일 에러 발생
// String str = list.get(0);List list = new ArrayList();
list.add("hello");
String str = (String) list.get(0);1.5 이전
List list = new ArrayList();
list.add(30);
// 런타임 예외 발생!
String str = (String) list.get(0);1.5 이전
List list = new ArrayList();
list.add(30);
Object element = list.get(0);
if(element instanceof String){
String str = (String) element;
}1.5 Generics
List<String> list = new ArrayList<String>();
list.add("hello");
String str = list.get(0);제네릭 클래스
class MyList<T>{
private T[] ts;
private int cursor;
@SuppressWarnings("unchecked")
MyList(){
this.ts = (T[]) new Object[10];
}
void add(T t){
this.ts[cursor++] = t;
}
T get(int idx){
return this.ts[idx];
}
}제네릭 클래스
제네릭 메서드
class MyList2{
private Object[] ts;
private int cursor;
MyList2(){
this.ts = new Object[10];
}
<T> void add(T t){
this.ts[cursor++] = t;
}
@SuppressWarnings("unchecked")
<T> T get(int idx){
return (T) this.ts[idx];
}
}제네릭 메서드
MyList2 myList2 = new MyList2();
myList2.add(123);
String str2 = myList2.get(0);제네릭 클래스 VS 제네릭 메서드
제네릭 클래스 VS 제네릭 메서드
// 사용하는 쪽에서 명시적으로 <String> 이라는 타입 아규먼트를 지정해야함
MyList<String> myList = new MyList<String>();
myList.add("hello");
String str = myList.get(0);MyList2 myList2 = new MyList2();
// 제네릭 메서드로 구현되어있지만 사용하는 쪽에서는 제네릭이 적용되어있음을 모름
myList2.add(123);
String str2 = myList2.get(0); Java SE 1.7 Diamond Operator
MyList<String> myList = new MyList<>();타입 소멸(Type Erasure)
타입 소멸(Type Erasure)
Test.MyList var1 = new Test.MyList();
var1.add("hello");
String var2 = (String)var1.get(0);class MyList<T> {
private T[] ts = (Object[])(new Object[10]);
private int cursor;
MyList() {
}
void add(T var1) {
this.ts[this.cursor++] = var1;
}
T get(int var1) {
return this.ts[var1];
}
} 타입 소멸(Type Erasure)
public static <T> void method(){
T t1 = new T();
T t2 = T.newInstance();
T[] ts = new T[10];
} public static <T> void method(Class<T> tClass) throws IllegalAccessException, InstantiationException {
T t = tClass.newInstance();
}공변/불공변/반공변
공변(Covariant)
Object[] objects = new String[10];
Object[] objects = new String[10];
objects[0] = 10;
불공변(Invariant)/반공변(ContraVariant)
// Compile Error
ArrayList<Object> objects = new ArrayList<String>();모든 타입을 받는 제네릭 클래스
public static void method(Object[] objects){
// 배열은 이런식으로 받아주면 된다.
}public static void method(List<Object> list){
// 리스트도 이런식으로 받아주면 될까?
}
// 무변의 특성을 가진 제네릭 클래스이기때문에 컴파일 에러 발생
method(new ArrayList<String>());
method(new ArrayList<Integer>());모든 타입을 받는 제네릭 클래스
public static void main(String[] args) {
method(new ArrayList<String>());
method(new ArrayList<Integer>());
}
public static void method(List list){
} 모든 타입을 받는 제네릭 클래스
와일드카드(Wild Card)
public static void main(String[] args) {
method(new ArrayList<String>());
method(new ArrayList<Integer>());
}
public static void method(List<?> list){
} One more thing
public static void main(String[] args) {
method(new ArrayList<String>());
method(new ArrayList<Integer>());
}
public static <T> void method(List<T> list){
} 타입 파라미터 VS 와일드카드
타입 파라미터 VS 와일드카드
public static <T> T get(List<T> list, int idx){
return list.get(idx);
}타입 파라미터 VS 와일드카드
public static boolean contains(List<?> list, Object object){
return list.contains(object);
}타입 파라미터 VS 와일드카드
public static void reverse(List<?> list){
int last = list.size() - 1;
for(int i = 0, length = list.size() / 2; i <= length; i++){
Object element = list.get(i);
list.set(i, list.get(last - i));
list.set(last - i, element);
}
} 타입 파라미터 VS 와일드카드
public static void reverse(List<?> list){
helper(list);
}
private static <T> void helper(List<T> list){
int last = list.size() - 1;
for(int i = 0, length = list.size() / 2; i <= length; i++){
T element = list.get(i);
list.set(i, list.get(last - i));
list.set(last - i, element);
}
} 타입 파라미터 VS 와일드카드
한정적 타입 파라미터
class ToyList<T>{
private T[] ts;
private int cursor;
@SuppressWarnings("unchecked")
ToyList(){
this.ts = (T[]) new Object[10];
}
void add(T t){
this.ts[cursor++] = t;
}
T get(int idx){
return this.ts[idx];
}
}
interface Toy{}
class Robot implements Toy{}
class Drone implements Toy{} 한정적 타입 파라미터
ToyList<Robot> robots = new ToyList<>();
ToyList<Drone> robots = new ToyList<>();
ToyList<String> robots = new ToyList<>();한정적 타입 파라미터
class ToyList<T extends Toy>{
// ...생략
}
// String은 Toy의 하위 타입이 아니므로 컴파일 에러 발생
ToyList<String> robots = new ToyList<>();공변/반공변
public static <T extends Number> void method(List<T> numbers) {}
method(new ArrayList<Integer>());
method(new ArrayList<Long>());
method(new ArrayList<String>()); // 컴파일 에러PECS
public void sort(Comparator<? super E> c) {}
public boolean addAll(Collection<? extends E> c) {}PECS
PECS
PECS
List<Number> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
list1.addAll(list2); Type Token
class Super<T>{}
class Sub extends Super<String>{}
Sub sub = new Sub();
// 상위 타입의 정보를 구함
ParameterizedType type = (ParameterizedType) sub.getClass().getGenericSuperclass();
// 제네릭 정보를 배열로 얻음
Type[] types = type.getActualTypeArguments();
// 첫번째 타입 파라미터의 이름을 구함
String typeName = types[0].getTypeName(); Type Token
Type Token
public abstract class TypeReference<T> implements Comparable<TypeReference<T>> {
final Type _type;
protected TypeReference() {
Type superClass = this.getClass().getGenericSuperclass();
if (superClass instanceof Class) {
throw new IllegalArgumentException("Internal error: TypeReference constructed without actual type information");
} else {
this._type = ((ParameterizedType)superClass).getActualTypeArguments()[0];
}
}
public Type getType() {
return this._type;
}
public int compareTo(TypeReference<T> o) {
return 0;
}
}참고자료