[Java] Generic

이ꢌ민·2025λ…„ 11μ›” 22일

JAVA

λͺ©λ‘ 보기
3/3

λͺ©μ°¨

  1. Generic
  2. μ‚¬μš©μ˜ˆμ œ
  3. wild card
  4. PECS

Generic

πŸ’‘ 클래슀 λ‚΄λΆ€μ—μ„œ μ‚¬μš©ν•  데이터 νƒ€μž…μ„ μ™ΈλΆ€μ—μ„œ μ§€μ •ν•˜λŠ” 기법, 즉 νƒ€μž…μ˜ λ³€μˆ˜ν™”

ArrayList<String> list = new ArrayList<>();
  • μ—¬κΈ°μ„œ νƒ€μž…μ„ μ§€μ •ν•˜λŠ” < > κ°€ μ œλ„€λ¦­

νƒ€μž… λ§€κ°œλ³€μˆ˜ <T>

  • ꡬ체화: μΈμŠ€ν„΄μŠ€ 생성 μ‹œ νƒ€μž… μ§€μ • β†’ μ œλ„€λ¦­ νƒ€μž… μ „νŒŒ

  • νƒ€μž… νŒŒλΌλ―Έν„° μƒλž΅ κ°€λŠ₯ (μƒμ„±μž νƒ€μž… μΆ”λ‘ )

  • Reference νƒ€μž…λ§Œ κ°€λŠ₯ (μ›μ‹œ νƒ€μž… X)

  • Java μ œλ„€λ¦­μ€ 컴파일 νƒ€μž„ νƒ€μž…μ†Œκ±°(Type Erasure) 기반
    primitive μ‚¬μš© λΆˆκ°€ β†’ Wrapper(Integer λ“±) μžλ™ λ°•μ‹±μœΌλ‘œ 싀무 뢈편 거의 μ—†μŒ

  • μ œλ„€λ¦­ νƒ€μž… λ³€μˆ˜ κ΄€λ‘€

    • : νƒ€μž…
    • : μš”μ†Œ ex) List
    • : ν‚€ ex) Map<k,v>
    • : 리턴 κ°’ λ˜λŠ” λ§€ν•‘λœ κ°’(Variable)
    • : 숫자
    • <S, U, V> : 2번째, 3번쨰, 4λ²ˆμ§Έμ— μ„ μ–Έλœ νƒ€μž…

μ‚¬μš© 이유

  • 컴파일 μ‹œ νƒ€μž… 검사 β†’ λŸ°νƒ€μž„ μ˜ˆμ™Έ λ°©μ§€
  • λΆˆν•„μš”ν•œ μΊμŠ€νŒ… 제거 β†’ μ„±λŠ₯ 및 가독성 ν–₯상

μ£Όμ˜μ‚¬ν•­

μ œλ„€λ¦­ νƒ€μž… μžμ²΄λŠ” 생성 λΆˆκ°€

  • TλŠ” 컴파일 νƒ€μž„μ—λ§Œ 쑴재, λŸ°νƒ€μž„ μ†Œκ±°. JVM이 μ‹€μ œ νƒ€μž…μ„ μ•Œ 수 μ—†μ–΄ new T()λ₯Ό λ§Œλ“€ 수 μ—†μŒ
T item = new T();      // λΆˆκ°€
T[] arr = new T[10];   // λΆˆκ°€

static 에 μ œλ„€λ¦­ μ‚¬μš© λΆˆκ°€

  • 클래슀 λ ˆλ²¨μ—μ„œ 곡유, κ°μ²΄λ§ˆλ‹€ λ‹€λ₯Έ T νƒ€μž… κ°€μ§ˆ 수 있음 -> νƒ€μž… μ•ˆμ •μ„± 보μž₯

μ œλ„€λ¦­ 클래슀 λ°°μ—΄ 생성 λΆˆκ°€(선언은 κ°€λŠ₯)


κΈ°λ³Έ μ˜ˆμ‹œ

class FruitBox<T> {
    List<T> fruits = new ArrayList<>();
    public void add(T fruit) {
        fruits.add(fruit);
    }
}

