우리가 제네릭 타입을 정의할 때
List<Integer> list = new ArrayList<>();
와 같이 List<E>로 정의한다. 읽을 때는 List of E 로 읽는다. 그러나 꺽쇠 없이, 매개변수 타입을 정하지 않고 List 타입만을 사용하면 이를 로 타입이라 한다.
로 타입을 사용하면 제네릭을 사용한다고 할 수 없다. 그리고 타입의 불안정성이 그대로 노출된다. 우리가 List에 매개변수 정의가 없다면 서로 관련 없는 타입을 넣어도 unchecked call 이란 경고만 나타나고 컴파일 시에는 어떤 문제도 생기지 않는다. 그러나 런타임시에 의도치 못한 런타임 에러가 발생할 수 있다. 이와 같이 타입에 대한 안정성을 컴파일단계에서 확인을 못한다면 굳이 정적 타입 언어인 자바를 쓸 이유가 없다.
자바에서는 로타입을 허용한다. 여러 문제점이 있음에도 허용한 이유는 기존 코드의 내용을 수용하면서 새로운 코드와도 잘 동작을 해야하기 때문이다. 그러나 실제 개발할 때는 로타입은 정적타입에 맞는 타입이 아닐 뿐 더러 여러 잠재 위험성이 있기 때문에 절대로 쓰지 말자.
Object를 사용하면 다양한 타입을 모든 타입의 슈퍼 타입인 Object으로 형변환해서 보관할 수 있고 전혀 관련 없는 형변환 및 호출을 예방할 수 있다.
예를 들어 List에 정수와 문자열 타입을 넣었다고 가정하자. 그러나 개발자 실수로 List에서 값을 꺼내서 10을 더한후 출력한다고 하면, 실패할 것이다. 당연하다. 그러나 로타입의 경우 뒤늦게 런타임에 알게 된다. 우연히 아다리가 맞는 경우 경고만 뜰 뿐 런타임 예외도 뜨지 않을 수도 있다.
비한정적 와일드카드 타입이란 List<?>와 같이 사용하는 것을 말한다. ?가 가진 의미는 어떤 타입이든 될 수 있지만 타입의 불변식을 훼손하지는 못한다. 무슨 의미냐면 모든 타입을 넣을 수 있는 것이 아니라 어떤 매개 변수로 정하든 유연하게 처리하겠다는 의미이다. List, List 이런식으로 받아서 처리하겠다는 의미이다.
주로 메서드 제네릭 타입을 선언 할 때 많이 사용한다.
클래스 리터럴에 경우 허용한다. 클래스 리터럴은 주로 리플렉션을 활용할 때 사용하는데 예를 들면 List.class, String[].class, int.class와 같은 것들이다. 이들은 List.class, List<?>.class와 같은 리터럴을 허용하지 않는다.
또 다른 곳에 사용해도 되는 곳은 instanceof 이다. 이는 타입을 체크할 때 쓰는 예약어인데 이 경우는 로 타입을 사용해도 좋다.
ex)
if (o instanceof Set) {
Set<?> s = (Set<?>) o;
}
이렇게 사용하는 이유는 굳이 지저분하게 꺽쇠로 붙일 필요도 없고 제네릭은 사실 컴파일이 끝나면 매개변수 타입은 아무 의미가 없다. 그저 모두 Set, List와 같이 받아드릴 뿐이다. 그렇기에 o 라는 타입이 Set의 sub타입임을 검증하는 것이라면 로타입만 써도 무방하다.