Genric Invariant & Covariant & Contravariant

Generic๊ณผ ํ•˜์œ„ ํƒ€์ž… (Grape๊ฐ€ Fruit์˜ ์ž์‹์ด๋ผ๊ณ  ํ•ด์„œ, List<Grape>๊ฐ€ List<Fruit>์˜ ์ž์‹์ธ ๊ฑด ์•„๋‹ˆ๋‹ค)

Grape๊ฐ€ Fruit์˜ ์ž์‹์ด๋ผ๊ณ  ํ•  ๋•Œ,

    List<Grape> grapes = new ArrayList<>();
    fruits.add(new Grape()); 

    List<Fruit> fruits = grapes; // compile error

์œ„ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด fruits ๋ฆฌ์ŠคํŠธ(ArrayList<Fruit>)๋ฅผ List<Grape> grapes = fruits; ํ•˜๋ฉด ์ปดํŒŒ์ผ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ๊ทธ ์ด์œ ๋Š” ํƒ€์ž… ์•ˆ์ •์„ฑ์ด ๊นจ์ง€๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, Fruit๊ฐ€ Grape์˜ ์ƒ์œ„ ํƒ€์ž…์ด๋ผ๊ณ  ํ•ด์„œ, ๋ฐ˜๋“œ์‹œ GenericType๊ฐ€ GenericType์˜ ์ƒ์œ„ ํƒ€์ž…์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์—†๋Š” ๊ฒƒ์ด๋‹ค. ์ด๋ฅผ **๋ฌด๋ณ€์„ฑ** ํ˜น์€ **๋ฌด๊ณต๋ณ€**์ด๋ผ๊ณ  ํ•œ๋‹ค. ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ์ฐธ๊ณ ํ•˜์ž.

    List<Grape> grapes = new ArrayList<Fruit>(); // ์ด๊ฒŒ ๋œ๋‹ค๋ฉด
    grapes.add(new Fruit());
    List<Apple> apples = grapes.getAll(); // List<Grape> ๋ฆฌํ„ด <-- List<Apple>์— List<Grape>๊ฐ€ ๋‹ด๊ธฐ๋Š” ๊ฑด ์ข€ ์ด์ƒํ•˜์ง€ ์•Š์€๊ฐ€?

Covariant ์ƒํ•œ ๊ฒฝ๊ณ„ ์™€์ผ๋“œ์นด๋“œ, Upper Bounded Wildcards)

