공변성반공변성불공변성
2025년 10월 12일
서론 #
Java와 C# 등에서 사용되는 문법이고, 처음 들으면 아주 어지럽다.
왜 사용하는가
라이브러리 개발자 입장에서 생각해야함
class Animal
class Cat extends Animal
class Kitty extends Cat
class Tom extends Tom
제네릭은 기본적으로 불공변성임
Cat 변수에 Kitty를 담을 수 있지만, List<Cat>에는 List<Kitty>를 담을 수 없다.
공변성, 반공변성은 제약조건을 걸고 이걸 가능하게 해주는거임
공변성 #
공변성 List<? extends Cat> 은 Cat을 상속받는 모든 List<T> 클래스를 넣을 수 있다.
대신 Cat을 상속받는 모든 클래스이기 때문에 List<Cat>, List<Kitty>, List<Tom> 이 가능하게 되고, 함수를 실제로 구현하는 입장에서는 어떤 타입이 올지 명확히 알 수 없다.
List<Cat> 이라면 Cat, Kitty, Tom을 리스트에 집어넣을 수 있게 되는데 List<Kitty>라면 Cat도 넣을 수 없고, Tom은 당연히 넣을 수 없게된다.
함수를 작성할 때 파라미터로 List<? extends Cat> 를 받았다고 가정해보자. 특정 타입 예를들어 List<Cat> 을 생각하고 add(new Cat()), add(new Kitty()) 와 같은 코드를 작성해버리면 List
모든 경우에서 어떤 타입을 가정하더라도 쓰기(add)를 하는 경우 다른 타입에서 무조건 런타임 에러가 발생하기 때문에 컴파일 타임에 미리 불가능하도록 에러가 발생한다.
읽기는 그럼 어떨까?
get으로 하나 얻어왔을때 Cat인 것은 확실하기 때문에 Cat이나 Animal, Object는 가능할 것이다. 하지만 Kitty인지, Tom인지는 확신할 수 없다.
null은 어떤 클래스든 가질 수 있는 값이기 때문에
아마 add(null) 은 가능하지 않을까?
반공변성 #
얘도 마찬가지이다.
List<? super Cat> 으로 List<Cat>, List<Animal>, List<Object> 를 받아올 수 있다.
함수를 호출할 때 List<Cat> 이 들어오면 쓰기를 할 때 add(new Cat()), add(new Kitty()), add(new Tom())은 가능하더라도, add(new Animal()) 이 불가능할 것이다.
List<Animal>이 들어온 경우에도 이미 add(new Cat()), add(new Kitty()), add(new Tom()) 는 모두 가능한 코드가 된다.
그래서 Cat 하위의 값을 넣을 순 있지만, 그 이상은 절대 넣을 수 없다.
읽기는 어떨까?
List<Cat> 이 오면 get 했을때 Cat이 가능하지만, List<Animal>이 들어오면 Cat으로 읽어올 수 없고, List<Object>가 들어오면 Animal로도 읽어올 수 없게될 것이다.
그래서 어떤 값이든 읽을 수 없다.
만약 모든 클래스가 상속받는 클래스가 있다면 그 클래스만 읽기가 가능하게 열어두지 않았을까?
Object o = list.get(0)
결론 #
공변성과 반공변성은 함수를 구현하는 사람이 실제 함수를 호출하는 사람과 협업할 때,
사용하는 사람에게 타입의 자유도를 주는 만큼 함수 내부에서는 에러가 절대 발생하지 않는 것을 보장할 수 있는만큼 제약을 거는 것이다.
List<? extends Cat>이 없다면 파라미터를 List<Cat> 으로 지정하고 사용하는 사람은 List<Kitty> 를 바로 사용할 수 없고 굳이 List<Cat>에 새로 담아서 호출해야할 것이다.
내부에서 add(new Cat()) 이 가능해지겠지만, 사용하는사람이 다시 결과를 List<Kitty>에 담을 때 함수 내부에 담긴 Cat 녀석 때문에 번거로움이 생긴다. (이런걸 원하는 함수도 있긴 할것이다)
List<? super Cat>이 없다면 외부에서 굳이 List<Cat> 으로 다운캐스팅을 해야하거나 인자를 List<Object> 같은걸로 해야할텐데, 이건 더 말이 안된다.
함수의 파라미터를 List<Object>로 하면 List<Dog>나 List<곤충>도 가능해지고, 만약 addCat 이라는 함수라면 이거 하나 호출하자고 List<Animal>을 모두 List<Cat>에 담을수도 없는 노릇이다.