TypeReference 관련해서 찾아보다가 Generic 부터 제대로 알지 못하고 있다는 느낌을 받게 되었고, 기본적인 것 부터 다시 학습해보고 정리한다.
솔직히 면접 질문에 간단히 답변할 수 있는 수준으로만 알고있었다.
제네릭 타입에서는 매개변수화 타입(Parameterized type)들을 정의한다.
Type parameter (타입 매개 변수) : <>
꺽쇠 괄호 내부에 있는 타입
List<String>
의 String은 실 타입 매개 변수Raw Type : 제네릭 타입에서 타입 매개변수를 전혀 사용하지 않았을 때
public class Example<T> {
private T member;
public void main(){
Example<Integer> parameterType = new Example<>(1);
Example rawType = new Example(1);
}
}
Type eraser (타입 소거자) : 컴파일 타임에만 타입에 대한 제약 조건을 적용하고, 런타임에는 타입에 대한 정보를 소거한다. 자세한 내용은 다음에
Reifiable Type (실체화 타입) : 컴파일 단계에서 타입소거에 의해 지워지지 않는 타입 정보
List<?>, ArrayList<?>
등 비한정 와일드카드가 포함된 매개변수화 타입Non-Reifiable Type (비 실체화 타입) : 타입소거에 의해서 타입 정보가 제거된 타입으로, 제네릭 타입 파라미터는 모두 제거 된다.
List<T>, List<Number>, ArrayList<String>, List<? extends Number>
<T>
<? extends T>
<? super T>
public static <T> int countGreaterThan(T[] anArray, T elem) {
int count = 0;
for (T e : anArray)
if (e > elem) // compiler error
++count;
return count;
}
public static <T extends Comparable<T>> int countGreaterThan(T[] anArray, T elem) {
int count = 0;
for (T e : anArray)
if (e.compareTo(elem) > 0)
++count;
return count;
}
Price<Integer>
IS A Price<Number>
가 성립하지 않는다.public void addPrice(Price<Number> price) { }
addPrice(new Price<Integer>(3));
public void addPrice(Price< ? extends Number> price) { }
addPrice(new Price<Integer>(3)); // O
super type token 내용을 읽다가
Class<T>
에 대해 정확히 알지 못해 Reflection에 대한 내용도 추가로 공부했다.
// Class 객체를 얻는 방법
Class<Price> clazz = Price.class; //class property를 사용한 방법
Price price = new Price();
Class<? extends Price> priceClass = price.getClass(); // 인스턴스의 getClass() 메소드 사용
// 모든 public 요소를 가져옴
price.getFields();
clazz.getMethods();
clazz.getAnnotations();
// 상속받은 클래스와 인터페이스를 제외하고 해당 클래스에 직접 정의된 모든 요소에 접근
clazz.getDeclaredFields();
clazz.getDeclaredMethods();
clazz.getDeclaredAnnotations();
// 동적 타입 변환
Price newPrice = clazz.cast(object);
Constructor<?> constructor = clazz.getDeclaredConstructor();
Object o = constructor.newInstance();
Price price = (Price) constructor.newInstance();
// clazz.newInstance()는 deprecated
Constructor<?> withArgConstructor = clazz.getDeclaredConstructor(Integer.class);
Class<Price> clazz = Price.class;
Price price = new Price(12000);
Method exchange = clazz.getDeclaredMethod("exchange");
exchange.invoke(price);
List<String>
과 같은 타입public get (Class<T> clazz){ }
public static void main() {
get(List<String>.class); //-> 불가능
}
// version 1
public class Sup<T>{
T value;
}
public static void main() {
Sup<String> s = new Sup<>();
s.getClass().getDeclaredField("value").getType(); // -> Object로 반환되며 String으로 나오지 않는다.
}
List<String>.class
처럼 써먹기 위해나왔다.// version 2
public class Sup<T>{
T value;
}
public class Sub extends Sup<String>{
}
public static void main() {
Sub s = new Sub();
Type t = s.getClass().getGenericSuperclass();
ParameterizedType ptype = (ParameterizedType)t; //여기서는 안전해서 강제 type casting 함
Type actualTypeArgument = ptype.getActualTypeArguments()[0]; // Sup<String, Integer> 인 경우에 [1]은 Integer
// actualTypeArgument를 출력하면 String이 나온다.
}
Sup<String> s = new Sup<>()
vs Sub s = new Sub()
Sub extends Sup<String>
로 명시하면서 ParameterizedTyped으로 값이 넘어온다.class Price<T extends Number> {
T value;
public T exchange(){ return value * 1300 }
}
public static void main() {
Price<Integer> jpnPrice = new Price<Integer>(){
@Override
public Integer exchange(){ return value * 900 }
};
new ParameterizedTypeReference<List<Price>>(){}
가 매번 만들어야하니 캐싱해두고 저장해두는 방법도 있을 것이다. 참고
https://jyami.tistory.com/99
https://medium.com/@joongwon/java-java%EC%9D%98-generics-604b562530b3
타입 소거 관련
토비의 봄 슈퍼타입 토큰
Reflection 참고