๊ทธ๋Ÿฌ๋ฉด List<Fruit> fruits = new ArrayList<Grape>();๊ฐ€ ๊ฐ€๋Šฅํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผํ• ๊นŒ? ๋‹ต์€ extends์— ์žˆ๋‹ค. ์•ž์„œ ๋งํ–ˆ๋‹ค์‹œํ”ผ List์™€ List๋Š” ์ƒ์† ๊ด€๊ณ„๊ฐ€ ์•„๋‹ˆ๋ผ๊ณ  ํ–ˆ๋‹ค. ํ•˜์ง€๋งŒ `List<? extends Fruit> grapes`๋ฅผ ํ•ด์ฃผ๋ฉด List์™€ ์ƒ์† ๊ด€๊ณ„๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

    List<? extends Fruit> grapes = new ArrayList<>();
    List<Fruit> fruits = grapes; // ok

    Fruit tempFruit = fruits.get(0); // ok
    Apple apple = fruits.get(1); // compile error
    fruits.add(null); // ok
    fruits.add(new Fruit); // compile error
  • extends ํ‚ค์›Œ๋“œ๋ฅผ ์ด์šฉํ•ด์„œ Fruit๊ฐ€ Grape์˜ ์ƒ์œ„ํƒ€์ž…์ด๊ณ , List๊ฐ€ List์˜ ์ƒ์œ„ ํƒ€์ž…์ผ ๋•Œ extends ํ‚ค์›Œ๋“œ๋ฅผ ์ด์šฉํ•ด์„œ ํ• ๋‹น์ด ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฅผ ๊ณต๋ณ€(Covariant)๋ผ๊ณ  ํ•œ๋‹ค.
  • ์ƒํ•œ ๊ฒฝ๊ณ„ ์™€์ผ๋“œ์นด๋“œ์˜ ์›์†Œ๋Š” (? extends T) T ํ˜น์€ T์˜ ํ•˜์œ„ ํด๋ž˜์Šค์ด๋‹ค.
  • ์›์†Œ๋“ค์˜ ์ตœ๊ณ  ๊ณตํ†ต ์กฐ์ƒ์ธ T๋กœ ์ฝ์œผ๋ฉด. ์–ด๋–ค ํƒ€์ž…์ด ์˜ค๋“  T๋กœ ์ฝ์„ ์ˆ˜ ์žˆ๋‹ค. ๋‹ค์‹œ ๋งํ•˜๋ฉด, list.get(0)์„ ํ•  ๋•Œ, ๋ฐ˜ํ™˜ ํƒ€์ž…์€ Fruit์ด๋ฉฐ ๊ทธ๋ณด๋‹ค ํ•˜์œ„ ํƒ€์ž…์ธ Apple์ด๋‚˜ Banana๋กœ ์ฝ์„ ์‹œ ์ปดํŒŒ์ผ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ๊ทธ ์ด์œ ๋Š” ์—ญ์‹œ ํƒ€์ž… ์•ˆ์ •์„ฑ ๋•Œ๋ฌธ์ด๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด Applie apple = fruits.get(1);๊ฐ€ compile error๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ด์œ ๋Š” ๋ฆฌ์ŠคํŠธ์— Fruit ํ˜น์€ Banana๊ฐ€ ๋“ค์–ด์˜ค๋ฉด Apple๋กœ ํƒ€์ž…์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
  • List<? extends Fruit>์—๋Š” ํƒ€์ž… ์•ˆ์ •์„ฑ์„ ์ด์œ ๋กœ null๋งŒ ์‚ฝ์ž…ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” ์ƒํ•œ ๊ฒฝ๊ณ„ ์™€์ผ๋“œ์นด๋“œ(? extends Fruit)์˜ element๊ฐ€ ์–ด๋–ค ํƒ€์ž…์ธ์ง€ ์•Œ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. Fruit ํ˜น์€ Fruit์˜ ํ•˜์œ„ ํด๋ž˜์Šค๋ผ๋ฉด ์‚ฝ์ž…ํ•  ์ˆ˜ ์žˆ์ง€ ์•Š์„๊นŒ? ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ๋ณด์ž
          List<Apple> apples = new ArrayList<>();
          List<? extends Fruit> fruits = apples; // ok
          fruits.add(new Banana()); // compile error <-- List<Apple>์— Banana๊ฐ€ ๋“ค์–ด๊ฐ€ ๋ฒ„๋ ธ๋‹ค!!
    

