공부한 내용을 정리하는 것이 아니라 내가 공부하다가 생긴 의문을 글로 써보는 것은 처음이라 어색하지만 일단 기록해본다. 자바에는 배열과 리스트가 있다. 리스트에 지원하는 API가 많기 때문에 리스트를 쓰라고 많이 권장하지만 또 다른 차이점이있다.

메소드의 파라미터

배열과 리스트는 메소드의 파라미터로 받을 때 큰 차이가 발생한다.

void function(Object[] array) {
	// Logic
}
...
SomeClass[] array = {new SomeClass()};
function(array);

위 코드의 경우 SomeClassObject의 자손이므로 문제없이 작동한다. 즉 공변성을 가진다는 뜻이다. 하지만 리스트의 경우 상황이 달라진다.

void function(List<Object> list) {
	// Logic
}
...
List<SomeClass> list = Arrays.asList(new SomeClass());
function(list);

위 코드는 인자로 SomeClass의 리스트를 받을 수 없어 컴파일을 실패한다. Java에서는 제네릭에 대해서 무공변성을 가지기 때문이다.

배열과 리스트가 차이를 가지게 된 이유

왜 이런 차이를 가지게 된 것일까? 이유는 정말로 단순하다. 배열은 제네릭이 만들어지기 전부터 있었기 때문이다. 배열을 만들 당시에는 다형성을 가지기 위해서 공변성을 가지도록 했다. 그리고 제네릭을 만들 당시에는 아래와 같은 이유때문에 무공변성을 선택했다.

// Illegal code - because otherwise life would be Bad
List<Dog> dogs = new ArrayList<Dog>(); // ArrayList implements List
List<Animal> animals = dogs; // Awooga awooga
animals.add(new Cat());
Dog dog = dogs.get(0); // This should be safe, right?

위와 같이 공변성을 가질 경우 Dog의 리스트에 Cat을 삽입할 수 있는 불상사가 발생하기 때문이다. 그럼 배열은 이 문제를 아직 가지고 있는데 왜 유지하고 있을까?

배열의 문제점

위와 같은 공변성으로부터 나오는 문제는 하나의 클래스가 여러가지의 자식 클래스를 가질 수 있기 때문에 발생한다.

Java-covariance

위 그림과 같이 공변성일 경우 Animal의 자식 클래스인 DogCat 둘 다 삽입을 할 수 있는 상황이 발생한다. 그래서 필자는 ‘반공변성을 가지면 해결이 되지않나’라는 의문을 가졌다.

Java-contravariance

반공변성을 가질 경우 List<Cat>을 파라미터로 가지는 메서드를 선언하면 위 그림과 같이 여러 개의 부모 클래스를 고려해야될 필요가 없어진다. Java는 여러 개의 부모를 상속받을 수 없기 때문이다. 그럼 도대체 왜 반공변성을 도입하지 않은 것일까?

배열에 반공변성을 가지도록 하지 않는 이유

기본적으로 우리가 실생활에 생각하는 것과 달라지기 때문에 코드를 이해하기가 쉽지 않다. List<Cat>을 파라미터로 받는 메서드면 당연히 다양한 종류의 고양이 클래스가 삽입 될 수 있다고 생각할 것인데 갑자기 Animal 객체를 추가한다는 것은 이해하기 쉽지 않다. 또한 Animal객체를 추가할 수 있는 경우, Cat의 인스턴스 메서드를 호출하고 싶은데 Animal에는 존재하지 않기 때문에 이를 처리하는 과정도 복잡해진다.

따라서, 자바에서는 배열이 공변성을 가지는 것을 유지하고있다. 또한 공변성이 가지는 문제를 해결하기 위해, 객체의 실제 타입을 내부에서 기억하고 배열에 삽입이 될 때마다 올바른 타입이 입력되었는지 확인하는 과정을 거치도록 설계되었다. 이런 타입 검사는 상당한 성능 저하가 발생하기 때문에 이를 보완하기 위해 제네릭을 도입한 List가 나왔다고 한다. 그러니 다들 배열말고 리스트를 쓰도록하자.

참고 자료

https://stackoverflow.com/questions/18666710/why-are-arrays-covariant-but-generics-are-invariant

https://stackoverflow.com/questions/18666710/why-are-arrays-covariant-but-generics-are-invariant

https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)#Covariant_arrays_in_Java_and_C.23

https://edykim.com/ko/post/what-is-coercion-and-anticommunism/

댓글남기기