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.Serializable
public 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 type
Type 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 error
Lower 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; // OK
List<? 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?