λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°

빈 ꡬ멍 μ±„μš°κΈ°

[Java] λ³€μ„± Variance

좜처

ChatGPT


λ³€μ„±(Variance)은 μ œλ„€λ¦­ νƒ€μž…μ΄ 상속 κ΄€κ³„μ—μ„œ μ–΄λ–»κ²Œ λ™μž‘ν•˜λŠ”μ§€, 즉 νƒ€μž… κ°„μ˜ 상속 관계가 μ œλ„€λ¦­ νƒ€μž…μ—μ„œλ„ κ·ΈλŒ€λ‘œ μœ μ§€λ  수 μžˆλŠ”μ§€μ— λŒ€ν•œ κ°œλ…μ„ μ˜λ―Έν•œλ‹€. 변성은 객체 지ν–₯ ν”„λ‘œκ·Έλž˜λ°μ—μ„œ μ œλ„€λ¦­ νƒ€μž…μ˜ μ•ˆμ „ν•œ μ‚¬μš©μ„ μœ„ν•˜ μ€‘μš”ν•˜λ‹€. 변성은 크게 곡변성(Convariance), λ°˜κ³΅λ³€μ„±(Contravariance), 그리고 무곡변성(Invariance)으둜 λ‚˜λˆŒ 수 μžˆλ‹€.

 

1. λ³€μ„±μ˜ μ’…λ₯˜

1. 곡변성 Convariance

μ •μ˜

곡변성은 μ œλ„€λ¦­ νƒ€μž…μ΄ νŠΉμ • νƒ€μž…μ˜ ν•˜μœ„ νƒ€μž… 관계λ₯Ό κ·ΈλŒ€λ‘œ μœ μ§€ν•˜λŠ” 것을 μ˜λ―Έν•œλ‹€.

 

κ·ΈλŸ¬λ‹ˆκΉŒ, A νƒ€μž…κ³Ό A νƒ€μž…λ₯Ό 상속받은 A'νƒ€μž…μ΄ 있고, B νƒ€μž…κ³Ό B νƒ€μž…μ„ 상속받은 B' νƒ€μž…μ΄ μžˆλ‹€λ©΄, A<? extends B> λ³€μˆ˜μ— A'<B'> 객체λ₯Ό 넣을 수 μžˆλ‹€λŠ” 것이닀.

μ˜ˆμ‹œ

λ§Œμ•½ Apple이 Fruit의 ν•˜μœ„ νƒ€μž…μ΄λΌλ©΄, List<Apple>도 List<Fruit>의 ν•˜μœ„ νƒ€μž…μœΌλ‘œ 간주될 수 μžˆλŠ” 상황이닀.

μ‚¬μš© 사둀

μžλ°”μ—μ„œλŠ” 곡변성을 μ§€μ›ν•˜κΈ° μœ„ν•΄ μƒν•œ 경계 μ™€μΌλ“œ μΉ΄λ“œ(? extendsd T)λ₯Ό μ‚¬μš©ν•œλ‹€. μ΄λŠ” νŠΉμ • μ œλ„€λ¦­ νƒ€μž…μ΄ νŠΉμ • μƒμœ„ 클래슀의 μ„œλΈŒνƒ€μž…(ν•˜μœ„ νƒ€μž…)λ“€λ§Œ ν—ˆμš©ν•œλ‹€λŠ” 것을 μ˜λ―Έν•œλ‹€.

List<? extends Fruit> fruits = new ArrayList<Apple>();

 

μœ„ μ½”λ“œμ—μ„œ List<? extends Fruit>λŠ” Fruit의 ν•˜μœ„ νƒ€μž…λ“€(Apple, Orange λ“±)의 리슀트둜 μ‚¬μš©ν•  수 μžˆλ‹€.

μ œμ•½

κ³΅λ³€μ„±μ—μ„œλŠ” λ¦¬μŠ€νŠΈμ— μƒˆλ‘œμš΄ μš”μ†Œλ₯Ό μΆ”κ°€ν•  수 μ—†λ‹€. μ™œλƒν•˜λ©΄ μ‹€μ œ νƒ€μž…μ΄ μ •ν™•νžˆ 무엇인지 μ»΄νŒŒμΌλŸ¬κ°€ μ•Œ 수 μ—†κΈ° λ•Œλ¬Έμ΄λ‹€.

