# 제네릭의 공변, 반공변, 무공변

# 무공변

  • 자바에서 제네릭은 기본적으로 무공변(Invariant)임
  • 무공변 : 타입 S, T가 있을 때 서로 관계가 없다는 것을 의미
public class Animal {
}

public class Cat extends Animal {
}

List<Animal> animals = new ArrayList<Cat>(); // 컴파일 에러
List<Cat> cats = new ArrayList<Animal>(); // 컴파일 에러
  • 위와 같이 AnimalCat이 서로 상속 관계이기 때문에 공변성이 있지만, 제네릭은 상속 관계가 호환되지 않음(무공변)
  • 즉, 타입이 정확하게 일치하지 않으면 컴파일 에러 발생
  • 무공변은 타입 안정성을 보장하지만, 유연성이 부족하다는 단점이 있음
  • 자바에서는 와일드카드(?)와 extends, super 키워드로 공변과 반공변을 지원함

# 공변

  • S가 T의 하위 타입일 때 S는 T가 될 수 있다는 것을 의미
public class Main {
    public static void main(String[] args) {
        List<? extends Animal> animals = new ArrayList<Dog>();
        animals.add(null); // 가능
        animals.add(new Dog()); // 불가능
    }

    public static class Animal {

    }

    public static class Dog extends Animal {
    }
}
  • 제네릭에서는 <? extends T>를 사용하여 하위 타입을 허용하고 읽기 전용으로 사용 가능
  • 쓰기는 null만 가능 → 런타임 시점에 어떤 타입이 올지 모르기 때문에 어떤 구체적인 객체도 추가를 허용하지 않음

# 반공변

  • S가 T의 하위 타입일 때 T는 S가 될 수 있다는 것을 의미
public class Main {
    public static void main(String[] args) {
        List<? super Dog> animals = new ArrayList<Dog>();
        animals.add(new Dog());
        Object object = animals.get(0);
    }

    public static class Animal {

    }

    public static class Dog extends Animal {
    }
}
  • 제네릭에서는 <? super S>를 사용하여 상위 타입을 허용하고 쓰기 전용으로 사용 가능
  • 읽기는 Object 타입으로만 가능 → Dog, Animal, Object 중 어떤 타입일지 알 수 없기 때문에 최상위 타입으로만 반환

# PECS란?

  • PECS(Producer Extends, Consumer Super)는 제네릭에서 와일드카드의 상위 또는 하위 경계를 설정할 때 사용되는 가이드라인
  • 객체를 생산할 때는 <? extends T>를 사용하고, 소비할 때는 <? super T>를 사용
public void produce(List<? extends Animal> animals) { // 생산자 역할
    for (Animal animal : animals) {
        System.out.println("animal = " + animal);
    }
}

public void consume(List<? super Dog> dogs) { // 소비자 역할
    dogs.add(new Dog());
}

# <?>와 <Object>의 차이점

# <?>

  • 모든 타입을 메서드 인자로 받을 수 있지만 null 외에는 값을 추가할 수 없기 때문에 읽기 전용으로 사용됨

# <Object>

  • <Object> 외의 타입을 메서드 인자로 받을 수 없지만 모든 객체를 추가할 수 있기 때문에 읽기, 쓰기 모두 사용 가능함