2022-03-20 TIL
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
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<? 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<?>์๋ 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
๋๊ธ๋จ๊ธฐ๊ธฐ