Generic
์์ํ๊ธฐ์ ์์ ๊ด๋ จ ์ฉ์ด๋ฅผ ์ ๋ฆฌํด ๋๊ฒ์ด๋ค.
ํ๊ธ
์์ด
์
๋งค๊ฐ๋ณ์ํ ํ์
parameterized type
List<String>
์ค์ ํ์ ๋งค๊ฐ๋ณ์
actual type parameter
String
์ ๋ค๋ฆญ ํ์
generic type
List<E>
์ ๊ท ํ์ ๋งค๊ฐ๋ณ์
formal type parameter
E
๋นํ์ ์ ์์ผ๋์นด๋ ํ์
unbounded wildcard type
List<?>
๋ก ํ์
raw type
List
ํ์ ์ ํ์ ๋งค๊ฐ๋ณ์
bounded type parameter
<E extends Number>
์ฌ๊ท ํ์ ํ์
recursive type bound
<T extends Comparable<T>>
ํ์ ์ ์์ผ๋์นด๋ ํ์
bounded wildcard type
List<? extends Number>
์ ๋ค๋ฆญ ๋ฉ์๋
generic method
static <E> List<E> asList(E[] a)
ํ์ ํ ํฐ
type token
String.class
Generic์ด๋ ๋ฌด์์ผ๊น?
JDK 1.5์ ์ฒ์ ๋์ ๋์์ผ๋ฉฐ, ์ ๋ค๋ฆญ์ ํด๋์ค ๋ด๋ถ์์ ์ฌ์ฉํ ๋ฐ์ดํฐ ํ์ ์ ์ธ๋ถ์์ ์ง์ ํ๋ ๊ธฐ๋ฒ์ ์๋ฏธํ๋ค.(์ํ์ฝ๋ฉ )
public class Fruit<T> {
public T fruit;
}Fruit<Apple> apple = new Fruit<>();
Fruit<Banana> apple = new Fruit<>();์์ ์์ ๋ฅผ ๋ณด๋ฉด Fruit ์ธ์คํด์ค๋ฅผ ์์ฑํ ๋, Apple, Banana ๋ฅผ ๋ฃ์ด ํ์
์ ์ง์ ํ๊ณ ์๋ค. ์ฆ, ํด๋์ค๋ฅผ ์ ์ ํ ๋๋ ์ด๋ค ํ์
์ด ๋ค์ด์ฌ์ง ํ์ ํ์ง ์๊ณ , ์ธ์คํด์ค๋ฅผ ์์ฑํ ๋ ๋ฐ์ดํฐ ํ์
์ ์ง์ ํ๋ ๊ธฐ๋ฅ์ด๋ค.
์ ์ ๋ค๋ฆญ์ ์ฌ์ฉํ ๊น?
Generic์ ํด๋์ค์ ์ธํฐํ์ด์ค, ๋ฉ์๋๋ฅผ ์ ์ํ ๋ ํ์ ์ ํ๋ผ๋ฏธํฐ๋ก ์ฌ์ฉํ ์ ์๊ฒ ํ๋ค. Generic ํ์ ์ ์ด์ฉํจ์ผ๋ก์จ ์๋ชป๋ ํ์ ์ด ์ฌ์ฉ๋ ์ ์๋ ๋ฌธ์ ๋ฅผ ์ปดํ์ผ ๊ณผ์ ์์ ์ ๊ฑฐํ ์ ์๊ฒ๋์๋ค. ํ์ ํ๋ผ๋ฏธํฐ๋ ์ฝ๋ ์์ฑ ์ ๊ตฌ์ฒด์ ์ธ ํ์ ์ผ๋ก ๋์ฒด๋์ด ๋ค์ํ ์ฝ๋๋ฅผ ์์ฑํ๋๋ก ํด์ค๋ค.
์ปดํ์ผ ์ ๊ฐํ ํ์
์ฒดํฌ๋ฅผ ํ ์ ์๋ค.
์ปดํ์ผ ์ธ์ด์ ๊ธฐ๋ณธ์ ๋ชจ๋ ์๋ฌ๋ ์ปดํ์ผ์ด ๋ฐ์ํ ์ ์๋๋ก ํ๋ ๊ฒ์ด ์ข๋ค.(์ค๋ฅ๋ ๋นจ๋ฆฌ ๋ฐ๊ฒฌํ ์๋ก ์ข๋ค.) ๋ฐํ์์ ์ค์ ๋ก ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋์ํ๊ณ ์๋ ์ํฉ์ด๊ธฐ ๋๋ฌธ์ ๋ฐํ์์ ๋ฐ์ํ๋ ์๋ฌ๋ ํญ์ ์ฌ๊ฐํ ๋ฌธ์ ๋ฅผ ์ด๋ํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
public class GenericTest {
@Test
void runtimeExceptionTest() {
Person person = new Person("ํํธ๋");
Assertions.assertThrows(ClassCastException.class, () -> {
Employee employee = (Employee) person.info;
});
}
static class Employee {
public int rank;
Employee(int rank){ this.rank = rank; }
}
static class Person {
public Object info;
Person(Object info){ this.info = info; }
}
}java.lang.ClassCastException: class java.lang.String cannot be cast to class ...์๋ ์ฑ๊ณต์ ์ผ๋ก ์ปดํ์ผ๋์ง๋ง, ๋ฐํ์์ ClassCastException ์ด ๋ฐ์ํ๋ ๊ฒ์ ๋ณด์ฌ์ฃผ๋ ์์ ์ด๋ค. ์ด๋ Generic Type์ ์ฌ์ฉํด ์ปดํ์ผ ํ์์ ์ค๋ฅ๋ฅผ ๋ฐ๊ฒฌํ ์ ์๋๋ก ํ ์ ์๋ค.
public class GenericTest {
@Test
void compileTimeExceptionTest() {
Person<Employee> person = new Person<>(new Employee(10));
Employee employee = person.info;
Assertions.assertEquals(10, employee.rank);
// ์ปดํ์ผ ์ค๋ฅ ๋ฐ์
// java: incompatible types: java.lang.String cannot be converted to int
Person<Employee> person2 = new Person<>(new Employee("ํํธ๋"));
}
static class Employee {
public int rank;
Employee(int rank){ this.rank = rank; }
}
static class Person<T> {
public T info;
Person(T info){ this.info = info; }
}
}Generic type์ผ๋ก ๋ณ๊ฒฝ ํ์๋ java: incompatible types: java.lang.String cannot be converted to int ์ปดํ์ผ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
์ฆ, ์ปดํ์ผ ๋จ๊ณ์์ ์ค๋ฅ ๊ฒ์ถ์ด ๊ฐ๋ฅํ๋ฉฐ, ํ์ ์์ ์ฑ์ ์ถ๊ตฌํ ์ ์๊ฒ๋๋ค.
ํ์
๋ณํ(casting)์ ์ ๊ฑฐํ๋ค.
// generic์ด ์๋๊ฒฝ์ฐ
List list = new ArrayList();
list.add("hello");
String str = (String)list.get(0); // ํ์
๋ณํ์ด ํ์
// generic
List<String> list = new ArrayList<String>();
list.add("hello");
String str = list.get(0); // ํ์
๋ณํ์ ํ์ง ์์Generic Type
Generic Type์ ํ์
์ ํ๋ผ๋ฏธํฐ๋ก ๊ฐ์ง๋ ํด๋์ค(class<T>)์ ์ธํฐํ์ด์ค(interface<T>)๋ฅผ ๋งํ๋ค.
public class ํด๋์ค๋ช
<T> {...}
public interface ์ธํฐํ์ด์ค๋ช
<T> {...}ํ์ ํ๋ผ๋ฏธํฐ๋ ๋ณ์๋ช ๊ณผ ๋์ผํ ๊ท์น์ผ๋ก ์์ฑ๋ ์ ์์ง๋ง, ์ผ๋ฐ์ ์ผ๋ก ๋๋ฌธ์ ํ๊ธ์๋ก ํํํ๋ค. Generic Type์ ์ค์ ์ฝ๋์์ ์ฌ์ฉํ๋ ค๋ฉด ํ์ ํ๋ผ๋ฏธํฐ์ ๊ตฌ์ฒด์ ์ธ ํ์ ์ ์ง์ ํด์ผํ๋ค.
public class Box {
private Object obj;
public void set(Object object){ this.obj = object; }
public Object get(){ return obj; }
}์์ ์ฝ๋์์ ํด๋์ค ํ๋ ํ์ ์ Object๋ก ์ ์ธํ ์ด์ ๋ ํ๋์ ๋ชจ๋ ์ข ๋ฅ์ ๊ฐ์ฒด๋ฅผ ์ ์ฅํ๊ณ ์ถ์ด์์ด๋ค. Object๋ ๋ชจ๋ ์๋ฐ ํด๋์ค์ ์ต์์ ์กฐ์(๋ถ๋ชจ) ํด๋์ค์ด๋ค. ์์ ๊ฐ์ฒด๋ ๋ถ๋ชจํ์ ์ ๋์ ํ ์ ์๊ธฐ ๋๋ฌธ์ ๋ชจ๋ ์๋ฐ ๊ฐ์ฒด๋ Objectํ์ ์ผ๋ก ์๋ ํ์ ๋ณํ๋์ด ์ ์ฅ๋๋ค.
Box box = new Box();
box.set("์๋
"); // String ํ์
์ Objectํ์
์ผ๋ก ์๋ํ์
๋ณํ
String str = (String) box.get(); // Object ํ์
์ Stringํ์
์ผ๋ก ๊ฐ์ ํ์
๋ณํ๋ค์๊ณผ ๊ฐ์ด get์ผ๋ก ๊ฐ์ ธ์ค๊ธฐ์ํด์๋ ๊ฐ์ ํ์ ๋ณํ์ด ํ์ํ๋ค.
Object ํ์ ์ ์ฌ์ฉํ๋ฉด ๋ชจ๋ ์ข ๋ฅ์ ์๋ฐ ๊ฐ์ฒด๋ฅผ ์ ์ฅํ ์ ์๋ค๋ ์ฅ์ ์ ์์ง๋ง, ์ ์ฅํ ๋์ ์ฝ์ด์ฌ ๋ ํ์ ๋ณํ์ด ๋ฐ์ํ๋ฉฐ, ์ฆ์ ํ์ ๋ณํ์ ์ ์ฒด ํ๋ก๊ทธ๋จ ์ฑ๋ฅ์ ์ข์ง ๋ชปํ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ค.
Generic์ ํตํด์ ํ์ ๋ณํ์ด ๋ฐ์ํ์ง ์๋๋ก ํ ์ ์๋ค.
public class Box<T>{
private T t;
public void set(T t){this.t = t;}
public T get(){ return t; }
}Box<String> box = new Box<String>();์ฌ๊ธฐ์ T๋ ํด๋์ค๋ก ๊ฐ์ฒด๋ฅผ ์์ฑํ ๋ ๊ตฌ์ฒด์ ์ธ ํ์ ์ผ๋ก ๋ณ๊ฒฝ๋๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์ ์ฅํ ๋์ ์ฝ์ด์ฌ ๋ ํ์ ๋ณํ์ด ๋ฐ์ํ์ง ์๋๋ค. ์ด์ ๊ฐ์ด generic์ ํด๋์ค ์ค๊ณ์ ๊ตฌ์ฒด์ ์ ํ์ ์ ๋ช ์ํ์ง ์๊ณ , ํ์ ํ๋ผ๋ฏธํฐ๋ก ๋์ฒดํ๋ค๊ฐ ์ค์ ํด๋์ค๊ฐ ์ฌ์ฉ๋ ๋ ๊ตฌ์ฒด์ ์ธ ํ์ ์ ์ง์ ํจ์ผ๋ก์จ ํ์ ๋ณํ์ ์ต์ํ์ํจ๋ค.
ํ์
ํ๋ผ๋ฏธํฐ
ํ์ ํ๋ผ๋ฏธํฐ์ ์ด๋ฆ์ ์ง๊ธฐ ๋๋ฆ์ด์ง๋ง, ์ผ๋ฐ์ ์ผ๋ก ๋๋ฌธ์ ํ๊ธ์๋ก ํํ ๋ณดํธ์ ์ผ๋ก ์์ฃผ ์ฌ์ฉํ๋ ํ์ ๋งค๊ฐ๋ณ์๋ ๋ค์๊ณผ ๊ฐ๋ค.
ํ์ ๋งค๊ฐ๋ณ์
์๋ฏธ
E
Element
K
Key
N
Number
T
Type
V
Value
S,U,V
2nd, 3rd, 4th types
๋ค์ด์๋ชฌ๋ <>
<>์ ๋ค๋ฆญ ํ์
๋ณ์ ์ ์ธ๊ณผ ๊ฐ์ฒด ์์ฑ์ ๋์์ ํ ๋ ํ์
ํ๋ผ๋ฏธํฐ์ ๊ตฌ์ฒด์ ์ธ ํ์
์ ์ง์ ํ๋ ์ฝ๋๊ฐ ์ค๋ณต๋ ์ ์๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์๋ฐ7์์ ๋ถํฐ๋ <> (๋ค์ด์๋ชฌ๋์ฐ์ฐ์)๋ฅผ ์ ๊ณตํ๋ค. ์๋ฐ ์ปดํ์ผ๋ฌ๋ ํ์
ํ๋ผ๋ฏธํฐ ๋ถ๋ถ์ <>์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ๋ฉด ํ์
ํ๋ผ๋ฏธํฐ๋ฅผ ์ ์ถํด์ ์๋์ผ๋ก ์ค์ ํด์ค๋ค.
// java7์ดํ
Box<String> box = new Box<>();๋ค์ค ํ์
ํ๋ผ๋ฏธํฐ(Multiple Type Parameters)
Generic Type์ ๋ ๊ฐ ์ด์์ ๋ฉํฐ ํ์ ํ๋ผ๋ฏธํฐ๋ฅผ ์ฌ์ฉํ ์ ์๋ค. ์ด ๊ฒฝ์ฐ ๊ฐ ํ์ ํ๋ผ๋ฏธํฐ๋ฅผ ์ฝค๋ง๋ก ๊ตฌ๋ถํ๋ค.
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 setMode(M model){ this.model = model; }
}public class ProductEx{
public static void main(String[] args){
Product<TV, String> prd1 = new Product<TV,String>();
prd1.setKind(new TV());
prd1.setModel("์ผ์ฑTV");
TV tv = prd1.getKind();
String tvModel = prd1.getModel();
Product<Car, String> carPrd = new Product<Car,String>();
carPrd.setKind(new Car());
carPrd.setModel("๋์ ค");
Car car = carPrd.getKind();
String carModel = car.getModel();
}
}Raw Types
Raw type์ ํ์ ํ๋ผ๋ฏธํฐ๊ฐ ์๋ ์ ๋ค๋ฆญ ํ์ ์ ์๋ฏธํ๋ค.
public class Box<T> {
public void set(T t) { /* ... */ }
// ...
}// Box๋ Generic type์ด์ง๋ง ํ์
ํ๋ผ๋ฏธํฐ ์์ด ์์ฑ
Box rawBox = new Box();Raw type์ Java๊ฐ ์ ๋ค๋ฆญ์ ๋์ ํ๊ธฐ์ (JDK 5.0) ์ด์ ๊ธฐ์กด ์ฝ๋์์ ํธํ์ฑ์ ๋ณด์ฅํ๊ธฐ ์ํด์ ์ ๊ณตํ๊ณ ์๋ ๊ฒ์ด๋ค.
Box rawBox = new Box();
Box<String> stringBox = new Box<>();
Box rawBox = stringBox; // OKํ์ง๋ง, raw type์ paratmeterized type์ผ๋ก ์ค์ ํ๋ฉด ๊ฒฝ๊ณ ๋ฌธ๊ตฌ๋ฅผ ๋ณผ ์ ์๋ค.
Box rawBox = new Box(); // rawBox is a raw type of Box<T>
Box<Integer> intBox = rawBox; // warning: unchecked conversion๋ํ, raw type์ ์ฌ์ฉํด, ์ ๋ค๋ฆญ ํ์ ์ ์ ๋ค๋ฆญ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ๊ฒฝ์ฐ์๋ ๊ฒฝ๊ณ ๊ฐ ํ์๋๋ค.
Box<String> stringBox = new Box<>();
Box rawBox = stringBox;
rawBox.set(8); // warning: unchecked invocation to set(T)์ ๊ฒฝ๊ณ ๋ raw type์ด generic type ๊ฒ์ฌ๋ฅผ ์๋ตํด ์์ ํ์ง ์์ ์ฝ๋๊ฐ ๋ฐํ์์์ ๋ฐ๊ฒฌ๋ ์๋ ์๋ค๋ ๊ฒฝ๊ณ ๋ฌธ๊ตฌ์ด๋ค. ๊ทธ๋ฌ๋ฏ๋ก, raw type์ ์ต๋ํ ์ฌ์ฉํ์ง ์๋ ๊ฒ์ด ์ข์ผ๋ฉฐ, ์์ธํ ๋ด์ฉ์ [effective java - ITEM 26]์์ ํ์ธํ ์ ์๋ค.
Generic Method
์ผ๋ฐ ํด๋์ค์ ๋ฉ์๋์์ ํ์ ๋งค๊ฐ๋ณ์๋ฅผ ์ฌ์ฉํด ์ ์ ๊ฐ๋ฅ
์ ๋ค๋ฆญ ๋ฉ์๋์์ ํ์ ๋งค๊ฐ๋ณ์ ๋ฒ์๋ ๋ฉ์๋ ๋ด๋ถ๋ก ์ ํ๋๋ค.
์ ๋ค๋ฆญ ํด๋์ค์ ์์ฑ์์์๋ ์ฌ์ฉ ๊ฐ๋ฅ
Generic Method๋ ๋งค๊ฐํ์ ๊ณผ ๋ฆฌํด ํ์ ์ผ๋ก ํ์ ํ๋ผ๋ฏธํฐ๋ฅผ ๊ฐ๋ ๋ฉ์๋์ด๋ค.
public <ํ์
ํ๋ผ๋ฏธํฐ, ...> ๋ฆฌํดํ์
๋ฉ์๋๋ช
(๋งค๊ฐ๋ณ์, ...){...}public <T> Box<T> boxing(T t){...}ํ์
๋งค๊ฐ๋ณ์ <T> ๋ ๋ฐ๋์ ๋ฉ์๋์ ์์์(public, static)์ ๋ฐํํ ์ฌ์ด์ ์์น๋์ด์ผํ๋ค.
Generic ๋ฉ์๋๋ ๋ค์๊ณผ ๊ฐ์ด ํธ์ถ๋ ์ ์๋ค.
//๋ช
์์ ์ผ๋ก ๊ตฌ์ฒด์ ํ์
์ ์ง์
๋ฆฌํดํ์
๋ณ์ = <๊ตฌ์ฒด์ ํ์
> ๋ฉ์๋๋ช
(๋งค๊ฐ๊ฐ);
Box<Integer> box = <Integer>boxing(100);
//๋งค๊ฐ๊ฐ์ ๋ณด๊ณ ๊ตฌ์ฒด์ ํ์
์ ์ถ์
๋ฆฌํดํ์
๋ณ์ = ๋ฉ์๋๋ช
(๋งค๊ฐ๊ฐ);
Box<Integer> box = boxing(100);public class Util {
public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
return p1.getKey().equals(p2.getKey()) &&
p1.getValue().equals(p2.getValue());
}
}
public class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public void setKey(K key) { this.key = key; }
public void setValue(V value) { this.value = value; }
public K getKey() { return key; }
public V getValue() { return value; }
}Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "pear");
boolean same = Util.<Integer, String>compare(p1, p2);Util.<Integer, String>compare(p1, p2) ๊ณผ ๊ฐ์ด ํ์
์ ๋ช
์์ ์ผ๋ก ์ผ์ง๋ง, ๋ค์๊ณผ ๊ฐ์ด ์๋ตํด๋ ์ปดํ์ผ๋ฌ๊ฐ ์ ์ถํ ์ ์๋ค.
boolean same = Util.compare(p1, p2);์ ํ๋ ํ์
ํ๋ผ๋ฏธํฐ(<T extends ์ต์์ํ์
>)
<T extends ์ต์์ํ์
>)ํ์
ํ๋ผ๋ฏธํฐ์ ์ง์ ๋๋ ๊ตฌ์ฒด์ ์ธ ํ์
์ ์ ํํ ํ์๊ฐ ์ข
์ข
์๋ค. ์ซ์๋ฅผ ์ฐ์ฐํ๋ ์ ๋ค๋ฆญ ๋ฉ์๋์ ๋งค๊ฐ๊ฐ์ผ๋ก๋ Numberํ์
๋๋ ๊ทธ ํ์ ํด๋์ค ํ์
(Integer, Double, Long, Short, ...) ์ ์ธ์คํด์ค๋ง ๊ฐ์ ธ์ผํ๋ค. ์ด๋ฌํ ์ด์ ๋ก ์ ํ๋ ํ์
ํ๋ผ๋ฏธํฐ๊ฐ ํ์ํ ๊ฒฝ์ฐ๊ฐ ์๋ค.
public <T extends ์์ํ์
> ๋ฆฌํดํ์
๋ฉ์๋(๋งค๊ฐ๋ณ์, ...){...}์์ ํ์
์ ํด๋์ค๋ฟ๋ง ์ธํฐํ์ด์ค๋ ๊ฐ๋ฅํ๋ค. ํ์ง๋ง ์ธํฐํ์ด์ค๋ผ๊ณ ํด์ implements๋ฅผ ์ฌ์ฉํ์ง ์๋๋ค.( extends ์ฌ์ฉ)
ํ์ ํ๋ผ๋ฏธํฐ์ ๊ตฌ์ฒด์ ํ์ : ์์ํ์ , ์์ํ์ ์ ํ์ ๋๋ ๊ตฌํํด๋์ค
{}์์์์ ํ์ ํ๋ผ๋ฏธํฐ ๋ณ์ : ์์ ํ์ ์ ๋ฉค๋ฒ(ํ๋, ๋ฉ์๋๋ก ์ ํ)
public <T extends Number> int compare(T t1, T t2){
double v1 = t1.doubleValue();
double v2 = t2.doubleValue();
return Double.compare(v1,v2);
}public class Util{
public static <T extends Number> int compare(T t1, T t2){
double v1 = t1.doubleValue();
double v2 = t2.doubleValue();
return Double.compare(v1,v2);
}
}public class Example{
public static void main(String[] args){
//String str = Util.compare("a","b"); Numberํ์
์ด ์๋๋ฏ๋ก ์ค๋ฅ
int result = Util.compare(10,20);
int result2 = Util.compare(10.5,20);
}
}์ฌ๊ธฐ์ ์ฃผ์ํ ์ ์ ํจ์ ๋ด์์ ํ์
ํ๋ผ๋ฏธํฐ ๋ณ์๋ก ์ฌ์ฉํ ๊ฒ์ ์์ ํ์
์ ๋ฉค๋ฒ(ํ๋, ๋ฉ์๋)๋ก ์ ํ๋๋ค๋ ์ ์ด๋ค. doubleValue()๋ Number ํด๋์ค์ ๋ฉ์๋์ด๊ธฐ๋๋ฌธ์ ์ฌ์ฉํ ์ ์๋ ๊ฒ์ด๋ค.