λ¦¬μŠ€νŠΈμ— μƒˆλ‘œμš΄ μš”μ†Œλ₯Ό μΆ”κ°€ν•  λ•Œ νƒ€μž… μ•ˆμ „μ„±μ„ μœ μ§€ν•˜κΈ° μœ„ν•΄, μžλ°”λŠ” 곡병성에 μ˜ν•΄ μ •μ˜λœ λ¦¬μŠ€νŠΈμ— μƒˆλ‘œμš΄ μš”μ†Œλ₯Ό μΆ”κ°€ν•˜λŠ” 것을 μ œν•œν•œλ‹€.

 

- 예제

List<? extends Fruit> fruits = new ArrayList<Apple>();

// fruits.add(new Apple());  // 컴파일 였λ₯˜
// fruits.add(new Fruit());  // 컴파일 였λ₯˜

 

μœ„ μ½”λ“œμ—μ„œ fruits λ¦¬μŠ€νŠΈλŠ” Fruit의 ν•˜μœ„ νƒ€μž…λ“€λ‘œ μ œν•œλ˜κΈ° λ•Œλ¬Έμ— Apple νƒ€μž…μ˜ 리슀트둜 μ΄ˆκΈ°ν™”λ˜μ—ˆλ‹€. κ·ΈλŸ¬λ‚˜ fruits λ¦¬μŠ€νŠΈμ— μƒˆλ‘œμš΄ μš”μ†Œλ₯Ό μΆ”κ°€ν•˜λ €κ³  ν•  λ•Œ, 컴파일 였λ₯˜κ°€ λ°œμƒν•œλ‹€. μ™œ κ·ΈλŸ°μ§€ μ•Œμ•„λ³΄κ² λ‹€.

 

 

- νƒ€μž… μ•ˆμ „μ„± 문제

fruits의 μ„ μ–Έ νƒ€μž…μ€ List<? extends Fruit>둜, μ΄λŠ” Fruit의 ν•˜μœ„ νƒ€μž…μ„ μ˜λ―Έν•œλ‹€. 이 μ‹œμ μ—μ„œ μžλ°” μ»΄νŒŒμΌλŸ¬λŠ” fruits λ¦¬μŠ€νŠΈκ°€ μ‹€μ œλ‘œ μ–΄λ–€ ν•˜μœ„ νƒ€μž…(Apple, Orange λ“±)의 λ¦¬μŠ€νŠΈμΈμ§€ μ •ν™•νžˆ μ•Œ 수 μ—†λ‹€.

λ§Œμ•½ fruits λ¦¬μŠ€νŠΈμ— new Apple()을 μΆ”κ°€ν•œλ‹€κ³  κ°€μ •ν•΄ 보자. fruitsκ°€ List<Orange>둜 μ΄ˆκΈ°ν™”λ˜μ—ˆλ‹€λ©΄, Apple 객체λ₯Ό Orange 객체둜 μ·¨κΈ‰ν•˜κ²Œ λ˜μ–΄ νƒ€μž… 뢈일치 λ¬Έμ œκ°€ λ°œμƒν•œλ‹€. λ”°λΌμ„œ, μ»΄νŒŒμΌλŸ¬λŠ” fruits λ¦¬μŠ€νŠΈμ— Apple 객체λ₯Ό μΆ”κ°€ν•˜λŠ” 것을 ν—ˆμš©ν•˜μ§€ μ•ŠλŠ”λ‹€.

- 컴파일러의 νƒ€μž… μΆ”λ‘  μ œν•œ: List<? extends Fruit>λŠ” μ»΄νŒŒμΌλŸ¬μ—κ²Œ "이 λ¦¬μŠ€νŠΈλŠ” Fruit의 ν•˜μœ„ νƒ€μž…μœΌλ‘œ κ΅¬μ„±λ˜μ–΄ μžˆλ‹€"κ³  μ•Œλ €μ€€λ‹€. κ·ΈλŸ¬λ‚˜ 이 λ¦¬μŠ€νŠΈκ°€ μ–΄λ–€ νŠΉμ •ν•œ ν•˜μœ„ νƒ€μž…μ˜ λ¦¬μŠ€νŠΈμΈμ§€ μ•Œ 수 μ—†κΈ° λ•Œλ¬Έμ—, μ»΄νŒŒμΌλŸ¬λŠ” νƒ€μž… μ•ˆμ „μ„±μ„ 보μž₯ν•˜κΈ° μœ„ν•΄ μš”μ†Œ μΆ”κ°€λ₯Ό 막닀.

 