FruitBox<Integer> intBox = new FruitBox<>();
FruitBox<Double> doubleBox = new FruitBox<>();
FruitBox<String> strBox = new FruitBox<>();
FruitBox<Apple> appleBox = new FruitBox<>();

μ‚¬μš© 예제

볡수 νƒ€μž… νŒŒλΌλ―Έν„°

class Apple {}
class Banana {}

class FruitBox<T, U> {
    List<T> apples = new ArrayList<>();
    List<U> bananas = new ArrayList<>();

    public void add(T apple, U banana) {
        apples.add(apple);
        bananas.add(banana);
    }
}

쀑첩 νƒ€μž… νŒŒλΌλ―Έν„°

ArrayList<LinkedList<String>> list = new ArrayList<>();
LinkedList<String> node1 = new LinkedList<>();
node1.add("aa"); node1.add("bb");

μ œλ„€λ¦­ μΈν„°νŽ˜μ΄μŠ€

  • μΈν„°νŽ˜μ΄μŠ€λ₯Ό implements ν•œ ν΄λž˜μŠ€μ—μ„œλ„ μ˜€λ²„λΌμ΄λ”©ν•œ λ©”μ„œλ“œλ₯Ό μ œλ„€λ¦­ νƒ€μž…μ— λ§žμΆ°μ„œ λ˜‘κ°™μ΄ κ΅¬ν˜„ν•΄μ•Όν•¨
interface ISample<T> {
    void addElement(T t, int index);
    T getElement(int index);
}

μ œλ„€λ¦­ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€

  • λžŒλ‹€ ν‘œν˜„μ‹μ˜ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€ μˆ™μ§€ ν•„μš”
interface IAdd<T> { T add(T x, T y); }
IAdd<Integer> sum = (x, y) -> x + y;

μ œλ„€λ¦­ λ©”μ„œλ“œ

  • λ©”μ„œλ“œ 선언뢀에 κ°€ μ„ μ–Έλœ λ©”μ„œλ“œ: ν΄λž˜μŠ€μ—μ„œ μ„€μ •λœ νƒ€μž…μ„ λ°›μ•„μ˜€λŠ” 것이 μ•„λ‹Œ 직접 λ©”μ„œλ“œμ—μ„œ λ™μ μœΌλ‘œ νƒ€μž…μ„ 받아와 μ‚¬μš©ν•  수 μžˆλŠ” λ…λ¦½μ μœΌλ‘œ μš΄μš©κ°€λŠ₯ν•œ μ œλ„€λ¦­λ©”μ„œλ“œ
    • μ œλ„€λ¦­ ν΄λž˜μŠ€μ— μ •μ˜λœ νƒ€μž… λ§€κ°œλ³€μˆ˜μ™€ μ œλ„€λ¦­ λ©”μ„œλ“œμ— μ •μ˜λœ νƒ€μž… λ§€κ°œλ³€μˆ˜λŠ” λ³„κ°œ
    • 호좜 μ—­μ‹œ λ©”μ„œλ“œ μ™Όμͺ½
    • μ»΄νŒŒμΌλŸ¬κ°€ μ œλ„€λ¦­ νƒ€μž…μ— λ“€μ–΄κ°ˆ 데이터 νƒ€μž…μ„ λ©”μ†Œλ“œμ˜ λ§€κ°œλ³€μˆ˜λ₯Ό 톡해 μΆ”μ •ν•  수 있기 λ•Œλ¬Έμ—, λŒ€λΆ€λΆ„μ˜ 경우 μ œλ„€λ¦­ λ©”μ„œλ“œμ˜ νƒ€μž… νŒŒλΌλ―Έν„°λ₯Ό μƒλž΅ν•˜κ³  ν˜ΈμΆœκ°€λŠ₯
    • 처음 μ œλ„€λ¦­ 클래슀λ₯Ό μΈμŠ€ν„΄μŠ€ν™”ν•˜λ©΄, 클래슀 νƒ€μž… λ§€κ°œλ³€μˆ˜μ— μ „λ‹¬ν•œ νƒ€μž…μ— 따라 μ œλ„€λ¦­ λ©”μ†Œλ“œλ„ νƒ€μž…μ΄ 정해짐. 그런데 만일 μ œλ„€λ¦­ λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν• λ•Œ 직접 νƒ€μž… νŒŒλΌλ―Έν„°λ₯Ό λ‹€λ₯΄κ²Œ μ§€μ •ν•΄μ£Όκ±°λ‚˜, λ‹€λ₯Έ νƒ€μž…μ˜ 데이터λ₯Ό λ§€κ°œλ³€μˆ˜μ— λ„˜κΈ΄ν•˜λ©΄Β λ…λ¦½μ μΈ νƒ€μž…μ„ κ°€μ§„ μ œλ„€λ¦­ λ©”μ„œλ“œλ‘œ 운용