Contravariant (ํ•˜ํ•œ ๊ฒฝ๊ณ„ ์™€์ผ๋“œ์นด๋“œ, Lower Bounded Wildcards)

    List<Grape> grapes = new ArrayList<>();
    List<? super Grape> fruits = grapes; // Grape๋ณด๋‹ค ์ƒ์œ„ ํƒ€์ž… ํ• ๋‹น ๊ฐ€๋Šฅ

    Grape grape = fruits.get(0); // compile error
    Object object = fruits.get(0); // ok

    fruits.add(new Grape()); // ok
    fruits.add(new Fruit()); // compile error
  • Fruit๊ฐ€ Grape์˜ ์ƒ์œ„ ํƒ€์ž…์ด๊ณ  List๊ฐ€ List์˜ **ํ•˜์œ„ ํƒ€์ž…**์ด๋ฉด ๋ฐ˜๊ณต๋ณ€(super๋ฅผ ์ด์šฉํ•ด์„œ ๋ฐ˜๊ณต๋ณ€ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ)
  • T ํ•˜ํ•œ ๊ฒฝ๊ณ„ ์™€์ผ๋“œ์นด๋“œ์˜ element๋Š” T์˜ ์ƒ์œ„ ํด๋ž˜์Šค ์ค‘ ์–ด๋–ค ํƒ€์ž…๋„ ๋  ์ˆ˜ ์žˆ๋‹ค. ์–ด๋–ค ํƒ€์ž…์ด ์™€๋„ ์ฝ์„ ์ˆ˜ ์žˆ๋„๋ก T๋“ค์˜ ๊ณตํ†ต ์กฐ์ƒ์ธ Object๋กœ ๋ฐ›๋Š”๋‹ค. Object๋ณด๋‹ค ํ•˜์œ„ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋กœ get์„ ํ•˜๋ฉด Compile error๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ๊ทธ ์ด์œ ๋Š” ํ•˜ํ•œ ์ œํ•œ(super)๋Š” ์•„๋ž˜๋งŒ์„ ์ œํ•œํ•˜๋ฏ€๋กœ T๋ณด๋‹ค ์ƒ์œ„ ํด๋ž˜์Šค์˜ List๋Š” ์–ผ๋งˆ๋“ ์ง€ ์˜ฌ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ T๋“ค์˜ ๊ณตํ†ต ์กฐ์ƒ์ธ Object๋กœ๋งŒ get์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์œ„ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด List<? super Grape> fruits์˜ ์ƒ์œ„ ํƒ€์ž…์€ super ํ‚ค์›Œ๋“œ๋กœ ์ธํ•ด List์ด๊ฑฐ๋‚˜ ๊ทธ๋ณด๋‹ค ๋” ์ƒ์œ„ ํƒ€์ž…์ด ๋œ๋‹ค. ์ตœ์†Œ ํƒ€์ž…์ด Grape์ด๋ฏ€๋กœ new Grape() element์˜ ์ถ”๊ฐ€๋Š” ๋ณด์žฅํ•˜์ง€๋งŒ ๊ทธ๋ณด๋‹ค ๋” ์ƒ์œ„ ํƒ€์ž…์ผ ๊ฒฝ์šฐ ํ•ด๋‹น element๊ฐ€ Fruit์ผ์ง€, Object์ผ์ง€ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ํƒ€์ž…์„ ํ™•์ •ํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ element ์ถ”๊ฐ€๋Š” Grape๋‚˜ Grape๋ณด๋‹ค ํ•˜์œ„ ํƒ€์ž…์œผ๋กœ ์ œํ•œ๋œ๋‹ค. ์—ฌ๊ธฐ์„œ ๋“œ๋Š” ์˜๋ฌธ์ ์€ Grape๋งŒ ์‚ฝ์ž…ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•˜๋ฉด ๊ทธ๋ ค๋ ค๋‹ˆ ํ•˜๊ฒ ๋Š”๋ฐ Grape๋ณด๋‹ค ๋” ํ•˜์œ„ ํƒ€์ž…๋„ ์‚ฝ์ž…ํ•  ์ˆ˜ ์žˆ๋‹ค๋‹ˆ? `fruits.add(new Grape());` ๊นŒ์ง€๋Š” ์•Œ๊ฒ ๋Š”๋ฐ `fruits.add(new ShineMuscat());`๋„ ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ์†Œ๋ฆฌ๋‹ค. fruits์—๋Š” Grape ํ˜น์€ Grape๋ณด๋‹ค ์ƒ์œ„ ํด๋ž˜์Šค๋งŒ ์‚ฝ์ž…ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•˜๋Š” ๊ฑฐ ์•„๋‹๊นŒ? ์–ด์งธ์ˆ˜ Grape๋‚˜ Grape๋ณด๋‹ค ํ•˜์œ„ ํด๋ž˜์Šค๋งŒ ์˜ฌ ์ˆ˜ ์žˆ๋Š” ๊ฑธ๊นŒ? -> ์ปดํŒŒ์ผ๋Ÿฌ๋Š” fruits๊ฐ€ List์ธ์ง€ List์ธ์ง€ List์ธ์ง€ ์•Œ์ง€ ๋ชปํ•œ๋‹ค. ๋งŒ์•ฝ fruits๊ฐ€ List์ผ ๊ฒฝ์šฐ, Fruit๋Š” Grape์˜ ์ƒ์œ„ ํด๋ž˜์Šค์ด๋ฏ€๋กœ element๋ฅผ ์‚ฝ์ž…ํ•  ์ˆ˜ ์—†๋‹ค. ์ปดํŒŒ์ผ๋Ÿฌ๋Š” fruits์ด ์–ด๋–ค ํƒ€์ž…์˜ ๋ฆฌ์ŠคํŠธ์ธ์ง€ ์•Œ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์ถ”๊ฐ€ํ•˜๋ ค๋Š” element๊ฐ€ Grape์ด๊ฑฐ๋‚˜ Grape๋ณด๋‹ค ํ•˜์œ„ ํด๋ž˜์Šค์ด๋ฉด ํƒ€์ž… ์•ˆ์ •์„ฑ์ด ๋ณด์žฅ๋˜๊ธฐ ๋•Œ๋ฌธ์— List<? super T>์—์„œ๋Š” T์ด๊ฑฐ๋‚˜ T๋ณด๋‹ค ํ•˜์œ„ ํด๋ž˜์Šค๋งŒ ์‚ฝ์ž…ํ•  ์ˆ˜ ์žˆ๋‹ค.

