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