λ”°λΌμ„œ, κ³΅λ³€μ„±μ—μ„œλŠ” μƒˆλ‘œμš΄ μš”μ†Œλ₯Ό μΆ”κ°€ν•˜λŠ” 것이 μ œν•œλœλ‹€. 리슀트의 νŠΉμ • νƒ€μž…μ„ μ •ν™•νžˆ μ•Œ 수 μ—†λŠ” κ²½μš°κ°€ 많기 λ•Œλ¬Έμ—, 이둜 인해 λ°œμƒν•  수 μžˆλŠ” 잠재적인 νƒ€μž… λΆˆμΌμΉ˜μ™€ 같은 문제λ₯Ό λ°©μ§€ν•˜κΈ° μœ„ν•΄μ„œμ΄λ‹€.

 

κ³΅λ³€μ„±μ˜ Practical μ‚¬μš© 사둀

곡변성은 일반적으둜 읽기 μ „μš© μƒν™©μ—μ„œ μœ μš©ν•˜λ‹€. 예λ₯Ό λ“€μ–΄, λ©”μ„œλ“œκ°€ μ œλ„€λ¦­ 리슀트λ₯Ό 인자둜 λ°›μ•„ 리슀트의 μš”μ†Œλ₯Ό μˆœνšŒν•˜κ±°λ‚˜ 읽기만 ν•  λ•Œ, 곡변성을 μ‚¬μš©ν•˜λŠ” 것이 μ μ ˆν•˜λ‹€. μ΄λ ‡κ²Œ ν•˜λ©΄ ν•˜μœ„ νƒ€μž…μ˜ μš”μ†Œλ₯Ό ν¬ν•¨ν•˜λŠ” λ¦¬μŠ€νŠΈλ„ μƒμœ„ νƒ€μž…μ˜ 리슀트둜 μ·¨κΈ‰ν•  수 μžˆμ–΄ μœ μ—°μ„±μ΄ 높아진닀.

public void printFruits(List<? extends Fruit> fruits) {
    for (Fruit fruit : fruits) {
        System.out.println(fruit);
    }
}

 

μœ„ λ©”μ„œλ“œλŠ” Fruit의 ν•˜μœ„ νƒ€μž…λ“€μ„ ν¬ν•¨ν•˜λŠ” 리슀트λ₯Ό 인자둜 받을 수 있고, μš”μ†Œλ“€μ„ μ•ˆμ „ν•˜κ²Œ 좜λ ₯ν•  수 μžˆλ‹€.

 

2. λ°˜κ³΅λ³€μ„± Contravariance

μ •μ˜

λ°˜κ³΅λ³€μ„±μ€ μ œλ„€λ¦­ νƒ€μž…μ΄ νŠΉμ • νƒ€μž…μ˜ μƒμœ„ νƒ€μž… 관계λ₯Ό μœ μ§€ν•˜λŠ” 것을 μ˜λ―Έν•œλ‹€.

 

κ·ΈλŸ¬λ‹ˆκΉŒ, A νƒ€μž…κ³Ό A νƒ€μž…λ₯Ό 상속받은 A'νƒ€μž…μ΄ 있고, B νƒ€μž…κ³Ό B νƒ€μž…μ„ 상속받은 B' νƒ€μž…μ΄ μžˆλ‹€λ©΄, A<? super B'> λ³€μˆ˜μ— A'<B> 객체λ₯Ό 넣을 수 μžˆλ‹€λŠ” 것이닀.

μ˜ˆμ‹œ

λ§Œμ•½ Fruit이 Apple의 μƒμœ„ νƒ€μž…μ΄λΌλ©΄, List<Fruit>도 List<Apple>의 μƒμœ„ νƒ€μž…μœΌλ‘œ 간주될 수 μžˆλŠ” 상황이닀.

μ‚¬μš© 사둀