Muliple Bounds
์ ํ๋ ํ์ ํ๋ผ๋ฏธํฐ๋ฅผ ์ฌ๋ฌ๊ฐ๋ก ์ค์ ํ ์ ์๋ค.
<T extends B1 & B2 & B3>Class A { /* ... */ }
interface B { /* ... */ }
interface C { /* ... */ }class D <T extends A & B & C> { /* ... */ }์ฌ๊ธฐ์ D ํด๋์ค์ ํ์
ํ๋ผ๋ฏธํฐ๋ A, B, C ๋ชจ๋ ์ ํ์ ํ์ ํด๋์ค์ฌ์ผํ๋ฉฐ, ์ด์ค ํ๊ฐ๊ฐ ํด๋์ค์ธ ๊ฒฝ์ฐ ๋ฐ๋์ ๋จผ์ ์ ์ธ๋์ด์ผํ๋ค.
class D <T extends B & A & C> { /* ... */ }์์ ๊ฐ์ด A ๊ฐ ํด๋์ค์ด์ง๋ง, ์ธํฐํ์ด์ค์ธ B ๋ณด๋ค ๋ฆ๊ฒ ์ ์ธ๋๋ค๋ฉด ์ปดํ์ผ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค.
Generic Method์ Bounded Type Parameter
์ ํ๋ ํ์ ํ๋ผ๋ฏธํฐ(Bounded Type Parameter)๋ ์ ๋ค๋ฆญ ์๊ณ ๋ฆฌ์ฆ์ ๊ตฌํํ ๋ ํต์ฌ์ด๋๋ค.
// ๋๋ฒ์งธ ์ธ์(elem)๋ณด๋ค ํฐ ๊ฐ์ด anArray์ ๋ช๊ฐ๊ฐ ์๋์ง ์ธ๋ ๋ฉ์๋
public static <T> int countGreaterThan(T[] anArray, T elem) {
int count = 0;
for (T e : anArray)
if (e > elem) // compiler error
++count;
return count;
}์ ๋ฉ์๋๋ ์ปดํ์ผ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค.
java: bad operand types for binary operator '>'
first type: T
second type: T> ์ฐ์ฐ์๋ ๊ธฐ๋ณธํ(int, short, double, long ...)์๋ง ๋์์ด ํ์ฉ๋๊ธฐ ๋๋ฌธ์ด๋ค. ์ฆ, > ์ฐ์ฐ์๋ ๊ฐ์ฒด๊ฐ ๋น๊ต์ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, Comparable ์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฉํด ํด๋น ์ค๋ฅ๋ฅผ ํด๊ฒฐํ ์ ์๋ค.
public interface Comparable<T> {
public int compareTo(T o);
}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;
}Comparable ์ธํฐํ์ด์ค๋ก ์ ํ๋ ํ์
์ ์ฌ์ฉํด ๊ฐ์ฒด๋ฅผ ๋น๊ตํ ์ ์๋ค.
Generics, Inheritance, Subtypes
Object someObject = new Object();
Integer someInteger = new Integer(10);
someObject = someInteger; // OKํ์
๊ฐ ํธํ์ด๋๋ค๋ฉด, ํน์ ํ์
์ ๊ฐ์ฒด๋ฅผ ๋ค๋ฅธ ํ์
์ ํ ๋น์ด ๊ฐ๋ฅํ๋ค. Object ๋ Integer์ ์์ ํด๋์ค์ด๊ธฐ ๋๋ฌธ์ ํ ๋น์ด ๊ฐ๋ฅํ๋ค. ๊ฐ์ฒด์งํฅ ์ด๋ก ์์๋ ์ด๋ฌํ ๊ฒฝ์ฐ๋ฅผ "is a" ๊ด๊ณ๋ผ๊ณ ๋ถ๋ฅธ๋ค. "Integer is a Object" ์ด๋ฏ๋ก Object์ Integer๊ฐ ํ ๋น์ด ๊ฐ๋ฅํ ๊ฒ์ด๋ค.
public void someMethod(Number n) { /* ... */ }
someMethod(new Integer(10)); // OK
someMethod(new Double(10.1)); // OK๋ํ, "Integer is a Number" ์ด๋ฏ๋ก ์ ์์ ์ฝ๋ ๋ํ ์ ์๋ํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
์ด๋ฌํ ๊ท์น์ ์ ๋ค๋ฆญ์์๋ ๋๊ฐ์ด ์ ์ฉ๋๋ค. ์ ๋ค๋ฆญ ํ์ ํธ์ถ์, ํ์ ์ธ์๊ฐ "is a" ๊ด๊ณ๋ผ๋ฉด ํ์ ์ธ์๋ก ์ ๋ฌํ ์ ์๋ ๊ฒ์ด๋ค.
Box<Number> box = new Box<Number>();
box.add(new Integer(10)); // OK
box.add(new Double(10.1)); // OK์ฌ๊ธฐ์ ์ฃผ์ํด์ผํ ๋ถ๋ถ์ด ์๋ค.
public void boxTest(Box<Number> n) { /* ... */ }boxTest() ๋ฉ์๋์ ์ธ์๋ก ์ด๋ค ํ์
์ ๋ฐ์ ์ ์์๊น? ๋๋ถ๋ถ Box<Integer> ์ Box<Double> ์ด ์ ๋ฌ ๊ฐ๋ฅํ ๊ฑฐ๋ผ๊ณ ์๊ฐํ ๊ฒ์ด๋ค. ํ์ง๋ง, Box<Integer>์ Box<Double> ์ Box<Number> ์ ์๋ธํ์
์ด ์๋๊ธฐ ๋๋ฌธ์ ์ธ์๊ฐ์ผ๋ก ์ ๋ฌ ํ ์ ์๋ค.

