의심할바 없이 제네릭은 자바에서 헷갈리는 것중 하나입니다. 그리고 매일 자바로 프로그래밍을 하지 않는다면 쉽게 잊어버릴 수 있는 개념이기도 합니다. 예를 들어 List<?> 와 List<Object>는 비슷해 보입니다. 그러나 약간 다릅니다. List<?>는 기본적으로 어떤 타입(any type)의 List 입니다. List<String>, List<Integer> 등을 할당할 수 있습니다.
기본적으로 List<?>는 밑에와 같이 할당할 때 까지는 모릅니다.
List<?> list = new List<String>();
그러면 List<Object>로 돌아와봅시다. 이는 List<String>과 별다른 차이가 없습니다. String 대신에 Object를 받아들일 뿐입니다. 모든 클래스는 Object를 상속을 받고 있습니다. 그러면 본질적으로 Object는 어떤 객체든 담을 수 있다는 얘기입니다.
그런데 자세히 읽어보시면 List<?>는 어떤 List 타입을 할당받을 수 있고, List<Object>는 어떤 Object 타입을 저장할 수 있습니다. 이것이 진짜 List<?>와 List<Object>의 차이입니다.
예시를 한번 봐야겠지요?
제가 List<?>는 어떤 타입(any type)의 List를 할당할 수 있다고 했지만 List<Object>에는 그렇게 할 수 없습니다. 이와 같이 String, Integer, Double 등 List<Object>에 넣을 수 있는 것들을 다 넣을 수 있습니다. 그러나 List<?>에는 불가능합니다. 왜냐하면 <?>은 타입이 unknown이기 때문입니다.
import java.util.ArrayList;
import java.util.List;
public class Demo {
public static void main(String[] args) {
printElements(new ArrayList<String>()); // OK
printElements(new ArrayList<Integer>()); // OK
printObjects(new ArrayList<String>());
// NOT OK compile time error
printObjects(new ArrayList<Integer>());
// NOT OK compile time error
}
public static void printElements(List<?> listOfUnknownType) {
listOfUnknownType.add("abc"); // compile time error
for (Object o : listOfUnknownType) {
System.out.println(o); // OK
}
}
public static void printObjects(List<Object> listOfObjects) {
listOfObjects.add("abc"); // OK
listOfObjects.add(101); // OK
for (Object o : listOfObjects) {
System.out.println(o); // OK
}
}
}
위 코드를 보시면 ArrayList<String>와 ArrayList<Integer>는 printElements() 함수를 통과를 하지만 printObjects() 함수는 통과하지 못하는 것을 보실 수 있습니다. 왜냐하면 String과 Integer은 Object와 다른 타입이기 때문입니다. 부모-자식 상으로는 같지만, 제네릭은 불변 타입이기 때문에 상속관계를 무시하기 때문에 다르다고 인식이 됩니다. 첫번째 함수인 printElements()를 통과하는 것이 가능했던 이유는 어떤 타입을 받을 수 있는 List<?>이었기 때문입니다.
결국에는 printObjects(List<Object> listOfObject) 는 에러를 던지게 되는거죠 왜냐하면 Object 타입만 받을 수 있기 때문입니다.
만약에 단순 읽는 작업을 하면 List<?>와 List<Object>의 기능은 같습니다. 그런데 objects 를 추가할때는 List<?>는 에러가 납니다. 왜냐하면 unknown 이기 때문입니다.
List<?>의 경우 컴파일러는 List의 타입이 유효한지 모릅니다. 그래서 타입안정성을 보장할 수 없기 때문에 에러를 던집니다. 그런데 List<Object>의 경우에는 컴파일러가 List의 타입이 Object인것을 정확히 알기 때문에 String, Integer, Double 등을 할당할 수 있습니다.
그래서 List<?>와 List<Object>의 차이는 코더가 무엇을 하느냐에 달려있습니다. 만약 객체를 읽을때 (List에서 객체를 출력할때) List<?>, List<Object> 둘다 사용해도 좋습니다. 그런데 요소를 추가할때는 List<Object>를 사용해야합니다. 만약 제네릭 함수를 작성중일 때 어떠한 타입의 list를 함수의 매개 변수로 받고싶을때는 List<?>를 사용합니다.
'언어 > 자바' 카테고리의 다른 글
[java] 클래스, 인스턴스, 지역 변수가 메모리에 올라가는 방식 (0) | 2024.10.08 |
---|---|
List<?> 와 List 의 차이 (0) | 2023.05.03 |