class Box<T> {
    public <U> void print(U value) { System.out.println(value); }
}

// μ‹€μ œ μ‚¬μš©
  class FruitBox<T> {

    // 클래슀의 νƒ€μž… νŒŒλΌλ―Έν„°λ₯Ό 받아와 μ‚¬μš©ν•˜λŠ” 일반 λ©”μ„œλ“œ
    public T addBox(T x, T y) {
        // ...
    }

    // λ…λ¦½μ μœΌλ‘œ νƒ€μž… ν• λ‹Ή μš΄μ˜λ˜λŠ” μ œλ„€λ¦­ λ©”μ„œλ“œ
    public static <T> T addBoxStatic(T x, T y) {
        // ...
    }
  }
  FruitBox.<Integer>addBoxStatic(1, 2);
  FruitBox.<String>addBoxStatic("μ•ˆλ…•", "μž˜κ°€");

    // λ©”μ„œλ“œμ˜ μ œλ„€λ¦­ νƒ€μž… μƒλž΅
  FruitBox.addBoxStatic(1, 2);
  FruitBox.addBoxStatic("μ•ˆλ…•", "μž˜κ°€");

μ œν•œλœ νƒ€μž… λ§€κ°œλ³€μˆ˜ (Bounded Type)

  • νƒ€μž… ν•œμ • ν‚€μ›Œλ“œ, extends - <T extends [μ œν•œ νƒ€μž…]> - 일반 클래슀, 좔상 클래슀, μΈν„°νŽ˜μ΄μŠ€ λͺ¨λ‘ κ°€λŠ₯ - extendsλŠ” 클래슀 1개만, μΈν„°νŽ˜μ΄μŠ€λŠ” μ—¬λŸ¬ 개 κ°€λŠ₯ - 닀쀑 νƒ€μž… ν•œμ • μ‹œ & μ—°μ‚°μž μ‚¬μš© - μž¬κ·€μ  νƒ€μž… ν•œμ •: Comparable - <E extends Comparable>

class A {}
interface B {}


class Sample<T extends A & B> {}
class Box<T extends Comparable<T>> {}

μ œλ„€λ¦­ ν˜•λ³€ν™˜

  • μ œλ„€λ¦­ μ„œλΈŒ νƒ€μž…κ°„ ν˜•λ³€ν™˜ λΆˆκ°€. 전달받은 κ·Έ νƒ€μž…μœΌλ‘œλ§Œ μ„œλ‘œ μΊμŠ€νŒ… κ°€λŠ₯
    • μ›μ†Œ - μ›μ†Œ o <-> 객체 - 객체 x

wild card

πŸ’‘λ³€μ„±(Variance): νƒ€μž… κ°„ 상속 관계가 μ œλ„€λ¦­μ—μ„œ μ–΄λ–»κ²Œ λ°˜μ˜λ˜λŠ”κ°€
곡변성: μ„œλ‘œ λ‹€λ₯Έ νƒ€μž…κ°„μ— 함계 λ³€ν•  수 μžˆλ‹€λŠ” νŠΉμ§•(Liskov μΉ˜ν™˜ 원칙)


곡변 :Β SΒ κ°€Β T 의 ν•˜μœ„ νƒ€μž…μ΄λ©΄, S[] λŠ” T[] 의 ν•˜μœ„ νƒ€μž…μ΄λ‹€. List λŠ” List 의 ν•˜μœ„ νƒ€μž…μ΄λ‹€.