μžλ°”μ—μ„œλŠ” λ°˜κ³΅λ³€μ„±μ„ μ§€μ›ν•˜κΈ° μœ„ν•΄ ν•˜ν•œ 경계 μ™€μΌλ“œμΉ΄λ“œ(? super T)λ₯Ό μ‚¬μš©ν•œλ‹€. μ΄λŠ” νŠΉμ • μ œλ„€λ¦­ νƒ€μž…μ΄ νŠΉμ • ν•˜μœ„ 클래슀의 μŠˆνΌνƒ€μž…(μƒμœ„ νƒ€μž…)λ“€λ§Œ ν—ˆμš©ν•œλ‹€λŠ” 것을 μ˜λ―Έν•œλ‹€.

List<? super Apple> apples = new ArrayList<Fruit>();

 

μœ„ μ½”λ“œμ—μ„œ List<? super Apple>λŠ” Apple의 μƒμœ„ νƒ€μž…λ“€(Fruit, Object λ“±)의 리슀트둜 μ‚¬μš©ν•  수 μžˆλ‹€.

 

μ œμ•½

λ°˜κ³΅λ³€μ„±μ—μ„œλŠ” λ¦¬μŠ€νŠΈμ—μ„œ 값을 κΊΌλ‚Ό λ•Œ νƒ€μž…μ΄ μ •ν™•νžˆ 무엇인지 μ•Œ 수 μ—†μœΌλ―€λ‘œ, μƒμœ„ νƒ€μž…μœΌλ‘œλ§Œ λ‹€λ£° 수 μžˆλ‹€.

 

List<? super T>λ₯Ό μ‚¬μš©ν•  λ•Œ, λ¦¬μŠ€νŠΈμ— 값을 μΆ”κ°€ν•˜λŠ” 것은 κ°€λŠ₯ν•˜μ§€λ§Œ, 값을 κΊΌλ‚Ό λ•ŒλŠ” λ‹€μŒκ³Ό 같은 μ œμ•½μ΄ μžˆλ‹€.

 

- νƒ€μž… μ •λ³΄μ˜ 상싀

List<? super T> λŠ” T의 ν•˜μœ„ νƒ€μž…μ„ μˆ˜μš©ν•  수 μžˆλŠ” λ¦¬μŠ€νŠΈμ΄λ‹€. 이 경우, λ¦¬μŠ€νŠΈμ— 담겨 μžˆλŠ” κ°μ²΄λŠ” T λ˜λŠ” T의 μƒμœ„ νƒ€μž…μΌ 수 μžˆλ‹€. 즉, λ¦¬μŠ€νŠΈμ— T 객체λ₯Ό μΆ”κ°€ν•  수 μžˆμ§€λ§Œ, λ¦¬μŠ€νŠΈμ—μ„œ κΊΌλ‚Ό λŒ€μ—λŠ” κ·Έ 객체의 μ •ν™•ν•œ νƒ€μž…μ„ μ•Œ 수 μ—†λ‹€.

- νƒ€μž…μ˜ λΆˆν™•μ‹€μ„±

List<? super T>μ—μ„œ 값을 κΊΌλ‚Ό λ•Œ, κΊΌλ‚Έ 객체의 νƒ€μž…μ€ Object둜 μ œν•œλœλ‹€. μ΄λŠ” λ¦¬μŠ€νŠΈκ°€ T의 ν•˜μœ„ νƒ€μž…λΏλ§Œ μ•„λ‹ˆλΌ T의 μƒμœ„ νƒ€μž…λ„ μˆ˜μš©ν•  수 있기 λ•Œλ¬Έμ΄λ‹€. λ”°λΌμ„œ, κΊΌλ‚Έ 객체가 T의 μ„œλΈŒνƒ€μž…μΈμ§€ μ•„λ‹Œμ§€ μ•Œ 수 μ—†κΈ° λ•Œλ¬Έμ—, ν•΄λ‹Ή 객체λ₯Ό T둜 μ•ˆμ „ν•˜κ²Œ μΊμŠ€νŒ…ν•  수 μ—†λ‹€. 

 

-예제

import java.util.ArrayList;
import java.util.List;

public class ContravarianceExample {

