참조: Effective Java 2nd Edition. Item 23:Don't use raw types in new code

먼저 용어 정리

class List<E> {
...
}

List<String> stList = ~~~~;

이런 코드가 있을 때...
- List<E>: Generic type
- List<String>: Parameterized type
- E: Formal type parameter
- String: Actual type parameter
- List: Raw type (제목에 쓴게 이거임)
- List<?>: Unbounded wildcard type
- 나머진 패스.

Generic 장점
- 컴파일 시에 타입 안전성을 보장한다.
- 부가적인 장점으로 컬렉션에 들어있는 것을 꺼낼 때 캐스팅할 필요가 없다.

그럼 Generic을 꼭 쓰게 하지 왜 Raw Type으로 쓸 수 있게 해뒀냐?
- 호환성 때문에. 이미 1.5전 버전 기준으로 만들어 놓은 코드가 많으니까.

Generic 특성
- List<String>은 List의
하위 타입이지만 List<Object>의 하위 타입은 아니다.
- 다음 item에서 더 자세히 설명 나옴.

Raw Type을 사용했을 때 발생할 수 있는 문제

    public static void main(String[] args) {
        List<String> strings = new ArrayList<String>();
        unsafeAdd(strings, new Integer(42));
        String s = strings.get(0); // Compiler-generated cast
    }

    private static void unsafeAdd(List list, Object o) {
        list.add(o);
    }

굵은 글씨에서 타입을 명시하지 않았기 때문에 list.add(o); 여기서 일단 컴파일 경고가 발생하지만 일단 무시하고 실행할 수는 있다 막상 실행하면 ClassCastException 발생한다. 하지만 만약 저기서 List<String>이라고 타입을 명시했다면 애초에 컴파일도 못했을 것이다.

어떤 종류가 들어있는지 모르는 컬렉션을 처리하는 메서드에서는 unbounded wildcard type을 사용하자.
   
    //위험
    static int numElementsInCommon(Set s1, Set s2) {
        int result = 0;
        for (Object o1 : s1)
            if (s2.contains(o1))
                result++;
        return result;
    }

    //안전
    static int numElementsInCommon(Set<?> s1, Set<?> s2) {
        int result = 0;
        for (Object o1 : s1)
            if (s2.contains(o1))
                result++;
        return result;
    }

위에껀 왜 위험하고 아래껀 왜 안전할까? 안전한 녀석에는 무언가 추가할 수가 없다. 위험한 녀석에는 아무 객체나 넣을 수 있지만 unbounded wildcard type을 사용한 경우에는 컬렉션에 아무것도 추가할 수 없다. 컴파일 에러다.

왜? Actual type parameter가 뭔지 모르는데 뭘 집어 넣을 수 있을까.. 생각해보면 당연하다.

Type parameter를 사용할 수 없는 경우
- List<String>.class
- instanceof