πŸ“‚ JAVA/μ£Όμš” κ°œλ…

μ œλ„€λ¦­(Generics) - 곡변(Covariant), λΆˆκ³΅λ³€(Invariant), λ°˜κ³΅λ³€(Contravariant)

Amenable 2023. 5. 29. 12:09

1. 곡변, λΆˆκ³΅λ³€, λ°˜κ³΅λ³€μ˜ κ°œλ… πŸŒ‚

  λ³€μ„±(Variance)μ΄λž€ νƒ€μž…μ˜ 계측 κ΄€κ³„μ—μ„œ μ„œλ‘œ λ‹€λ₯Έ νƒ€μž… 간에 μ–΄λ– ν•œ 관계가 μžˆλŠ”μ§€λ₯Ό λ‚˜νƒ€λ‚΄λŠ” 것이닀.

  λ³€μ„±μ˜ 3κ°€μ§€ μ’…λ₯˜μΈ 곡변(Covariant), λΆˆκ³΅λ³€(Invariant), λ°˜κ³΅λ³€(Contravariant)은 λ‹€μŒκ³Ό 같은 νŠΉμ„±μ„ κ°€μ§„λ‹€.

곡변 Sκ°€ T의 ν•˜μœ„ νƒ€μž…이면, S[]λŠ” T[]의 ν•˜μœ„ νƒ€μž…이닀.
λΆˆκ³΅λ³€ List<S>와 List<T>λŠ” 관계가 μ—†λ‹€.
λ°˜κ³΅λ³€ Sκ°€ T의 ν•˜μœ„ νƒ€μž…이면, T[]λŠ” S[]의 ν•˜μœ„ νƒ€μž…이닀.

 

2. μ œλ„€λ¦­μ€ 곡변성이 μ—†λ‹€. β˜”

  μžλ°”μ—μ„œ 배열은 곡변이고, μ œλ„€λ¦­μ€ λΆˆκ³΅λ³€μ΄λ‹€.

  즉, 객체 νƒ€μž…μ˜ κ΄€κ³„μ—μ„œ Aκ°€ B의 ν•˜μœ„ νƒ€μž…μΌ λ•Œ, λ°°μ—΄ A[]λŠ” B[]의 ν•˜μœ„ νƒ€μž…μ΄μ§€λ§Œ List<A>sms List<B>의 ν•˜μœ„ νƒ€μž…μ΄ μ•„λ‹ˆλ‹€.

public static void main(String[] args) {
    // 배열은 곡변
    Object[] covariance = new Integer[10];

    // μ œλ„€λ¦­μ€ λΆˆκ³΅λ³€
    List<Object> invariance = new List<Integer>;
}

 

3. λΆˆκ³΅λ³€μ˜ 문제점 🧺

  λ°°μ—΄κ³Ό 리슀트의 값을 print λ©”μ„œλ“œλ₯Ό μ΄μš©ν•΄μ„œ 좜λ ₯ν•œλ‹€κ³  ν•΄λ³΄μž. λ°°μ—΄μ˜ κ²½μš° μ•„λž˜μ™€ κ°™μ΄ λ‹€ν˜•μ„±μ˜ νŠΉμ§•μ„ μ΄μš©ν•˜μ—¬ printλ©”μ„œλ“œλ₯Ό λ§Œλ“€ μˆ˜ μžˆλ‹€.

public static void print(Object[] arr) {
    for(Object i : arr) {
        System.out.println(i);
    }
}

public static void main(String[] args) {
    String[] strings = {"1", "2", "3"};
    print(strings);

    Integer[] integers = {1, 2, 3};
    print(integers);
}

  ν•˜μ§€λ§Œ, μ œλ„€λ¦­μ˜ 경우 λΆˆκ³΅λ³€μ΄κΈ° λ•Œλ¬Έμ— λ°°μ—΄κ³Ό 같은 ν˜•μ‹μœΌλ‘œ μ½”λ“œλ₯Ό μž‘μ„±ν•˜λ©΄ 컴파일 μ—λŸ¬κ°€ λ‚œλ‹€.

public static void print(List<Object> arr) {
    for(Object i : arr) {
        System.out.println(i);
    }
}

public static void main(String[] args) {
    List<String> strings = List.of("1", "2", "3");
    print(strings); // 컴파일 μ—λŸ¬

    List<Integer> integers = List.of(1, 2, 3);
    print(integers); // 컴파일 μ—λŸ¬
}

  κ·Έλž˜μ„œ λ‹€μŒκ³Ό 같이 객체 μ§€ν–₯의 νŠΉμ§•μ„ μ „ν˜€ μ΄μš©ν•˜μ§€ λͺ»ν•œ μƒνƒœλ‘œ μ½”λ“œλ₯Ό μž‘μ„±ν•΄μ•Ό ν•œλ‹€.

public static void printString(List<String> arr) {
    for(Object i : arr) {
        System.out.println(i);
    }
}

public static void printInteger(List<Integer> arr) {
    for(Object i : arr) {
        System.out.println(i);
    }
}

public static void main(String[] args) {
    List<String> strings = List.of("1", "2", "3");
    printString(strings);

    List<Integer> integers = List.of(1, 2, 3);
    printInteger(integers);
}

 