Generic Classes and Subtyping
์ ๋ค๋ฆญ ํด๋์ค ์์ ๋๋ ์ ๋ค๋ฆญ ์ธํฐํ์ด์ค ๊ตฌํ์, ๋ ํด๋์ค๊ฐ "is a" ๊ด๊ณ๋ฅผ ๋ง๋ค ์ ์๋ค.

์๋ฅผ ๋ค์ด, Collection ํด๋์ค๋ฅผ ์ฌ์ฉํ ๋, ArrayList<E> ๋ List<E> ๋ฅผ ๊ตฌํํ๊ณ , List<E>๋ Collection<E> ๋ฅผ ์์ ๋ฐ๊ณ ์๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializablepublic interface List<E> extends Collection<E> {๊ทธ๋ฌ๋ฏ๋ก, ArrayList<String> ์ List<String> ์ Collection<String> ์ ํ์ ํ์
์ผ๋ก "is a"๊ด๊ณ๊ฐ ์ฑ๋ฆฝํ๊ฒ ๋๋ ๊ฒ์ด๋ค.
interface PayloadList<E,P> extends List<E> {
void setPayload(int index, P val);
...
}List<E> ์ธํฐํ์ด์ค๋ฅผ ์์๋ฐ๋ PayloadList๊ฐ ๋ค์๊ณผ ๊ฐ์ด ์ ์๋์์ ๋, PayloadList ๋ ๋ค์๊ณผ ๊ฐ์ ์๋ธํ์
๊ด๊ณ๊ฐ ํ์ฑ๋ ์ ์๋๊ฑธ ๋ณผ ์ ์๋ค.

Type Inference(ํ์
์ถ๋ก )
ํ์ ์ถ๋ก ์ ์ปดํ์ผ๋ฌ๊ฐ ๊ฐ ๋ฉ์๋ ํธ์ถ๊ณผ ํด๋น ์ ์ธ์ ๊ฒํ ํ๊ณ ํธ์ถ์ ์ ์ฉํ ์ ์๊ฒ ํ๋ ์ธ์๋ฅผ ๊ฒฐ์ ํ๋ ๋ฅ๋ ฅ์ด๋ค. ์ถ๋ก ์๊ณ ๋ฆฌ์ฆ์ ์ธ์์ ์ ํ๊ณผ ๊ฒฐ๊ณผ๊ฐ ํ ๋น๋๊ฑฐ๋ ๋ฐํ๋๋ ์ ํ์ ๊ฒฐ์ ํ๊ณ , ๊ฐ์ฅ ๊ตฌ์ฒด์ ์ธ ํ์ ์ ์ฐพ๊ธฐ ์ํด ๋ ธ๋ ฅํ๋ค.
Type Inference and Generic Methods
public class BoxDemo {
public static <U> void addBox(U u, List<Box<U>> boxes) {
Box<U> box = new Box<>();
box.set(u);
boxes.add(box);
}
public static <U> void outputBoxes(List<Box<U>> boxes) {
int counter = 0;
for (Box<U> box: boxes) {
U boxContents = box.get();
System.out.println("Box #" + counter + " contains [" +
boxContents.toString() + "]");
counter++;
}
}
public static void main(String[] args) {
ArrayList<Box<Integer>> listOfIntegerBoxes = new ArrayList<>();
BoxDemo.<Integer>addBox(Integer.valueOf(10), listOfIntegerBoxes);
BoxDemo.addBox(Integer.valueOf(20), listOfIntegerBoxes);
BoxDemo.addBox(Integer.valueOf(30), listOfIntegerBoxes);
BoxDemo.outputBoxes(listOfIntegerBoxes);
}
}Box #0 contains [10]
Box #1 contains [20]
Box #2 contains [30]์ ์์ ์์ ์ ๋ค๋ฆญ ๋ฉ์๋ addBox() ๋ ํ์
๋งค๊ฐ๋ณ์(U)๊ฐ ์ ์ธ๋์ด์๋ค. ์ผ๋ฐ์ ์ผ๋ก ์ปดํ์ผ๋ฌ๋ ํด๋น ์ ๋ค๋ฆญ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ๊ณณ์ ๋ณด๊ณ ํ์
ํ๋ผ๋ฏธํฐ๋ฅผ ์ถ๋ก ํ ์ ์๋ค. ์ ๋ค๋ฆญ ๋ฉ์๋ addBox ๋ฅผ ํธ์ถํ ๋, ๊ตฌ์ฒด์ ์ธ ํ์
๋งค๊ฐ๋ณ์๋ฅผ ์ฃผ์ง ์์๋ ์ปดํ์ผ๋ฌ๊ฐ ์๋์ผ๋ก ๊ฐ์ ์ถ๋ก ํ ์ ์๋ค.
BoxDemo.addBox(Integer.valueOf(20), listOfIntegerBoxes);๊ทธ๋ฌ๋ฏ๋ก, ๋๋ถ๋ถ ํ์ ์ ๊ตฌ์ฒด์ ์ผ๋ก ์ ์ธํ์ง ์๊ณ ๋ ์ฌ์ฉํ ์ ์๋ ๊ฒ์ด๋ค.
Type Inference and Instantiation of Generic Classes
์ ๋ค๋ฆญ ํด๋์ค๋ฅผ ์์ฑ์๋ฅผ ํตํด ๊ฐ์ฒด๋ฅผ ์์ฑํ ๋ ํ์
์ธ์ ๋์ <> ๋ค์ด์๋ชฌ๋ ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ๋ฉด, ์ปดํ์ผ๋ฌ๋ ํ์
์ธ์๋ฅผ ์ ์ถํ ์ ์๋ค.
Map<String, List<String>> myMap = new HashMap<String, List<String>>();์ ๋ค๋ฆญ ํด๋์ค์ ์์ฑ์์ ํ์
๋งค๊ฐ๋ณ์ํํ ์์ฑ์ ๋์ ์๋์ฒ๋ผ <> ๋ง ์ฌ์ฉํ์ฌ ์ ์ธํ ์ ์๋ค.
Map<String, List<String>> myMap = new HashMap<>();ํ์ง๋ง, <> ์ฐ์ฐ์๋ฅผ ์ ์ธํ์ง ์์ผ๋ฉด, raw ํ์
์ด๋ฏ๋ก ํ์
์ถ๋ก ์ ํ์ง ์๊ธฐ๋๋ฌธ์ ์ฃผ์ํด์ผํ๋ค
Map<String, List<String>> myMap = new HashMap(); // raw typeType Inference and Generic Constructors of Generic and Non-Generic Classes
์ ๋ค๋ฆญ ํด๋์ค์ ๋น์ ๋ค๋ฆญ ํด๋์ค(non-generic) ๋ชจ๋ ์ ๋ค๋ฆญ ์์ฑ์๋ฅผ ์ ์ธํ ์ ์๋ค.
class MyClass<X> {
<T> MyClass(T t) {
// ...
}
}new MyClass<Integer>("");์ ์ฝ๋๋ ๋งค๊ฐ๋ณ์ํ๋ ํ์
MyClass์ ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ค. ์ ๋ค๋ฆญ ํด๋์ค์ธ MyClass์์ ํ์ ๋งค๊ฐ๋ณ์ X์ ๋ํด Integer ํ์
์ ๋ช
์์ ์ผ๋ก ์ง์ ํ๊ณ ์๋ค. ์ด ์ ๋ค๋ฆญ ํด๋์ค์ ์์ฑ์๋ ํ์ ๋งค๊ฐ ๋ณ์ T๋ฅผ ํฌํจํ๊ณ ์์ผ๋ฉฐ, ์ปดํ์ผ๋ฌ๋ ์ด ์ ๋ค๋ฆญ ํด๋์ค(MyClass)์ ์์ฑ์์ ํ๋ผ๋ฏธํฐ ํ์
์ด String์ธ ๊ฒ์ ์ถ๋ก ํ ์ ์๋ค. ์๋ฐ SE 7 ์ด์ ์ ์ปดํ์ผ๋ฌ๋ ์ ๋ค๋ฆญ ๋ฉ์๋์ ์ ์ฌํ ์ ๋ค๋ฆญ ์์ฑ์์ ์ค์ ํ์
ํ๋ผ๋ฏธํฐ๋ฅผ ์ถ๋ก ํ ์ ์์ผ๋ฉฐ, ์๋ฐ SE7 ์ด์ ์ปดํ์ผ๋ฌ๋ <> ๋ค์ด์๋ชฌ๋ ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ๋ฉด, ์ ๋ค๋ฆญ ํด๋์ค์ ์ค์ ํ์
ํ๋ผ๋ฏธํฐ๋ฅผ ์ถ๋ก ํ ์ ์๋ค.
MyClass<Integer> myObject = new MyClass<>(""); // JavaSE7 ์ดํ์ ์์์์ ์ปดํ์ผ๋ฌ๋ ์ ๋ค๋ฆญ ํด๋์ค MyClass<X>์ ํ์
ํ๋ผ๋ฏธํฐ๊ฐ Integer ์์ ์ถ๋ก ํ ์ ์๊ณ , ์ด ์ ๋ค๋ฆญ ํด๋์ค์ ์์ฑ์์ ํ์
ํ๋ผ๋ฏธํฐ T๊ฐ String ์์ ์ถ๋ก ํ ์ ์๋ค.
Target Types
ํ๊ฒ ํ์ ํํ์์ ์๋ฐ ์ปดํ์ผ๋ฌ๊ฐ ์์ํ๋ ๋ฐ์ดํฐ ํ์ ์ด๋ค.
@SuppressWarnings("unchecked")
public static final <T> List<T> emptyList() {
return (List<T>) EMPTY_LIST;
}List<String> list = Collections.emptyList();์ ์ ์ธ๋ฌธ์ ๋ฆฌํดํ์
์ List<String> ํ์
์ผ๋ก ๋ฐ๊ณ ์๊ธฐ๋๋ฌธ์ List<String> ๋ฅผ ์์ํ ์ ์๋ค. ์ด ๋ฐ์ดํฐ ํ์
์ ๋ฐ๋ก target type์ด๋ผ ํ๋ค. emptyList() ๋ฉ์๋๊ฐ List<T> ๋ฅผ ๋ฐํํ๊ธฐ๋๋ฌธ์ ์ปดํ์ผ๋ฌ๋ T๊ฐ String ์ด๋ผ๊ณ ์ถ๋ก ํ ์ ์๋ค.
List<String> list = Collections.<String>emptyList();๋ฌผ๋ก ์์ ๊ฐ์ด T ํ์
์ ๋ช
์ ํ ์ ์์ง๋ง, ๋ฌธ๋งฅ์ String ์ธ๊ฒ์ด ๋ช
๋ฐฑํ๊ธฐ ๋๋ฌธ์ ์ ์ด์ฃผ์ง ์์๋ ๋๋ค. ๋ช
์์ ์ผ๋ก ํ์
์ ์ ์ธํด์ค์ผํ๋ ๊ฒฝ์ฐ๋ฅผ ์ดํด๋ณด์.(Type witness)
void processStringList(List<String> stringList) {
// process stringList
}processStringList(Collections.emptyList());์ ์์ ๋ Java SE7์์ ์ปดํ์ผ ๋์ง ์์ผ๋ฉฐ, List<Object> cannot be converted to List<String> ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค. ์ปดํ์ผ๋ฌ๋ T์ ํ์
์ธ์๊ฐ ํ์ํ์ง๋ง, ์๋ฌด๊ฒ๋ ์ฃผ์ด์ง์ง ์์๊ธฐ ๋๋ฌธ์ Object๋ฅผ ํ์
์ธ์๋ก ๊ฐ๊ฒ๋๋ค. Collections.emptyList() ๋ List<Object> ๊ฐ์ฒด๋ฅผ ๋ฐํํ๊ธฐ๋๋ฌธ์ ์ปดํ์ผ ์ค๋ฅ๋ฅผ ๋ฐ์์ํค๋ ๊ฒ์ด๋ค. Java SE7์์๋ ๋ฐ๋์ ํ์
๊ฐ์ ๋ช
์ํด์ผํ๋ค.
processStringList(Collections.<String>emptyList());ํ์ง๋ง, Java SE8 ์ด์๋ถํฐ๋ ํ๊ฒ ํ์
์ ๊ฒฐ์ ํ ๋ ๋ฉ์๋์ ์ธ์ ๊ฐ๋ ์ดํผ๋๋ก ํ์ฅ๋์์ผ๋ฏ๋ก, ๋ช
์ํด์ค ํ์๊ฐ ์์ด์ก๋ค. ์ฆ, Collections.emptyList() ์ ๋ฐํ ๊ฐ์ธ List<T>๊ฐ List<String> ์ธ๊ฒ ์ถ๋ก ์ด ๊ฐ๋ฅํด์ก๊ธฐ ๋๋ฌธ์ Java SE8๋ถํฐ๋ ์๋ ์์๋ ์ปดํ์ผ ๋๋ค.
List<String> list = Collections.emptyList();Wildcards (์์ผ๋์นด๋)
์ ๋ค๋ฆญ์์ unkwonํ์
์ ํํํ๋ ?๋ฅผ ์ผ๋ฐ์ ์ผ๋ก ์์ผ๋์นด๋๋ผ๊ณ ๋ถ๋ฅธ๋ค. ์์ผ๋์นด๋๋ ํ๋ผ๋ฏธํฐ, ํ๋, ์ง์ญ๋ณ์ ํ์
, ๋ฆฌํดํ์
๋ฑ ๋ค์ํ ์ํฉ์์ ์ฐ์ด๋ฉฐ, ์ ๋ค๋ฆญ ๋ฉ์๋ ํธ์ถ, ์ ๋ค๋ฆญ ํด๋์ค ์ธ์คํด์ค ์์ฑ, ์์ ํ์
(super type)์ ํ์
์ธ์๋ก๋ ์ฌ์ฉ๋์ง ์๋๋ค.
์ฝ๋
์ข ๋ฅ
์ค๋ช
<?>
Unbounded wildcards ๋นํ์ ์ ์์ผ๋ ์นด๋
์ ํ ์์ (ํ์ ํ๋ผ๋ฏธํฐ๋ฅผ ๋์นํ๋ ๊ตฌ์ฒด์ ํ์ ์ผ๋ก ๋ชจ๋ ํด๋์ค๋ ์ธํฐํ์ด์ค ํ์ ์ด ์ฌ ์ ์๋ค.)
<? extends ์์ํ์
>
Upper Bounded Wildcards ์ํ ๊ฒฝ๊ณ ์์ผ๋์นด๋
์์ ํด๋์ค ์ ํ (ํ์ ํ๋ผ๋ฏธํฐ๋ฅผ ๋์นํ๋ ๊ตฌ์ฒด์ ํ์ ์ผ๋ก ์์ ํ์ ์ด๋ ํ์ ํ์ ๋ง ์ฌ ์ ์๋ค.)
<? super ํ์ํ์
>
Lower Bounded Wildcards ํํ ๊ฒฝ๊ณ ์์ผ๋์นด๋
ํ์ ํด๋์ค ์ ํ (ํ์ ํ๋ผ๋ฏธํฐ๋ฅผ ๋์นํ๋ ๊ตฌ์ฒด์ ํ์ ์ผ๋ก ํ์ ํ์ ์ด๋ ์์ํ์ ์ด ์ฌ ์ ์๋ค.)
public class Couse<T>{
private String name;
private T[] students;
public Course(String name, int capacity){
this.name = name;
// ํ์
ํ๋ผ๋ฏธํฐ๋ก ๋ฐฐ์ด์ ์์ฑํ๋ ค๋ฉด new T[n]ํํ๊ฐ ์๋ (T[])(new T[n])์ ํํ๋ก ์์ฑํด์ผํ๋ค.
students = (T[])(new Object[capacity]);
}
public String getName(){ return name; }
public T[] getStudents(){ return students; }
public void add(T t){
for(int i=0;i<students.length;i++){
if(students[i] == null){
students[i]=t;
break;
}
}
}
}์๊ฐ์์ด ๋ ์ ์๋ ํ์ ์ด ์๋์ ๊ฐ๋ค.
Person
Worker
Student
HighStudent
Course<?>: ์๊ฐ์์ ๋ชจ๋ ํ์ (Person, Worker, Student, HightStudent)Course<? extends Students>: ์๊ฐ์๋ Student์ HighStudent๋ง ๊ฐ๋ฅCourse<? super Worker>: Worker, Person๋ง ๊ฐ๋ฅ
Unbounded Wildcards
List<?> ์ ๊ฐ์ด ? ์ ํํ๋ก ์ ์๋๋ฉฐ, ๋นํ์ ์ ์์ผ๋์นด๋ ํ์
์ด ์ฌ์ฉ๋ ์ ์๋ ์๋๋ฆฌ์ค๋ ๋ค์๊ณผ ๊ฐ๋ค.
Object ํด๋์ค์์ ์ ๊ณต๋๋ ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ฌ ๊ตฌํํ ์ ์๋ ๋ฉ์๋๋ฅผ ์์ฑํ๋ ๊ฒฝ์ฐ
ํ์ ํ๋ผ๋ฏธํฐ์ ์์กด์ ์ด์ง ์์ ์ผ๋ฐ ํด๋์ค์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ( ex)
List.clear,List.size,Class<?>)
public static void printList(List<Object> list) {
for (Object elem : list)
System.out.println(elem + " ");
System.out.println();
}์ ๋ฉ์๋์ ๋ชฉํ๋ ์ด๋ ํ ํ์ ์ ๋ฆฌ์คํธ๊ฐ ์ค๋๋ผ๋ ๊ทธ ์์๋ฅผ ์ถ๋ ฅํ๋ ๊ฒ์ด๋ค. ํ์ง๋ง, ์ ์์ ๋ ํ๊ฐ์ง ๋ฌธ์ ์ ์ด ์๋ค.
List<Integer> li = Arrays.asList(1, 2, 3);
List<String> ls = Arrays.asList("one", "two", "three");
printList(li);
printList(ls);java: incompatible types: java.util.List<java.lang.Integer> cannot be converted to java.util.List<java.lang.Object>List<Object> ์ธ์ List<Integer>, List<String>, List<Double>์ ์ถ๋ ฅ์ java: incompatible types: ์ปดํ์ผ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉฐ ์คํจํ๋ค. ์๋ํ๋ฉด List<Object>์ ํ์ํ์
์ด ์๋๊ธฐ ๋๋ฌธ์ด๋ค.
public static void printList(List<?> list) {
for (Object elem: list)
System.out.print(elem + " ");
System.out.println();
}๋นํ์ ์ ์์ผ๋์นด๋ ํ์
์ ์ฌ์ฉํ๋ค๋ฉด, ์ฑ๊ณต์ ์ผ๋ก ์ถ๋ ฅ๋๋ ๊ฒ์ ์ ์ ์๋ค. ์๋ํ๋ฉด, ์ด๋ ํ ํ์
A๊ฐ ์๋ List<A>๋ List<?>์ ํ์ ํ์
์ด๊ธฐ ๋๋ฌธ์ด๋ค.
List<Integer> li = Arrays.asList(1, 2, 3);
List<String> ls = Arrays.asList("one", "two", "three");
printList(li);
printList(ls);๋นํ์ ์ ์์ผ๋์นด๋ List<?> ์์ ๊ฐ์ ธ์จ ์์๋ Object ํ์
์ด๋ค.
public static void get(List<?> list) {
Object obj = list.get(0);
Integer integer = list.get(0); // compile error
}๋นํ์ ์ ์์ผ๋ ์นด๋๋ ์ด๋ ํ ํ์
์ด ์๋ ์ฝ์ ์ ์๋๋ก, ๋ชจ๋ ํ์
์ ๊ณตํต ์กฐ์์ธ Object ๋ก ๋ฐ๋๋ค.
์ฌ๊ธฐ์ ์ฃผ์ํ ์ ์ List<Object> ์ List<?>์ด ๊ฐ์ง ์์ผ๋ฉฐ, List<Object>์๋ Object ์ ํ์ ํ์
์ ๋ชจ๋ ๋ฃ์ ์ ์์ง๋ง, List<?> ์๋ ์ค์ง null๋ง ๋ฃ์ ์ ์๋ค. ์๋ํ๋ฉด ๋น๊ฒฝ๊ณ ์์ผ๋ ์นด๋์ ์์๊ฐ ์ด๋ ํ ํ์
์ด ๋ค์ด์ค๋ ์ง ์ ์ ์์ผ๋ฏ๋ก, ํ์
์์ ์ฑ์ ์งํค๊ธฐ ์ํด null๋ง ์ถ๊ฐํ ์ ์๋ค.
List<?> list = new ArrayList<>();
list.add(null); // ๊ฐ๋ฅ
list.add("test"); // ์ปดํ์ผ ์ค๋ฅ๋ง์ฝ ๋ค์๊ณผ ๊ฐ์ด ๋ชจ๋ ํ์
์ ๋ฃ์ ์ ์๊ฒ ํ๋ค๋ฉด, List<Integer>์ Double์ ์ถ๊ฐํ๋ ๋ชจ์ ๋ฐ์ํ๊ฒ ๋๋ค. ์ด๋ ์ ๋ค๋ฆญ์ ํ์
์์ ์ฑ์ ์๋ฐํ๊ฒ ๋๋ฉฐ, null ๋ง ์ถ๊ฐํ ์ ์๋๋ก ํ๋ค.
public static void main(String[] args) {
List<Integer> ints = new ArrayList<>();
addDouble(ints);
}
private static void addDouble(List<?> ints){
ints.add(3.14); // List<Integer>์ Double์ ์ถ๊ฐํ๋ ๋ชจ์ ๋ฐ์
}Upper Bounded Wildcards
์ํ ๊ฒฝ๊ณ ์์ผ๋ ์นด๋๋ฅผ ์ฌ์ฉํด ๋ณ์์ ๋ํ ์ ํ์ ์ํํ ์ ์๋ค. ์๋ฅผ ๋ค์ด, List<Integer>, List<Double>, List<Number> ์์๋ง ๋์ํ๋ ๋ฉ์๋๋ฅผ ์ํ ๋ ์ํ ๊ฒฝ๊ณ ์์ผ๋ ์นด๋๋ฅผ ์ฌ์ฉํ๋ฉด๋๋ค.
List<? extends Number>List<Number> ๋ ์ค์ง List<Number> ๋ง ์ฌ ์ ์์ผ๋ฏ๋ก, Number ๋ฐ Number์ ํ์ํด๋์ค๊ฐ ๋ชจ๋ ์ฌ ์ ์๋ List<? extends Number> ๋ณด๋ค ๋ ์ ํ์ ์ด๋ค.
์ฆ, <? extends T>๋ T์ ํ์ ํ์
๋ง ์ฌ ์ ์๋ค.
<? extends T>์์ Getํ ์์๋ T ์ด๋ค.
<? extends T>์์ Getํ ์์๋ T ์ด๋ค.์ํ ๊ฒฝ๊ณ ์์ผ๋ ์นด๋์ ์์๋ T ํน์ T์ ํ์ ํด๋์ค์ด๋ฉฐ, ์์๋ค์ ์ต๊ณ ๊ณตํต ์กฐ์์ธ T๋ก ์ฝ์ผ๋ฉด ์ด๋ค ํ์
์ด ์ค๋ T๋ก ์ฝ์ ์ ์๋ค.
public static void printList(List<? extends Number> list) {
for (Number elem : list) {
System.out.println(elem);
}
}์ฌ๊ธฐ์ ๋ง์ฝ ํ์ ํ์
์ธ Integer๋ก ํ๋ฉด ์ปดํ์ผ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค. ์๋ํ๋ฉด, Double์ Number๋ก ๊ฐ์ด ๋ค์ด์ค๊ฒ ๋๋ฉด, Integer๋ก ํ์
์ ๋ณํํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
public static void printList(List<? extends Number> list) {
for (Integer elem : list) { //compile error
System.out.println(elem);
}
}List<? extends T> ์๋ null๋ง ์ฝ์
ํ ์ ์๋ค.
List<? extends T> ์๋ null๋ง ์ฝ์
ํ ์ ์๋ค.์ํ๊ฒฝ๊ณ ์์ผ๋์นด๋์ ์์๊ฐ ์ด๋ค ํ์
์ธ์ง ์ ์ ์๊ธฐ ๋๋ฌธ์ null ๋ง ์ฝ์
ํ ์ ์๋ค.
List<Integer> ints = new ArrayList<>();
List<? extends Number> numbers = ints;
numbers.add(Double.valueOf(3.14)); // compile errorLower Bounded Wildcards
<? super T> ์ ํํ๋ก, List<? super T> ์ ๊ฐ์ด ์ฌ์ฉํ๋ค. T ํน์ T์ ์์ ํด๋์ค๋ง ์ธ์๋ก ์ฌ ์ ์๋ค.
<? super T>์์ Getํ ์์๋ Object ์ด๋ค.
<? super T>์์ Getํ ์์๋ Object ์ด๋ค.T ํํ ๊ฒฝ๊ณ ์์ผ๋์นด๋์ ์์๋ T์ ์์ ํด๋์ค ์ค ์ด๋ ํ ํ์
๋ ์ฌ ์ ์๋ค. ์ด๋ ํ ํ์
์ด ์๋ ์ฝ์ ์ ์๋๋ก, T๋ค์ ๊ณตํต ์กฐ์์ธ Object๋ก ๋ฐ๋๋ค. List<Integer>, List<Double>, List<Number>๊ฐ ์๋ ๋ชจ๋ ์ฝ์ ์ ์๋ค.
public static void printList(List<? super Integer> list) {
for (Object elem : list) {
System.out.println(elem);
}
}List<? super T> ์๋ T์ ํ์ ํด๋์ค๋ง ์ฝ์
ํ ์ ์๋ค.
List<? super T> ์๋ T์ ํ์ ํด๋์ค๋ง ์ฝ์
ํ ์ ์๋ค.List<? super Integer> ints = new ArrayList<>();
ints.add(new Integer());
ints.add(new Number()); // compile error๋ง์ฝ ints๊ฐ List<Integer> ์ผ ๊ฒฝ์ฐ Number๋ Integer์ ์์ ํด๋์ค ์ด๋ฏ๋ก ์์๋ฅผ ์ถ๊ฐํ ์ ์๋ค. List<Integer>, List<Number>, List<Object> ์ค ์ด๋ ํ ๋ฆฌ์คํธ๊ฐ ์ฌ์ง ints๋ ์์ง ๋ชปํ๋ค. ํ์ง๋ง ๊ทธ ์ค ์ด๋ ํ ๋ฆฌ์คํธ๊ฐ ์ค๋๋ผ๋, Integer์ ํ์ ํด๋์ค๋ ์์๋ก ์ถ๊ฐํ ์ ์๋ค.
Wildcards and Subtyping
๋น์ ๋ค๋ฆญ ํด๋์ค์์๋ ์์ ํด๋์ค์ ํ์ ํด๋์ค๋ฅผ ๋์ ํ ์ ์๋ค.
class A { /* ... */ }
class B extends A { /* ... */ }B b = new B();
A a = b; // OKํ์ง๋ง, ์ ๋ค๋ฆญ ํด๋์ค์์๋ ์ปดํ์ผ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค.
List<B> lb = new ArrayList<>();
List<A> la = lb; //compile error์๋ํ๋ฉด List<B>๋ List<A>์ ํ์ ํ์
์ด ์๋๋ฉฐ, ์๋ฌด๋ฐ ๊ด๊ณ๊ฐ ์๋ค. List<B>์ List<A>์ ๊ณตํต ์กฐ์์ List<?>์ด๋ค. ๊ทธ๋ ๋ค๋ฉด, B์ A์ ์์๋ฅผ ๋ฐ๋ ์ ๋ค๋ฆญ ํ์
์ ๋ง๋ค๊ณ ์ถ์๋๋ ์์ ๊ฒฝ๊ณ ์์ผ๋์นด๋๋ฅผ ์ฌ์ฉํ๋ฉด๋๋ค.
List<? extends Integer> ints = new ArrayList<>();
List<? extends Number> numbers = ints; // OKList<? extends Integer>๋ List<? extends Number>์ ํ์ ํ์
์ด๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฅํ๋ค. ์๋ํ๋ฉด Integer์ ํ์ ํ์
์ Number์ ํ์ํ์
์ด๊ธฐ ๋๋ฌธ์, ์๋์ ๊ฐ์ ๊ด๊ณ๊ฐ ์ฑ๋ฆฝ๋๋ค.

