[Java] 제네릭 (공변, 반공변, 불공변)

한상희·2024년 3월 11일

자바

목록 보기
1/9
post-thumbnail

제네릭에 대해 배우다보니 공변, 반공변, 불공변에 대한 것에 알게 되었다. 제네릭에서 각각의 특징을 설명할때 나오는 단어였다. 더 자세히 알아볼려고 글을 쓰게 되었다.


공변 (Covariance)

공변이란 하위 타입의 객체를 상위 타입의 참조로 참조할 수 있는 성질을 말합니다. 그리고 대표적으로 자바에서는 배열이 공변(Convariance)적입니다. StringObject의 하위 타입(서브 타입)이므로, String 배열을 Object 배열로 참조할 수 있습니다.

String[] strings = new String[3];
Object[] objects = strings; // 가능: 배열은 공변적

또는 Animal과 Cat으로도 예를 들수 있습니다.

class Animal {
    ...
}

class Cat extends Animal {
    ...
}
Animal animal = new Cat();  

불공변 (Invariance)

불공변이란 뭘까요? 불공변이란 타입이 자기 자신으로만 할당 가능하며, 상위 타입으로는 할당할 수 없는 성질을 말합니다. 제네릭은 기본적으로 불공변입니다. 예를 들어 List<String>, List<Object>의 하위 타입(서브 타입)이 아닙니다. 하지만 우리는 와일드카드를 사용하여 공변성을 표현할 수 있습니다.

List<? extends Object> objList = new ArrayList<String>(); // 와일드카드를 사용한 공변

그리고 아래와 같은 예제가 불공변입니다.

List<Object> objList;
List<String> strList;

objList = strList; // 에러: 타입 불일치

List<Object>List<String>의 상위 타입이 아니기 때문에, List<String>List<Object>에 할당 할 수 없습니다.

반공변 (Contravariance)

그럼 반공변은 뭘까요? 반공변은 특정 타입의 하위 타입 대신 상위 타입을 허용하는 성질을 말합니다. 주로 제네릭에서 와일드카드를 이용한 <? super T>를 이용하는 것이다. 일단 <? super T>에 대해 알아봅시다.

제네릭에서 super, extends가 있습니다. extends을 쓰면 뒤에 있는 클래스나 그 클래스를 상속받은 것만 허용합니다. super는 뒤에 있는 클래스나 그 클래스의 상위 객체(부모 객체)를 받겠다는 뜻입니다. 그럼 다음 예를 봅시다.

class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}

class AnimalProcessor {
    public void process(List<? super Dog> animals) {
        // 여기서 animals 리스트에는 Dog 또는 Dog의 상위 타입(Animal 포함) 객체들이 올 수 있습니다.
    }
}

process 메서드는 List<? super Dog> 타입의 매개변수를 받습니다. 이는 List<Dog>, List<Animal>, 또는 Dog의 다른 상위 타입의 리스트를 매개변수로 받을 수 있음을 의미합니다. 이러한 성질을 반공변성이라고 합니다.

이제 실제 사용 예시를 보겠습니다.

AnimalProcessor processor = new AnimalProcessor();

List<Animal> animalList = new ArrayList<>();
animalList.add(new Animal());
processor.process(animalList); // 가능: Animal은 Dog의 상위 타입

List<Dog> dogList = new ArrayList<>();
dogList.add(new Dog());
processor.process(dogList); // 가능: 정확히 Dog 타입

// 다음은 불가능한 예시입니다.
// List<Cat> catList = new ArrayList<>();
// catList.add(new Cat());
// processor.process(catList); // 컴파일 에러: Cat은 Dog의 상위 타입이 아님

이런씩으로 반공변에 대해 알아보았습니다.

요약

공변은 하위 타입 관계를 유지하면서 타입이 변할 수 있음을 허용하는 것, 반공변은 하위 타입 관계가 반대로 되는 타입 변환을 허용하는 것, 불공변은 정확한 타입 일치를 요구하는 것입니다.

그리고 우리가 말한 내용이 PECS(펙스) 공식입니다.

PECS: producer-extends, consumer-super


참고
Java의 공변(Covariant)과 불공변(Invariant)
[Java] 자바 제네릭 불공변 / 공변 / 반공변 처음 들어봅니다.
가변성(Variance) 알아보기 - 공변, 무공변, 반공변
[Java] 왜 배열은 Covariant(공변)이고 제네릭은 Invariant(불공변)일까?

profile
안녕하세요

0개의 댓글