import java.util.ArrayList;
import java.util.List;
public class BeforeGenericExample {
public static void main(String[] args) {
List<Object> items = new ArrayList();
items.add("사과");
items.add(100);
Object first = items.get(0);
String fruit = (String) first;
System.out.println(fruit.toUpperCase());
// 여기서 문제가 생깁니다.
// 두 번째 값은 Integer인데, 실수로 String으로 형변환하고 있습니다.
// 컴파일은 되지만 실행 중 ClassCastException이 발생합니다.
String secondFruit = (String) items.get(1);
System.out.println(secondFruit.toUpperCase());
}
}
Object는 무엇이든 담을 수 있지만, 무엇이 들어 있는지는 보장하지 못합니다.처음에는 "유연해서 편하다"라고 느껴질 수 있습니다.
하지만 실무에서는 이런 유연함이 오히려 오류를 발생시켜 불안함이 됩니다.
여기서 Generic이 등장합니다.
"이 상자에는 어떤 타입만 담길지 미리 알려주자."
import java.util.ArrayList;
import java.util.List;
public class AfterGenericExample {
public static void main(String[] args) {
// List<String>은 "문자열만 담는 리스트"라는 뜻입니다.
List<String> fruits = new ArrayList<>();
// 문자열은 정상적으로 저장됩니다.
fruits.add("사과");
// 아래 코드는 컴파일 단계에서 막힙니다.
// 즉, 실행하기 전에 실수를 발견할 수 있습니다.
// fruits.add(100);
// 꺼낼 때도 이미 String 타입으로 보장되므로 형변환이 필요 없습니다.
String fruit = fruits.get(0);
System.out.println(fruit.toUpperCase());
}
}
flowchart LR
A["List"] --> B["String도 저장 가능"]
A --> C["Integer도 저장 가능"]
C --> D["꺼낼 때 직접 캐스팅"]
D --> E["런타임 오류 가능"]
F["List<String>"] --> G["String만 저장 가능"]
G --> H["형변환 불필요"]
H --> I["컴파일 단계에서 오류 차단"]