    public static void main(String[] args) {
        List<? super Integer> list = new ArrayList<>();
        list.add(1);  // Integerλ₯Ό μΆ”κ°€ν•  수 있음
        list.add(2);  // Integerλ₯Ό μΆ”κ°€ν•  수 있음

        Object obj = list.get(0);  // κΊΌλ‚Έ κ°’μ˜ νƒ€μž…μ€ Object둜 μ œν•œλ¨
        // Integer number = list.get(0);  // 컴파일 였λ₯˜: μ •ν™•ν•œ νƒ€μž…μ„ μ•Œ 수 μ—†κΈ° λ•Œλ¬Έ

        System.out.println(obj);  // 좜λ ₯: 1
    }
}

 

 

κ°’ μΆ”κ°€:

  • list.add(1)κ³Ό list.add(2)λŠ” κ°€λŠ₯ν•˜λ©°, Integerλ₯Ό List<? super Integer>에 μΆ”κ°€ν•  수 μžˆλ‹€.

κ°’ κΊΌλ‚΄κΈ°:

  • Object obj = list.get(0)은 κ°€λŠ₯ν•˜λ©°, get λ©”μ„œλ“œλŠ” λ¦¬μŠ€νŠΈμ— μžˆλŠ” 값을 Object둜 λ°˜ν™˜ν•œλ‹€. μ΄λŠ” λ¦¬μŠ€νŠΈμ— μ €μž₯된 객체의 νƒ€μž…μ΄ μ •ν™•νžˆ 무엇인지 μ•Œ 수 μ—†κΈ° λ•Œλ¬Έμ΄λ‹€.
  • Integer number = list.get(0)은 컴파일 였λ₯˜λ₯Ό λ°œμƒμ‹œν‚¨λ‹€. list.get(0)이 λ°˜ν™˜ν•˜λŠ” 값이 Integer일 μˆ˜λ„ μžˆμ§€λ§Œ, Object둜 λ°˜ν™˜λ˜κΈ° λ•Œλ¬Έμ— Integer둜 직접 μΊμŠ€νŒ…ν•  수 μ—†λ‹€. μ •ν™•ν•œ νƒ€μž…μ„ μ•Œ 수 μ—†κΈ° λ•Œλ¬Έμ— μ•ˆμ „ν•˜μ§€ μ•Šλ‹€.

 

λΉ„κ³΅λ³€μ„±μ˜ Practical μ‚¬μš© 사둀

νƒ€μž…μ„ μƒμœ„ νƒ€μž…μœΌλ‘œ λ‹€λ£¨λ©΄μ„œ μœ μ—°ν•œ μ½”λ“œλ₯Ό μž‘μ„±ν•  λ•Œ νš¨κ³Όμ μ΄λ‹€.

 

μ˜ˆμ‹œ 1 : 이벀트 λ¦¬μŠ€νŠΈμ™€ 콜백

이벀트 λ¦¬μŠ€λ„ˆλ‚˜ 콜백 νŒ¨ν„΄μ—μ„œ λ°˜κ³΅λ³€μ„±μ„ ν™œμš©ν•  수 μžˆλ‹€. μ΄λŸ¬ν•œ νŒ¨ν„΄μ€ νŠΉμ • νƒ€μž…μ˜ 이벀트λ₯Ό μ²˜λ¦¬ν•˜λŠ” ν•Έλ“€λŸ¬λ₯Ό λ“±λ‘ν•˜λŠ” κ²½μš°κ°€ λ§Žλ‹€.

이벀트 λ¦¬μŠ€ν„° μ •μ˜

interface EventListener<T> {
    void handleEvent(T event);
}

 

λ‹€μ–‘ν•œ 이벀트 ν•Έλ“€λŸ¬ 등둝

import java.util.ArrayList;
import java.util.List;

public class EventDispatcher {

    private List<EventListener<? super Integer>> listeners = new ArrayList<>();

    // ν•Έλ“€λŸ¬ μΆ”κ°€ λ©”μ„œλ“œ
    public void addListener(EventListener<? super Integer> listener) {
        listeners.add(listener);
    }

    // 이벀트 λ°œμƒ λ©”μ„œλ“œ
    public void dispatchEvent(Integer event) {
        for (EventListener<? super Integer> listener : listeners) {
            listener.handleEvent(event);
        }
    }
    
