Java Generic
Generic
제네릭 프로그래밍(Generic programming)은 데이터 형식에 의존하지 않고, 하나의 값이 여러 다른 데이터 타입들을 가질 수 있는 기술에 중점을 두어 재사용성을 높일 수 있는 프로그래밍 방식이다.
제네릭 프로그래밍 - 위키백과, 우리 모두의 백과사전
final List list = new ArrayList();
list.add("box1");
final String str1 = (String) list.get(0);
System.out.println(str1);
final List list = new ArrayList();
list.add("box1");
list.add(new Integer(1));
final String str1 = (String) list.get(0);
final String str2 = (String) list.get(1);
System.out.println(str1);
System.out.println(str2);
Exception in thread "main" java.lang.ClassCastException:
java.lang.Integer cannot be cast to java.lang.String
at Main.main(Main.java:14)
final List<String> list = new ArrayList<>();
list.add("box1");
list.add(new Integer(1));
final String box1 = list.get(0);
final String box2 = list.get(1);
System.out.println(box1);
System.out.println(box2);
Error:(11, 13) java: no suitable method found for add(java.lang.Integer)
method java.util.Collection.add(java.lang.String) is not applicable
(argument mismatch; java.lang.Integer cannot be converted to java.lang.String)
method java.util.List.add(java.lang.String) is not applicable
(argument mismatch; java.lang.Integer cannot be converted to java.lang.String)
public class Box<T> {
private T element;
public void add(T element) {
this.element = element;
}
public T get() {
return this.element;
}
}
final Box<String> home = new Box<>();
home.add("Cat!");
System.out.println(home.get());
Generic Class
public class Box<C, A, T> {
private C element;
public void add(C element) {
this.element = element;
}
public C get() {
return this.element;
}
}
final Box<String, Integer, Double> home = new Box<>();
home.add("Cat!");
System.out.println(home.get());
Generic Class
public <T> void print(T element) {
System.out.println(element);
}
print("Cat!");
print(new Integer(1));
print(new Double(2.0));
Generic Method
public void print1(List<? extends Integer> element) {
System.out.println(element);
}
public void print2(List<? super Double> element) {
System.out.println(element);
}
print1(Arrays.asList(new Integer(1)));
print1(Arrays.asList(new Double(1.0))); // error!
print2(Arrays.asList(new Double(1)));
print2(Arrays.asList(new Integer(1.0))); // error!
Bounded Type Parameter
final List<String> list = new ArrayList<>();
list.add("Cat1!");
final List<?> wList = list;
final String cat = (String) wList.get(0);
wList.add("Cat2!"); // compile error!
wList.remove(0);
Wildcard
Compatibility
final List list1 = new ArrayList();
list1.add("Cat1!");
final String cat1 = (String) list1.get(0);
final List<String> list2 = new ArrayList<>();
list2.add("Cat2!");
final String cat2 = list2.get(0);
~ J2SE 1.4
J2SE 1.5 ~
final List<String> list = new ArrayList<>();
list.add("Cat!");
final String cat = list.get(0);
final List list = new ArrayList();
list.add("Cat!");
final String cat = (String) list.get(0);
Compile!
public static main([Ljava/lang/String;)V
L0
LINENUMBER 10 L0
NEW java/util/ArrayList
DUP
INVOKESPECIAL java/util/ArrayList.<init> ()V
ASTORE 1
L1
LINENUMBER 11 L1
ALOAD 1
LDC "Cat!"
INVOKEINTERFACE java/util/List.add (Ljava/lang/Object;)Z
POP
L2
LINENUMBER 12 L2
ALOAD 1
ICONST_0
INVOKEINTERFACE java/util/List.get (I)Ljava/lang/Object;
CHECKCAST java/lang/String
ASTORE 2
Java bytecode
Covariant
& Contravariant
공변 (共変, covariant)
반(공)변 (反変, contravariant)불변 (不変 , invariant)
Covariant
public class Animal {
public Animal getAnimal() {
System.out.println("Animal!");
return new Animal();
}
}
public class Cat extends Animal {
@Override
public Cat getAnimal() {
System.out.println("Cat!");
return new Cat();
}
}
final Cat cat = new Cat();
cat.getAnimal(); // Cat!
final Animal animal = cat;
animal.getAnimal(); // Cat!
Covariant
final Number num1 = new Integer(1);
final Number num2 = new Double(1.0);
final Number[] numbers = new Number[2];
numbers[0] = new Integer(1);
numbers[1] = new Double(1.0);
Contravariant
public class Animal {
public void put(Animal animal) {
System.out.println("Animal!");
}
}
public class Cat extends Animal {
public void put(Object animal) {
System.out.println("Cat!");
}
}
final Cat cat = new Cat();
cat.put(new Cat()); // Animal!
Contravariant
final Cat cat1 = new Animal(); // compile error!
final Cat cat2 = (Cat) new Animal(); // runtime error!
final Cat[] cats = new Cat[2];
cats[0] = new Animal(); // compile error!
cats[1] = (Cat) new Animal(); // runtime error!
public void printNumber(final List<Number> numbers) {
numbers.forEach(System.out::println);
}
final List<Number> num1 = new ArrayList<Integer>(); // compile error!
final List<Number> num2 = new ArrayList<>();
num2.add(1);
printNumber(num2);
final List<Integer> num3 = new ArrayList<>();
num3.add(1);
printNumber(num3); // compile error!
Invariant
final Number[] numbers = new Number[3];
numbers[0] = new Integer(1);
numbers[1] = new Long(1);
numbers[2] = new Double(1.0);
System.out.println(numbers[0].longValue());
System.out.println(numbers[1].longValue());
System.out.println(numbers[2].longValue());
Array = Covariant
final Number[] numbers2 = new Integer[3];
numbers2[0] = new Double(1.0);
System.out.println(numbers2[0].longValue());
Array = Covariant
Exception in thread "main" java.lang.ArrayStoreException: java.lang.Double
public void print1(List<? extends Integer> element) {
System.out.println(element);
}
final List<? extends Number> numbers = new ArrayList<Integer>();
Bounded Type Parameter
covariant
public void print2(List<? super Double> element) {
System.out.println(element);
}
final List<? super Integer> integers = new ArrayList<Number>();
contravariant
final List<? extends Number> numbers = new ArrayList<Integer>();
Number number1 = numbers.get(0);
Integer number2 = numbers.get(1); // compile error!
numbers.add(new Integer(1)); // compile error!
numbers.add(new Long(1)); // compile error!
Bounded Type Parameter
covariant
final List<? super Integer> integers = new ArrayList<Number>();
Number integer1 = integers.get(0); // compile error!
Integer integer2 = integers.get(1); // compile error!
Object integer3 = integers.get(2);
integers.add(new Integer(1));
integers.add(new Long(1)); // compile error!
Bounded Type Parameter
contravariant
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
int srcSize = src.size();
if (srcSize > dest.size())
throw new IndexOutOfBoundsException("Source does not fit in dest");
if (srcSize < COPY_THRESHOLD ||
(src instanceof RandomAccess && dest instanceof RandomAccess)) {
for (int i=0; i<srcSize; i++)
dest.set(i, src.get(i));
} else {
ListIterator<? super T> di=dest.listIterator();
ListIterator<? extends T> si=src.listIterator();
for (int i=0; i<srcSize; i++) {
di.next();
di.set(si.next());
}
}
}
PECS
Producer Extends, Consumer Super
Type Token
public <T> T newInstances(Class<T> clazz) throws Exception {
final T instances = clazz.newInstance();
// 적절한 초기화 코드
return instances;
}
final String str = newInstances(String.class);
final List<String> list = newInstances(List<String>.class); // error!
Class<T>
public <T> List<T> newListInstances(Class<T> clazz) throws Exception {
final ArrayList<T> list = new ArrayList<>();
// 적절한 초기화 코드
return list;
}
final List<String> list = newListInstances(String.class);
Map, Set ???
Class<T>
public class Box<T> {
// 적절한 구현
}
public class LongBox extends Box<List<Long>> {
// 적절한 구현
}
final Type type = LongBox.class.getGenericSuperclass();
final ParameterizedType pType = ParameterizedType.class.cast(type);
final Type[] actualTypeArguments = pType.getActualTypeArguments();
System.out.println(actualTypeArguments[0]);
java.util.List<java.lang.Long>
ParameterizedType
public static void main(String[] args) throws Exception {
class LongBox extends Box<List<Long>> { }
final Type type = LongBox.class.getGenericSuperclass();
final ParameterizedType pType = ParameterizedType.class.cast(type);
final Type[] actualTypeArguments = pType.getActualTypeArguments();
System.out.println(actualTypeArguments[0]);
}
ParameterizedType
public static void main(String[] args) throws Exception {
Box typeBox = new Box<List<Long>>() {};
final Type type = typeBox.getClass().getGenericSuperclass();
final ParameterizedType pType = ParameterizedType.class.cast(type);
final Type[] actualTypeArguments = pType.getActualTypeArguments();
System.out.println(actualTypeArguments[0]);
}
ParameterizedType
final ParameterizedTypeReference<List<String>> typeReference =
new ParameterizedTypeReference<List<String>>() {};
final List<String> response =
restTemplate.exchange(
url,
HttpMethod.GET,
request,
typeReference).getBody();
Spring : RestTemplate
final TypeToken<List<String>> typeToken =
new TypeToken<List<String>>() {};
final List<String> list =
new Gson().fromJson(jsonText, typeToken.getType());
Google : Gson
final TypeReference type = new TypeReference<List<String>>() {};
final List<String> data = mapper.readValue(jsonData, type);
Jackson : ObjectMapper
- 새로 작성하는 코드에서는 원천(raw) 타입을 사용하지 말자
- 컴파일 경고 메시지가 없게 하자
- 배열보다는 List를 사용하자
- 제네릭 타입을 애용하자
- 제네릭 메소드를 애용하자
- 바운드 와일드 카드를 사용해서 API의 유연성을 높이자
- 타입 안전이 보장되는 혼성(heterogeneous) 컨테이너의 사용을 고려하자
Chapter 5. 제네릭(Generics) - Effective java 2/e
END
Java Generic
By serivires
Java Generic
- 591