Wildcard Capture
์ปดํ์ผ๋ฌ๋ ์ด๋ ํ ๊ฒฝ์ฐ์, ์์ผ๋์นด๋์ ํ์
์ ์ถ๋ก ํ๋ค. ์๋ฅผ๋ค์ด, List<?> ๋ฆฌ์คํธ๊ฐ ์ ์๋์ด์์๋, ์ปดํ์ผ๋ฌ๋ ์ฝ๋์์ ํน์ ํ์
์ ์ถ๋ก ํ๋ค. ์ด๋ฌํ ์๋๋ฆฌ์ค๋ฅผ ์์ผ๋์นด๋ ์บก์ฒ๋ผ๊ณ ํ๋ค.
"capture of" ์ค๋ฅ ๋ฌธ๊ตฌ๋ฅผ ์ ์ธํ๊ณ ๋ ์์ผ๋์นด๋ ์บก์ฒ๋ฅผ ์ ๊ฒฝ์ฐ์ง ์์๋ ๋๋ค.
public class WildcardError {
void foo(List<?> i) {
i.set(0, i.get(0)); // capture of compile error
}
}์ ์์ ์์๋ foo() ๋ฉ์๋์์ List.set(int, E)๋ฅผ ํธ์ถํ ๋ ์ปดํ์ผ๋ฌ๋ List์ ์ฝ์
๋๋ ๊ฐ์ฒด์ ์ ํ์ ํ์ธํ ์ ์๊ธฐ ๋๋ฌธ์ ์ค๋ฅ๋ฅผ ๋ฐ์์ํจ๋ค. ์ด๋ฌํ ์ ํ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด ์ผ๋ฐ์ ์ผ๋ก ์ปดํ์ผ๋ฌ๋ ๋ณ์์ ์๋ชป๋ ํ์
์ ๊ฐ์ ํ ๋นํ๊ณ ์๋ค๊ณ ๋ฏฟ๋๋ค๋ ๊ฒ์ ์๋ฏธํ๋ค. ์ด๋ ๊ฒ ์๋ฐ ์ปดํ์ผํ์์ ํ์
์์ ์ ์ถ๊ฐํ๊ธฐ์ํด ์ ๋ค๋ฆญ์ด ์ถ๊ฐ๋ ๊ฒ์ด๋ค.
public class WildcardFixed {
void foo(List<?> i) {
fooHelper(i);
}
// Helper method created so that the wildcard can be captured
// through type inference.
private <T> void fooHelper(List<T> l) {
l.set(0, l.get(0));
}
}์ด๋ฌํ ๊ฒฝ์ฐ fooHelper() ์ฒ๋ผ private helper method๋ฅผ ๋ง๋ค์ด์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ค. helper ๋ฉ์๋ ๋๋ถ์ ์ปดํ์ผ๋ฌ๋ T๊ฐ ์บก์ณ ๋ณ์์ธ CAP#1 ์์ ์ถ๋ก ํ ์ ์๋ค. ์ผ๋ฐ์ ์ผ๋ก helper ๋ฉ์๋๋ originalMethodNameHelper๋ก ์ง์ ๋๋ค.
Type Erasure
์ ๋ค๋ฆญ์ ํ์ ์ ์์ ์ฑ์ ๋ณด์ฅํ๋ฉฐ, ์คํ์๊ฐ์ ์ค๋ฒํค๋๊ฐ ๋ฐ์ํ์ง ์๋๋ก ํ๊ธฐ์ํด ์ถ๊ฐ๋๋ค. ์ปดํ์ผ๋ฌ๋ ์ปดํ์ผ ์์ ์ ์ ๋ค๋ฆญ์ ๋ํด ์์ ํ์ ์๊ฑฐ(type erasure)๋ฅผ ํ๋ค. ์ฆ, ์ปดํ์ผ ํ์์๋ง ํ์ ์ ์ฝ ์กฐ๊ฑด์ ์ ์ํ๊ณ , ๋ฐํ์์๋ ํ์ ์ ์ ๊ฑฐํ๋ค๋ ๋ป์ด๋ค.
unbounded Type(
<?>,<T>)์Object๋ก ๋ณํbound type(
<E extends Comparable>)์ ๊ฒฝ์ฐ๋ Object๊ฐ ์๋Comprarable๋ก ๋ณํ์ ๋ค๋ฆญ ํ์ ์ ์ฌ์ฉํ ์ ์๋ ์ผ๋ฐ ํด๋์ค, ์ธํฐํ์ด์ค, ๋ฉ์๋์๋ง ์๊ฑฐ ๊ท์น์ ์ ์ฉ
ํ์ ์์ ์ฑ ๋ณด์กด์ ์ํด ํ์์ type casting
ํ์ฅ๋ ์ ๋ค๋ฆญ ํ์ ์์ ๋คํ์ฑ์ ๋ณด์กดํ๊ธฐ์ํด bridge method ์์ฑ
ํ๋์ฉ ์์ ๋ฅผ ๋ณด๋ฉด์ ์์๋ณผ ๊ฒ์ด๋ค.
unbounded Type(<?>, <T>)์ Object๋ก ๋ณํ
<?>, <T>)์ Object๋ก ๋ณํ// ํ์
์๊ฑฐ ์ด์
public class Node<T> {
private T data;
private Node<T> next;
public Node(T data, Node<T> next) {
this.data = data;
this.next = next;
}
public T getData() { return data; }
// ...
}// ๋ฐํ์(ํ์
์๊ฑฐ ํ)
public class Node {
private Object data;
private Node next;
public Node(Object data, Node next) {
this.data = data;
this.next = next;
}
public Object getData() { return data; }
// ...
}type erasure๊ฐ ์ ์ฉ๋๋ฉด์ ํน์ ํ์
์ผ๋ก ์ ํ๋์ง ์์ <T>๋ ๋ค์๊ณผ ๊ฐ์ด Object๋ก ๋์ฒด๋๋ค.
์ ๋ค๋ฆญ ๋ฉ์๋์์๋ ๋์ผํ๋ค.
public static <T> int count(T[] anArray, T elem) {
int cnt = 0;
for (T e : anArray)
if (e.equals(elem))
++cnt;
return cnt;
}public static int count(Object[] anArray, Object elem) {
int cnt = 0;
for (Object e : anArray)
if (e.equals(elem))
++cnt;
return cnt;
}T ๋ ๋นํ์ ์ ํ์
์ด๋ฏ๋ก, ์ปดํ์ผ๋ฌ๊ฐ Object ๋ก ๋ณํํ๋ค.
bound type(<E extends T>)์ ๊ฒฝ์ฐ๋ Object๊ฐ ์๋ T๋ก ๋ณํ
<E extends T>)์ ๊ฒฝ์ฐ๋ Object๊ฐ ์๋ T๋ก ๋ณํ// ์ปดํ์ผ ํ ๋ (ํ์
๋ณํ ์ )
public class Node<T extends Comparable<T>> {
private T data;
private Node<T> next;
public Node(T data, Node<T> next) {
this.data = data;
this.next = next;
}
public T getData() { return data; }
// ...
}// ๋ฐํ์ ์
public class Node {
private Comparable data;
private Node next;
public Node(Comparable data, Node next) {
this.data = data;
this.next = next;
}
public Comparable getData() { return data; }
// ...
}ํ์ ๋ ํ์
(bound type)์์๋ ์ปดํ์ผ ์์ ์ ์ ํ๋ ํ์
์ผ๋ก ๋ณํ๋๋ค. Comparable ๋ก ๋ณํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
public static <T extends Shape> void draw(T shape) { /* ... */ }public static void draw(Shape shape) { /* ... */ }์ฌ๊ธฐ์๋ Shape ๋ก ๋ณํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
ํ์ฅ๋ ์ ๋ค๋ฆญ ํ์
์์ ๋คํ์ฑ์ ๋ณด์กดํ๊ธฐ์ํด bridge method ์์ฑ
์ปดํ์ผ๋ฌ๊ฐ ์ปดํ์ผ ์์ ์ ์ ๋ค๋ฆญ ํ์ ์์ ์ฑ์ ์ํด bridge method๋ฅผ ์์ฑํ ์ ์๋ค. ๋ค์ ์์ ๋ฅผ ์ดํด๋ณด์.
public class Node<T> {
public T data;
public Node(T data) { this.data = data; }
public void setData(T data) {
System.out.println("Node.setData");
this.data = data;
}
}
public class MyNode extends Node<Integer> {
public MyNode(Integer data) { super(data); }
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
}์ ๋๊ฐ์ ํด๋์ค๊ฐ ์๋ค. ์ด๋ ๋ค์๊ณผ ์ฝ๋๋ฅผ ์คํํด์ผํ๋ค๊ณ ์๋ฅผ ๋ค์ด๋ณด์.
MyNode mn = new MyNode(5);
Node n = mn; // A raw type - compiler throws an unchecked warning
n.setData("Hello"); // Causes a ClassCastException to be thrown.
Integer x = mn.data;ํ์
์ด ์๊ฑฐ๋ ํ์๋ ๋ค์๊ณผ ๊ฐ์ด ์ ์ฉ๋๋ฉฐ,๋ฐํ์์ ClassCastException ๋ฅผ ๋ฐ์์ํค๊ฒ ๋๋ค.
MyNode mn = new MyNode(5);
Node n = (MyNode)mn; // A raw type - compiler throws an unchecked warning
n.setData("Hello"); // Causes a ClassCastException to be thrown.
Integer x = (String)mn.data;ํ์
์๊ฑฐ ํ์ Node์ MyNode๋ ๋ค์๊ณผ ๊ฐ์ด ๋ณํ๋๋ ๊ฒ์ ๋ณผ ์ ์์ผ๋ฉฐ, ์๊ฑฐ ํ์๋ Node ์๊ทธ๋์ฒ ๋ฉ์๋๊ฐ setData(T data) ์์ setData(Object data)๋ก ๋ฐ๊พธ๊ธฐ ๋๋ฌธ์ MyNode ์ setData(Integer data)๋ฅผ overriding ํ ์ ์๊ฒ ๋๋ค.
public class Node {
public Object data;
public Node(Object data) { this.data = data; }
public void setData(Object data) {
System.out.println("Node.setData");
this.data = data;
}
}
public class MyNode extends Node {
public MyNode(Integer data) { super(data); }
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
}๋ฐํ์ ์์๋ ๋ค์๊ณผ ๊ฐ์ด ํ์
์ด ์๊ฑฐ๋ ์ํ๋ก ๋ณํ ๊ฒ์ด๋ค. (Object๋ก ๋ณํ) ๊ทธ๋ ๊ฒ ๋๋ฉด, Object ๋ก ๋ณํ๊ฒ ๋๋ ๊ฒฝ์ฐ์ ๋ํ ๋ถ์ผ์น๋ฅผ ์์ ๊ธฐ ์ํด ์ปดํ์ผ๋ฌ๋ ๋ฐํ์์ ํด๋น ์ ๋ค๋ฆญ ํ์
์ ํ์ ์๊ฑฐ๋ฅผ ์ํ bridge method๋ฅผ ์์ฑํด์ค๋ค.
class MyNode extends Node {
// Bridge method generated by the compiler
//
public void setData(Object data) {
setData((Integer) data);
}
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
// ...
}๊ทธ๋ ๊ธฐ๋๋ฌธ์ ClassCastException ์์ธ๋ฅผ ๋์ง๋ ๊ฒ์ ์ ์ ์๋ค.
Reifiable vs Non-Reifiable
Reifiable Type
๋ฐํ์์์ ์์ ํ๊ฒ ์ค๋ธ์ ํธ ์ ๋ณด๋ฅผ ํํํ ์ ์๋ ํ์ ์ ์ค์ฒดํ ํ์ (Reifiable Type)์ด๋ผ๊ณ ํ๋ค. ์ฆ, ์ปดํ์ผ ๋จ๊ณ์์ ํ์ ์๊ฑฐ์ ์ํด ์ง์์ง์ง ์๋ ํ์ ์ ๋ณด์ด๋ค.
์์ ํ์ (ex)
int,double,float,byte๋ฑ๋ฑNumber,Integer์ ๊ฐ์ ์ผ๋ฐ ํด๋์ค์ ์ธํฐํ์ด์ค ํ์๋นํ์ ์์ผ๋์นด๋(
?)๊ฐ ํฌํจ๋ ๋งค๊ฐ๋ณ์ํ ํ์ (ex)List<?>,ArrayList<?>Raw type (ex)
List,ArrayList,Map
๋นํ์ ์ ์์ผ๋์นด๋๋ ์ ์ด์ ํ์
์ ๋ณด๋ฅผ ์ ํ ๋ช
์ํ์ง ์์์ผ๋ฏ๋ก, ์ปดํ์ผ์์ ํ์
์๊ฑฐ๋ฅผ ํ๋ค๊ณ ํด๋ ์์ ์ ๋ณด๊ฐ ์๊ธฐ ๋๋ฌธ์ ์ค์ฒดํ ํ์
์ด๋ผ ๋ณผ ์ ์์ผ๋ฉฐ, ํ์
์๊ฑฐ์ ์ํด ์ปดํ์ผ ์์ ์ Object๋ก ๋ณํ๋๋ค.
Non-Reifiable Type
์ปดํ์ผ ๋จ๊ณ์์ ํ์ ์๊ฑฐ์ ์ํด์ ํ์ ์ ๋ณด๊ฐ ์ ๊ฑฐ๋ ํ์ ์ด๋ค. ์ด ๊ฒฝ์ฐ ๋ฐํ์์์ ๋ฐํ์์์ ์ ์ ์๋ ์ ๋ณด๊ฐ ์๋ค.
Generic Type (ex)
List<E>Parameterized Type (ex)
List<Number>,ArrayList<String>Bounded wildcard Type (ex)
List<? extends Number>,List<? super String>
Heap Pollution
ํ ์ค์ผ์ ํ๋ผ๋ฏธํฐํ ๋ ํ์ ์ ๋ณ์๊ฐ ํด๋น ํ๋ผ๋ฏธํฐํ๋ ํ์ ์ด ์๋ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ ๋ ๋ฐ์ํ๋ค.
raw type๊ณผ ํ๋ผ๋ฏธํฐํ๋ ํ์ ์ด ์์ฌ ์ฌ์ฉ๋๋ ๊ฒฝ์ฐ
unchecked cast๊ฐ ์ฌ์ฉ๋ ๊ฒฝ์ฐ
Potential Vulnerabilities of Varargs Methods with Non-Reifiable Formal Parameters
๊ฐ๋ณ์ธ์(varargs) ๋งค๊ฐ๋ณ์๋ฅผ ํฌํจํ ์ ๋ค๋ฆญ ๋ฉ์๋๋ ํ ์ค์ผ์ ์ ๋ฐํ ์ ์๋ค.
public class ArrayBuilder {
public static <T> void addToList (List<T> listArg, T... elements) {
for (T x : elements) {
listArg.add(x);
}
}
public static void faultyMethod(List<String>... l) {
Object[] objectArray = l; // Valid
objectArray[0] = Arrays.asList(42);
String s = l[0].get(0); // ClassCastException thrown here
}
}public class HeapPollutionExample {
public static void main(String[] args) {
List<String> stringListA = new ArrayList<String>();
List<String> stringListB = new ArrayList<String>();
ArrayBuilder.addToList(stringListA, "Seven", "Eight", "Nine");
ArrayBuilder.addToList(stringListB, "Ten", "Eleven", "Twelve");
List<List<String>> listOfStringLists = new ArrayList<List<String>>();
ArrayBuilder.addToList(listOfStringLists, stringListA, stringListB);
ArrayBuilder.faultyMethod(Arrays.asList("Hello!"), Arrays.asList("World!"));
}
}์ปดํ์ผ์์ addToList() ๋ฉ์๋ ์ ์ ๋ถ๋ถ์ ๋ค์๊ณผ ๊ฐ์ ๊ฒฝ๊ณ ๋ฌธ๊ตฌ๊ฐ ๋ฌ๋ค.
Possible heap pollution from parameterized vararg type์ปดํ์ผ์ด ๊ฐ๋ณ์ธ์(varargs) ๋ฉ์๋๋ฅผ ๋ง๋๊ฒ๋๋ฉด ๊ฐ๋ณ์ธ์(varargs) ํ๋ผ๋ฏธํฐ๋ฅผ ๋ฐฐ์ด๋ก ๋ณํ์ํจ๋ค. ํ์ง๋ง, ์๋ฐ๋ ํ๋ผ๋ฏธํฐํ๋ ํ์
์ ๋ฐฐ์ด์ ์์ฑ์ ํ์ฉํ์ง ์๋๋ค. ์ ์์ ์์ ArrayBuilder.addToList ๊ฐ๋ณ์ธ์ ๋งค๊ฐ๋ณ์ T...๋ฅผ T[] ๋ฐฐ์ด๋ก ๋ณํํ๋๋ฐ, ํ์
์๊ฑฐ์ ์ํด์ ์ปดํ์ผ๋ฌ๋ ๊ฐ๋ณ์ธ์ ํ๋ผ๋ฏธํฐ๋ฅผ Object[]๋ก ๋ณํ์ํค๊ฒ๋๊ณ ๊ฒฐ๊ณผ์ ์ผ๋ก ํ์ค์ผ์ ๊ฐ๋ฅ์ฑ์ด ์๊ธฐ๋ ๊ฒ์ด๋ค.
Object[] objectArray = l;๊ตฌ๋ฌธ์ ํ์ค์ผ์ ์ ์ฌ์ ์ผ๋ก ๋ด์ฌํ๊ณ ์๋๋ฐ, ์ปดํ์ผ๋ฌ๋ ๊ฒฝ๊ณ ๋ฌธ๊ตฌ๋ฅผ ์์ฑํ์ง ์๊ณ ์๋ค. ์๋ํ๋ฉด, ์ปดํ์ผ๋ List<String>... l ์ ํ์ ํ๋ผ๋ฏธํฐ List[] ๋ก ๋ณํํ ๋ ์ด๋ฏธ ๊ฒฝ๊ณ ๋ฅผ ๋ง๋ค์๊ธฐ ๋๋ฌธ์ด๋ค. ์ฆ, ๋ณ์ l์ Object[]์ ํ์ ํ์
์ธ List[] ํ์
์ ๊ฐ๊ฒ๋๊ณ ์ด ๊ตฌ๋ฌธ์ ์ ํจํ๊ฒ ๋๋ ๊ฒ์ด๋ค.
objectArray[0] = Arrays.asList(42);์ฌ๊ธฐ์ objectArray์ ์ฒซ๋ฒ์งธ ์์์ Integerํ์
์ ๊ฐ์ฒด ๋ฆฌ์คํธ๋ฅผ ํ ๋นํ๊ณ ,
ArrayBuilder.faultyMethod(Arrays.asList("Hello!"), Arrays.asList("World!"));ArrayBuilder.faultyMethod ๋ฅผ ํธ์ถํ๋ค๋ฉด, ๋ฐํ์์์ ClassCastException ์ค๋ฅ๋ฅผ ๋ฐ์์ํค๋ ๊ฒ์ด๋ค.
Prevent Warnings from Varargs Methods with Non-Reifiable Formal Parameters
ํ๋ผ๋ฏธํฐํ ํ์
์ ๊ฐ๋ณ์ธ์ ๋ฉ์๋๊ฐ ClassCastException ์ ๋ฐ์์ํค์ง ์๊ณ , ๋น์ทํ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ง ์๋ ๋ค๋ ๊ฒ์ด ๋ณด์ฅ๋๋ฉด(ํ์
์์ ์ฑ์ด ๋ณด์ฅ) static๊ณผ ์์ฑ์๊ฐ ์๋ ๋ฉ์๋ ์ ์ธ๋ถ์@SafeVarargs ์ด๋
ธํ
์ด์
์ ์ถ๊ฐํด ๊ฒฝ๊ณ ๋ฌธ๊ตฌ๋ฅผ ์ง์ธ ์ ์๋ค. ์ด ์ด๋
ธํ
์ด์
์ ๋ฉ์๋ ๊ตฌํ์์ varargs ํ์ ํ๋ผ๋ฏธํฐ์ ๋ํด ๋ถ์ ์ ํ ์ฒ๋ฆฌ๋ฅผํ์ง ์์์์ ๋ํ๋ธ๋ค. ์ข์ ๋ฐฉ๋ฒ์ ์๋์ง๋ง ๋ฉ์๋ ์ ์ธ์ ๋ค์๊ณผ ๊ฐ์ ๋ฐฉ๋ฒ์ผ๋ก๋ ๊ฒฝ๊ณ ๋ฌธ๊ตฌ๋ฅผ ์ต์ ํ ์ ์๋ค.
@SuppressWarnings({"unchecked", "varargs"})๋จ, ์ด๊ฒฝ์ฐ์๋ ๋ฉ์๋ ํธ์ถ ๋ถ๋ถ์์ ์์ฑ๋๋ ๊ฒฝ๊ณ ๋ฅผ ๋ง์์ฃผ์ง๋ ์๋๋ค.
Generic Type์ ์์๊ณผ ๊ตฌํ
์ ๋ค๋ฆญ ํ์ ๋ ๋ถ๋ชจ ํด๋์ค๊ฐ ๋ ์ ์๋ค.
public class ChildProduct<T,M> extends Product<T,M>{...}์์ ์ ๋ค๋ฆญ ํ์ ์ ์ถ๊ฐ์ ์ผ๋ก ํ์ ํ๋ผ๋ฏธํฐ๋ฅผ ๊ฐ์ง ์ ์๋ค.
public class ChildProduct<T,M,C> extends Product<T,M>{...}์ ๋ค๋ฆญ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ํด๋์ค๋ ์ ๋ค๋ฆญ ํ์ ์ด๋๋ค.
public interface Storage<T>{
public void add(T item, int index);
public T get(int index);
}public class StorageImpl<T> implements Storage<T>{
private T[] array;
public StorageImpl(int capacity){
this.array = (T[])(new Object[capacity]);
}
@Override
public void add(T item, int index){
array[index] = item;
}
@Override
public T get(int index){
return array[index];
}
}์ฐธ๊ณ
Last updated
Was this helpful?