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 ์ตœ์ƒ์œ„ํƒ€์ž…>)

ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ์— ์ง€์ •๋˜๋Š” ๊ตฌ์ฒด์ ์ธ ํƒ€์ž…์„ ์ œํ•œํ•  ํ•„์š”๊ฐ€ ์ข…์ข… ์žˆ๋‹ค. ์ˆซ์ž๋ฅผ ์—ฐ์‚ฐํ•˜๋Š” ์ œ๋„ค๋ฆญ ๋ฉ”์†Œ๋“œ์˜ ๋งค๊ฐœ๊ฐ’์œผ๋กœ๋Š” 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<?> ์™€ ๊ฐ™์ด ? ์˜ ํ˜•ํƒœ๋กœ ์ •์˜๋˜๋ฉฐ, ๋น„ํ•œ์ •์  ์™€์ผ๋“œ์นด๋“œ ํƒ€์ž…์ด ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  1. Object ํด๋ž˜์Šค์—์„œ ์ œ๊ณต๋˜๋Š” ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒฝ์šฐ

  2. ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ์— ์˜์กด์ ์ด์ง€ ์•Š์€ ์ผ๋ฐ˜ ํด๋ž˜์Šค์˜ ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ( 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 ์ด๋‹ค.

์ƒํ•œ ๊ฒฝ๊ณ„ ์™€์ผ๋“œ ์นด๋“œ์˜ ์›์†Œ๋Š” 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๋งŒ ์‚ฝ์ž…ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ƒํ•œ๊ฒฝ๊ณ„ ์™€์ผ๋“œ์นด๋“œ์˜ ์›์†Œ๊ฐ€ ์–ด๋–ค ํƒ€์ž…์ธ์ง€ ์•Œ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— 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 ์ด๋‹ค.

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 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๋กœ ๋ณ€ํ™˜

// ํƒ€์ž… ์†Œ๊ฑฐ ์ด์ „
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๋กœ ๋ณ€ํ™˜

// ์ปดํŒŒ์ผ ํ•  ๋•Œ (ํƒ€์ž… ๋ณ€ํ™˜ ์ „) 
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)์ด๋ผ๊ณ  ํ•œ๋‹ค. ์ฆ‰, ์ปดํŒŒ์ผ ๋‹จ๊ณ„์—์„œ ํƒ€์ž…์†Œ๊ฑฐ์— ์˜ํ•ด ์ง€์›Œ์ง€์ง€ ์•Š๋Š” ํƒ€์ž… ์ •๋ณด์ด๋‹ค.

  1. ์›์‹œ ํƒ€์ž… (ex) int, double, float, byte ๋“ฑ๋“ฑ

  2. Number, Integer์™€ ๊ฐ™์€ ์ผ๋ฐ˜ ํด๋ž˜์Šค์™€ ์ธํ„ฐํŽ˜์ด์Šค ํƒ€์ž…

  3. ๋น„ํ•œ์ • ์™€์ผ๋“œ์นด๋“œ(?)๊ฐ€ ํฌํ•จ๋œ ๋งค๊ฐœ๋ณ€์ˆ˜ํ™” ํƒ€์ž… (ex) List<?>, ArrayList<?>

  4. Raw type (ex) List, ArrayList, Map

๋น„ํ•œ์ •์  ์™€์ผ๋“œ์นด๋“œ๋Š” ์• ์ดˆ์— ํƒ€์ž… ์ •๋ณด๋ฅผ ์ „ํ˜€ ๋ช…์‹œํ•˜์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ, ์ปดํŒŒ์ผ์‹œ์— ํƒ€์ž… ์†Œ๊ฑฐ๋ฅผ ํ•œ๋‹ค๊ณ  ํ•ด๋„ ์žƒ์„ ์ •๋ณด๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์‹ค์ฒดํ™” ํƒ€์ž…์ด๋ผ ๋ณผ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ํƒ€์ž… ์†Œ๊ฑฐ์— ์˜ํ•ด ์ปดํŒŒ์ผ ์‹œ์ ์— Object๋กœ ๋ณ€ํ™˜๋œ๋‹ค.

Non-Reifiable Type

์ปดํŒŒ์ผ ๋‹จ๊ณ„์—์„œ ํƒ€์ž…์†Œ๊ฑฐ์— ์˜ํ•ด์„œ ํƒ€์ž… ์ •๋ณด๊ฐ€ ์ œ๊ฑฐ๋œ ํƒ€์ž…์ด๋‹ค. ์ด ๊ฒฝ์šฐ ๋Ÿฐํƒ€์ž„์‹œ์— ๋Ÿฐํƒ€์ž„์‹œ์— ์•Œ ์ˆ˜ ์žˆ๋Š” ์ •๋ณด๊ฐ€ ์—†๋‹ค.

  1. Generic Type (ex) List<E>

  2. Parameterized Type (ex) List<Number>, ArrayList<String>

  3. 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