(+์ถ”๊ฐ€) List<?>์—๋Š” null๋งŒ ์‚ฝ์ž…ํ•  ์ˆ˜ ์žˆ๋‹ค.

Covariant๋‚˜ Contravariant์ฒ˜๋Ÿผ ์™€์ผ๋“œ์นด๋“œ(?)์— ๊ฒฝ๊ณ„(extends OR super)๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ List<?>์˜ element๊ฐ€ ์–ด๋–ค ํƒ€์ž…์ธ์ง€ ์•Œ ์ˆ˜ ์—†๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ ํƒ€์ž… ์•ˆ์ •์„ฑ์„ ์ง€ํ‚ค๊ธฐ ์œ„ํ•ด null๋งŒ ์‚ฝ์ž…ํ•  ์ˆ˜ ์žˆ๋‹ค.

    public static void main(String[] args) {
        List<Integer> ints = new ArrayList<>();
        addDouble(ints);
    }

    public static void addDouble(List<?> ints) {
        ints.add(3.14);
    }

์œ„ ์ฝ”๋“œ๋ฅผ ๋ณด์ž. ์œ„ ์ฝ”๋“œ์—์„œ ์ƒํ•œ์ด๋‚˜ ํ•˜ํ•œ ๊ฒฝ๊ณ„๊ฐ€ ์—†๋Š” List<?> ints = List<Integer>๋ฅผ ํ• ๋‹นํ•˜๊ณ  ints.add(3.14);๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์ปดํŒŒ์ผ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ํ•ด๋‹น ๋ช…๋ น์ด ์ปดํŒŒ์ผ ์˜ค๋ฅ˜๊ฐ€ ๋‚˜๋Š” ๊ฑด ๋‹น์—ฐํ•˜๋‹ค. ๊ทธ ์ด์œ ๋Š” ๋ถ„๋ช…ํžˆ List๋ฅผ ๋„˜๊ฒผ๋Š”๋ฐ Double ํƒ€์ž…์„ ์‚ฝ์ž…ํ•˜๋ฉด ์ด์ƒํ•˜์ง€ ์•Š์€๊ฐ€? ๊ทธ๋ž˜์„œ ํ•˜ํ•œ์ด๋‚˜ ์ƒํ•œ ๊ฒฝ๊ณ„๊ฐ€ ์—†๋Š” List<?>๋Š” ํƒ€์ž… ์•ˆ์ •์„ฑ์„ ์ง€ํ‚ค๊ธฐ ์œ„ํ•ด ํ• ๋‹น์€ ์–ด๋–ค ๋ฆฌ์ŠคํŠธ๋„ ๋ฐ›์„ ์ˆ˜ ์žˆ์ง€๋งŒ, ์‚ฝ์ž…์€ null๋งŒ ๊ฐ€๋Šฅํ•˜๋‹ค. ๋‚œ์ด๋„๊ฐ€ ์žˆ๋Š” ๊ฐœ๋…์ด๋‹ˆ ํ™•์‹คํžˆ ์ดํ•ดํ•˜๋„๋ก ๋…ธ๋ ฅํ•˜์ž.

๋Œ“๊ธ€๋‚จ๊ธฐ๊ธฐ