    public static void main(String[] args) {
        EventDispatcher dispatcher = new EventDispatcher();
        
        // Integer 이벀트λ₯Ό μ²˜λ¦¬ν•˜λŠ” λ¦¬μŠ€λ„ˆ 등둝
        dispatcher.addListener(new EventListener<Integer>() {
            @Override
            public void handleEvent(Integer event) {
                System.out.println("Integer Event: " + event);
            }
        });

        // Number 이벀트λ₯Ό μ²˜λ¦¬ν•˜λŠ” λ¦¬μŠ€λ„ˆ 등둝
        dispatcher.addListener(new EventListener<Number>() {
            @Override
            public void handleEvent(Number event) {
                System.out.println("Number Event: " + event);
            }
        });

        dispatcher.dispatchEvent(42); // Integer 이벀트 λ°œμƒ
    }
}

 

μœ„ μ½”λ“œμ—μ„œ EventDispatcherλŠ” EventListener<? super Integer> νƒ€μž…μ˜ λ¦¬μŠ€λ„ˆλ₯Ό 등둝할 수 μžˆλ‹€. μ΄λŠ” Integer뿐만 μ•„λ‹ˆλΌ Number와 같은 Integer의 μƒμœ„ νƒ€μž…μ„ μ²˜λ¦¬ν•˜λŠ” λ¦¬μŠ€λ„ˆλ„ μˆ˜μš©ν•  수 있게 ν•œλ‹€. 이둜 인해 μ½”λ“œμ˜ μœ μ—°μ„±μ΄ 높아지고 λ‹€μ–‘ν•œ νƒ€μž…μ˜ λ¦¬μŠ€λ„ˆλ₯Ό 등둝할 수 μžˆλ‹€.

3. 무곡변성 Invariance

μ •μ˜

무곡변성은 μ œλ„€λ¦­ νƒ€μž…μ΄ νŠΉμ • νƒ€μž…μ˜ μƒμœ„ λ˜λŠ” ν•˜μœ„ 관계λ₯Ό ν—ˆμš©ν•˜μ§€ μ•ŠλŠ” 것을 μ˜λ―Έν•œλ‹€. 즉, μ œλ„€λ¦­ νƒ€μž…μ— λŒ€ν•΄ μ •ν™•νžˆ μΌμΉ˜ν•˜λŠ” νƒ€μž…λ§Œ μ‚¬μš©ν•΄μ•Ό ν•œλ‹€.

μ˜ˆμ‹œ

List<Fruit>λŠ” List<Apple>의 ν•˜μœ„ νƒ€μž…λ„ μ•„λ‹ˆκ³  μƒμœ„ νƒ€μž…λ„ μ•„λ‹ˆλ‹€. 두 μ œλ„€λ¦­ νƒ€μž…μ€ μ„œλ‘œ μ „ν˜€ 관련이 μ—†λ‹€.

List<Fruit> fruits = new ArrayList<Fruit>(); // μ •ν™•νžˆ μΌμΉ˜ν•΄μ•Όλ§Œ ν•©λ‹ˆλ‹€.

 

 

μœ„ μ½”λ“œμ—μ„œ List<Fruit>λŠ” List<Apple>둜 λŒ€μ²΄λ  수 μ—†λ‹€. λ¬΄κ³΅λ³€μ„±μ—μ„œλŠ” μ œλ„€λ¦­ νƒ€μž…μ˜ 상속 관계가 μœ μ§€λ˜μ§€ μ•ŠλŠ”λ‹€.

 

2. 변성이 μ‘΄μž¬ν•˜λŠ” 이유

변성은 μ œλ„€λ¦­ νƒ€μž…μ΄ 상속 ꡬ쑰와 ν•¨κ»˜ μ‚¬μš©λ  λŒ€μ˜ μ•ˆμ „μ„±μ„ 보μž₯ν•˜κΈ° μœ„ν•΄ ν•„μš”ν•˜λ‹€. 변성을 적절히 μ‚¬μš©ν•˜μ§€ μ•ŠμœΌλ©΄ νƒ€μž… μ•ˆμ •μ„±μ΄ 깨질 수 있으며, μ΄λŠ” λŸ°νƒ€μž„ μ˜ˆμ™Έλ‚˜ νƒ€μž… 뢈일치 였λ₯˜λ₯Ό μœ λ°œν•  수 μžˆλ‹€. λ”°λΌμ„œ μžλ°”μ—μ„œλŠ” μ œλ„€λ¦­ νƒ€μž…μ— λŒ€ν•œ 변성을 λͺ…μ‹œμ μœΌλ‘œ ν‘œν˜„ν•˜μ—¬ κ°œλ°œμžκ°€ νƒ€μž… μ•ˆμ „μ„±μ„ μœ μ§€ν•˜λ©΄μ„œ μ œλ„€λ¦­μ„ μ‚¬μš©ν•  수 μžˆλ„λ‘ ν•œλ‹€.

 