λ°˜κ³΅λ³€Β :Β SΒ κ°€Β T의 ν•˜μœ„ ν…μŠ€νŠΈνƒ€μž…μ΄λ©΄, T[] λŠ” S[] 의 ν•˜μœ„ νƒ€μž…μ΄λ‹€. (κ³΅λ³€μ˜ λ°˜λŒ€). ListΒ λŠ” List 의 ν•˜μœ„ νƒ€μž…μ΄λ‹€. (κ³΅λ³€μ˜ λ°˜λŒ€)

무곡변 / λΆˆκ³΅λ³€Β :Β S 와 TΒ λŠ” μ„œλ‘œ 관계가 μ—†λ‹€. List 와 List λŠ” μ„œλ‘œ λ‹€λ₯Έ νƒ€μž…μ΄λ‹€.

곡변 / λ°˜κ³΅λ³€ / 무곡변

  // 곡변성
Object[] Covariance = new Integer[10];

// λ°˜κ³΅λ³€μ„±
Integer[] Contravariance = (Integer[]) Covariance;

  // 곡변성
ArrayList<Object> Covariance = new ArrayList<Integer>();

// λ°˜κ³΅λ³€μ„±
ArrayList<Integer> Contravariance = new ArrayList<Object>();

μ œλ„€λ¦­ νƒ€μž…μ— λŒ€ν•΄μ„œ 곡변성 / λ°˜κ³΅λ³€μ„±μ„ μ§€μ›ν•˜μ§€ μ•ŠμŒ

  • 객체 νƒ€μž…, μ œλ„€λ¦­ ν΄λž˜μŠ€λŠ” λ‹€ν˜•μ„± 지원. μ œλ„€λ¦­ νƒ€μž…μ€ 무곡변, 전달받은 νƒ€μž…μœΌλ‘œλ§Œ μ„œλ‘œ μΊμŠ€νŒ… κ°€λŠ₯
  • object[] 같은 경우 문제 μ—†μŒ, List 같은 경우 μ—…, λ‹€μš΄μΊμŠ€νŒ… x. κ³ μ •λœ νƒ€μž…λ§Œ κ°€λŠ₯ -> μ œλ„€λ¦­ μ™€μΌλ“œ μΉ΄λ“œ

    λ°°μ—΄ 곡변성 문제

    Object[] numbers = new Integer[10];
    List<Object> list = new ArrayList<Integer>(); // 컴파일 μ—λŸ¬
    numbers[0] = "text"; // λŸ°νƒ€μž„ μ—λŸ¬

    μ œλ„€λ¦­μ—μ„œ μ™€μΌλ“œμΉ΄λ“œλ‘œ ν•΄κ²°

    List<Integer> ints = Arrays.asList(1,2,3);
    List<? extends Number> nums = ints;

    μ™€μΌλ“œμΉ΄λ“œ μ’…λ₯˜

    <?> (Unbounded)

    • λͺ¨λ“  ν΄λž˜μŠ€λ‚˜ μΈν„°νŽ˜μ΄μŠ€ νƒ€μž…μ΄ 올 수 있음. 읽기 μ „μš©, null만 μ‚½μž… κ°€λŠ₯

    <? extends T> (μƒν•œ μ œν•œ)

    • Upper Bounded Wildcards (μƒμœ„ 클래슀 μ œν•œ). 읽기 μ „μš©, μƒμœ„ νƒ€μž…μ΄λ‚˜ μƒμœ„ νƒ€μž…μ˜ ν•˜μœ„ νƒ€μž…λ§Œ 올 수 있음.
    • GET은 μƒμœ„νƒ€μž…μœΌλ‘œ, SET은 λΆˆκ°€ -> GET μ•ˆμ „

    <? super T> (ν•˜ν•œ μ œν•œ)

    • Lower Bounded Wildcards (ν•˜μœ„ 클래슀 μ œν•œ). ν•˜μœ„ νƒ€μž…μ΄λ‚˜ ν•˜μœ„ νƒ€μž…μ˜ μƒμœ„ νƒ€μž…λ§Œ 올 수 있음.
    • GET은 μ΅œμƒμœ„ νƒ€μž…μœΌλ‘œ. SET은 U와 U의 μžμ†νƒ€μž…μœΌλ‘œ. -> μ“°κΈ° κ°€λŠ₯, SET μ•ˆμ „

    μ‚¬μš©μ§€μ  λ³€μ„± (use-site variance)

    • νƒ€μž… λ§€κ°œλ³€μˆ˜ 지점에 λ³€μ„± μ •ν•˜λŠ” μžλ°”μ˜ 방식
    • Kotlin, ScalaλŠ” 선언지점 λ³€μ„±(declaration-site variance)을 μ‚¬μš©
    List<out T>
    List<in T>

    PECS

    Producer Extends, Consumer Super

    • μ™€μΌλ“œ νƒ€μž…μ˜ 객체λ₯Ό 생성, λ§Œλ“€ λ•ŒλŠ” extends(GET), μ‚¬μš©, μ†ŒλΉ„ μ‹œ super μ‚¬μš©(SET)
      • extends, super μ‚¬μš© μ‹œκΈ° κ·œμ •
      • μ˜€λΌν΄μ—μ„œλŠ” in, out κ°œλ…μœΌλ‘œ μ„€λͺ…. extends -> in, super -> out
    • μ œλ„€λ¦­ νƒ€μž… μ œν•œ

    Producer β€” extends (읽기 μ „μš©)

    void printAll(List<? extends Number> list) {
        for (Number n : list) System.out.println(n);
    }

    Consumer β€” super (μ“°κΈ° κ°€λŠ₯)

    void addNumbers(List<? super Integer> list) {
        list.add(10);
        list.add(20);
    }

    μ°Έκ³  λ‚΄μš©

    • μ™€μΌλ“œμΉ΄λ“œλŠ” 섀계가 μ•„λ‹Œ μ‚¬μš©μ„ μœ„ν•œ 것

      • ν΄λž˜μŠ€λ‚˜ μΈν„°νŽ˜μ΄μŠ€ μ œλ„€λ¦­ 섀계 μ‹œ μ‚¬μš©λΆˆκ°€
      • 이미 λ§Œλ“€μ–΄μ§„ μ œλ„€λ¦­ ν΄λž˜μŠ€λ‚˜ λ©”μ„œλ“œ μ‚¬μš© μ‹œ 이용
    • T extends νƒ€μž…, ? extends U 차이점

      • μ œλ„€λ¦­ 클래슀 섀계 <-> 이미 λ§Œλ“€μ–΄μ§„ μž¬λ„€λ¦­ 클래슀 μΈμŠ€ν„΄μŠ€ν™” ν›„ μ‚¬μš© μ‹œ νƒ€μž…νŒŒλΌλ―Έν„° λ„˜κ²¨μ€„ λ•Œ μž‘μ„±
    • T super νƒ€μž…: 무수히 λ§Žμ€ μžλ°”μ˜ ν΄λž˜μŠ€μ™€ μΈν„°νŽ˜μ΄μŠ€ 올 수 μžˆκΈ°λ•Œλ¬Έμ— μ‚¬μš© x

    • ? 와 object 차이: null 만 넣을 수 있음 <-> object ν•˜μœ„ νƒ€μž… λͺ¨λ‘ 넣을 수 있음
      | νƒ€μž… | λ„£κΈ°(set) | 읽기(get) | μ‚¬μš© λͺ©μ  |
      | -------------- | ----------- | --------------- | ---------------- |
      | List<?> | null만 κ°€λŠ₯ | Object νƒ€μž…μœΌλ‘œ GET | 읽기 μ „μš©, ν™•μž₯μ„± |
      | List<Object> | λͺ¨λ“  νƒ€μž… λ„£κΈ° κ°€λŠ₯ | Object둜 GET | μ‹€μ œ Object νƒ€μž… μ»¬λ ‰μ…˜ |


    πŸ”— μ°Έκ³ 

profile
이것저것이것 개발자

0개의 λŒ“κΈ€