4. ν•œμ •μ  μ™€μΌλ“œμΉ΄λ“œ 🚿

  μœ„μ—μ„œ μ‚΄νŽ΄λ³Έ λΆˆκ³΅λ³€μ˜ 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄μ„œ μ œλ„€λ¦­μ€ <? extends T>, <? super T>λΌλŠ” ν•œμ •μ  μ™€μΌλ“œμΉ΄λ“œλ₯Ό μ œκ³΅ν•œλ‹€.

μƒν•œ κ²½κ³„ μ™€μΌλ“œ μΉ΄λ“œ <? extends T> 곡변성 μ μš©
ν•˜ν•œ κ²½κ³„ μ™€μΌλ“œ μΉ΄λ“œ <? super T> λ°˜κ³΅λ³€μ„± μ μš©

πŸ“˜ 1. 곡변 - μƒν•œ 경계 μ™€μΌλ“œμΉ΄λ“œ

public class MyArrayList<T> {

    Object[] element = new Object[3];
    int index = 0;

    // λ‚΄λΆ€ 배열에 in을 μΆ”κ°€
    public MyArrayList(Collection<? extends T> in) {
        for(T elem : in) {
            element[index++] = elem;
        }
    }

    @Override
    public String toString() {
        return Arrays.toString(element);
    }
}

----------

public static void main(String[] args) {
    MyArrayList<String> names = new MyArrayList<>(List.of("amenable", "choi", "perseverance"));
    System.out.println(names); // [amenable, choi, perseverance]
}

πŸ“˜ 2. λ°˜κ³΅λ³€ - ν•˜ν•œ 경계 μ™€μΌλ“œμΉ΄λ“œ

public class MyArrayList<T> {

    Object[] element = new Object[3];
    int index = 0;

    ...

    // out에 λ‚΄λΆ€ 배열을 μΆ”κ°€
    public void clone(Collection<? super T> out) {
        for(Object elem : element) {
            out.add((T) elem);
        }
    }



}
public static void main(String[] args) {
    MyArrayList<String> names = new MyArrayList<>(List.of("amenable", "choi", "perseverance"));

    List<Object> temp = new ArrayList<>();
    names.clone(temp);

    System.out.println(temp); // [amenable, choi, perseverance]
}

 

5. PECS (Producer-Extends, Consumer-Super) πŸ›

  μƒν•œ 경계 μ™€μΌλ“œμΉ΄λ“œ(<? extends T>)와 ν•˜ν•œ 경계 μ™€μΌλ“œμΉ΄λ“œ(<? super T>)λŠ” μ–Έμ œ μ–΄λ–€ κ±Έ μ‚¬μš©ν•΄μ•Ό ν• κΉŒ?

  μƒμ‚°μž(Producer)λŠ” extendsλ₯Ό μ‚¬μš©ν•˜κ³ , μ†ŒλΉ„μž(Consumer)λŠ” superλ₯Ό μ‚¬μš©ν•˜λ©΄ λœλ‹€. 이것이 PECS(Producer-Extends, Consumer-Super) 곡식이닀.

 

  μœ„μ—μ„œ μƒν•œ 경계 μ™€μΌλ“œμΉ΄λ“œμ™€ ν•˜ν•œ 경계 μ™€μΌλ“œμΉ΄λ“œμ˜ μ˜ˆμ œμ—μ„œ 이미 PECSλ₯Ό μ μš©ν•˜μ˜€λ‹€. μ½”λ“œλ₯Ό λ‹€μ‹œ ν•œλ²ˆ 확인해 λ³΄λ©΄μ„œ PECSλ₯Ό 이해해 보도둝 ν•˜μž.

public class MyArrayList<T> {

    Object[] element = new Object[3];
    int index = 0;

    // μƒμ‚°μž(Producer) - extends
    // λ‚΄λΆ€ 배열에 in을 μΆ”κ°€
    public MyArrayList(Collection<? extends T> in) {
        for(T elem : in) {
            element[index++] = elem;
        }
    }

    // μ†ŒλΉ„μž(Consumer) - super
    // out에 λ‚΄λΆ€ 배열을 μΆ”κ°€
    public void clone(Collection<? super T> out) {
        for(Object elem : element) {
            out.add((T) elem);
        }
    }

    @Override
    public String toString() {
        return Arrays.toString(element);
    }
}

----------

public static void main(String[] args) {
    MyArrayList<String> names = new MyArrayList<>(List.of("amenable", "choi", "perseverance"));

    List<Object> temp = new ArrayList<>();
    names.clone(temp);
}

 

ν•΄λ‹Ή 글은
Hyo Kim λ‹˜μ˜ '[Java] μžλ°” μ œλ„€λ¦­ λΆˆκ³΅λ³€ / 곡변 / λ°˜κ³΅λ³€ 처음 λ“€μ–΄λ΄…λ‹ˆλ‹€.',
BeforB λ‹˜μ˜ '[Java] μ™œ 배열은 Convariant(곡변)이고 μ œλ„€λ¦­μ€ Invariant(λΆˆκ³΅λ³€)일까?',
Inpa λ‹˜μ˜ 'μžλ°” μ œλ„€λ¦­μ˜ 곡변성 & μ™€μΌλ“œ μΉ΄λ“œ μ™„λ²½ 이해'
λ₯Ό μ°Έκ³ ν•˜μ˜€μŠ΅λ‹ˆλ‹€.