1. 곡변성과 νƒ€μž… μ•ˆμ „μ„±

곡변성은 μžλ„€λ¦­ νƒ€μž…μ΄ 상속 κ³„μΈ΅μ—μ„œ ν•˜μœ„ νƒ€μž…μœΌλ‘œ ν™•μž₯될 λ•Œ μœ μš©ν•˜λ‹€. 예λ₯Ό λ“€μ–΄, List<? extends Number>λŠ” Number의 ν•˜μœ„ νƒ€μž…λ“€μ— λŒ€ν•œ 리슀트λ₯Ό μ•ˆμ „ν•˜κ²Œ μ²˜λ¦¬ν•  수 μžˆλ‹€. ν•˜μ§€λ§Œ λ¦¬μŠ€νŠΈμ— μƒˆλ‘œμš΄ μš”μ†Œλ₯Ό μΆ”κ°€ν•  λ•ŒλŠ” μ•ˆμ „ν•˜μ§€ μ•Šμ„ 수 μžˆμœΌλ―€λ‘œ 읽기 μ „μš© λ¦¬μŠ€νŠΈμ— 쀄 μ‚¬μš©λœλ‹€.

2. λ°˜κ³΅λ³€μ„±κ³Ό μœ μ—°μ„±

λ°˜κ³΅λ³€μ„±μ€ μ œλ„€λ¦­ νƒ€μž…μ΄ 상속 κ³„μΈ΅μ—μ„œ μƒμœ„ νƒ€μž…μœΌλ‘œ ν™•μž₯될 λ•Œ μœ μš©ν•˜λ‹€. 예λ₯Ό λ“€μ–΄, Comparator<? super Integer>λŠ” Integer의 μƒμœ„ νƒ€μž…λ“€μ— λŒ€ν•œ 비ꡐ기λ₯Ό μ‚¬μš©ν•˜μ—¬ 객체듀을 비ꡐ할 수 μžˆλ‹€. μ΄λŠ” λΉ„κ΅κΈ°μ˜ μœ μ—°μ„±μ„ μ¦κ°€μ‹œν‚¨λ‹€.

 

3. μ‹€μ œ μ˜ˆμ‹œ

곡변성과 λ°˜κ³΅λ³€μ„±μ€ μžλ°”μ˜ μ»¬λ ‰μ…˜ ν”„λ ˆμž„μ›Œν¬μ—μ„œ 자주 μ‚¬μš©λœλ‹€. 예λ₯Ό λ“€μ–΄, Collections.copy λ©”μ†Œλ“œλŠ” 곡변성과 λ°˜κ³΅λ³€μ„± λͺ¨λ‘λ₯Ό μ‚¬μš©ν•΄ 맀우 μœ μ—°ν•˜κ²Œ λ™μž‘ν•œλ‹€.

public static <T> void copy(List<? super T> dest, List<? extends T> src) {
    for (int i = 0; i < src.size(); i++) {
        dest.set(i, src.get(i));
    }
}

 

μœ„ λ©”μ†Œλ“œλŠ” src λ¦¬μŠ€νŠΈμ—μ„œ 데이터λ₯Ό 읽고, dest 리슀트둜 λ³΅μ‚¬ν•œλ‹€. srcλŠ” T의 ν•˜μœ„ νƒ€μž…μ˜ 리슀트둜 μ œν•œλ˜λ©°, destλŠ” T의 μƒμœ„ νƒ€μž…μ˜ 리슀트둜 μ œν•œλœλ‹€. 이 λ©”μ†Œλ“œλŠ” 맀우 μœ μ—°ν•˜λ©°, λ‹€μ–‘ν•œ νƒ€μž…μ˜ 리슀트λ₯Ό 볡사할 λ•Œ μ‚¬μš©λ  수 μžˆλ‹€.