alt
Home 자바의정석 - 12장 Generics
Post
Cancel

자바의정석 - 12장 Generics


  1. 객체의 타입을 컴파일 시에 체크해 같은 타입이라는 것만 보장하고, 타입 안정성을 높인다.
  2. 타입 체크와 형변환을 생략가능하게 한다.


  • e.g) List에 타입을 명시하지 않으면 다양한 타입의 객체를 한 리스트에 담을 수 있는데, 이걸 하나로 제한한다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    class Box {
      Object item;
        
      void setItem(Object item) { this.item = item; }
      Object getItem() { return item; }
    }
      
    // -- Generics
      
    class Box<T> {
      T item;
        
      void setItem(T item) { this.item = item };
      T getItem() { return item; }
    }
    
  • 타입 <T>, ArrayList<E>, Map<K, V>다양한 타입 변수 네이밍을 사용한다.

  • 컴파일 타임에 체크한다. 위의 예시에서 Box<String> b = new Box<String>(); 라고 선언하면, 타입 T가 컴파일시에 String으로 변환된다.

  • static 멤버: 모든 객체에 대해 동일하게 동작해야한다. 따라서, static에는 지네릭스가 적용 불가능하다.

  • 가능하면 Raw 타입을 사용하지 않고 지네릭을 사용한다. List -> List<Object>

    • JDK 1.5부터 지네릭스가 도입되어, 지네릭스를 사용할 것을 권장하지만 일단은 하위 호환을 위해 raw 타입을 허용해 놓은 것.
    • raw 타입을 사용하면 타입 안정성을 잃는다.


용어

  • Box<T> : Generic 클래스 “T Box” or “T의 Box”
  • T: 타입 변수. String으로 선언하게 되면 “매개변수화 된 타입”이 된다.
  • Box: 원시 타입, 로(Raw) 타입


상속 관계의 Generics

1
2
3
4
5
6
7
8
9
10
11
12
13
Box<Fruit> appleBox = new Box<Apple>(); // 에러

Box<Apple> appleBox = new FruitBox<Apple>(); // OK. 다형성
List<String> list = new ArrayList<String>(); // OK. 다형성

// ----

void add(Apple apple) { } // 이와같은 메서드가 있을 때

Box<Apple> appleBox = new Box<Apple>();
appleBox.add(new Apple()); // OK.
appleBox.add(new Grape()); // 에러. Apple 객체(혹은 자식 객체)만 추가 가능
appleBox.add(new GreenApple()); // OK. 자식 객체라면 가능


extends: 제한된 Generic

  • 일반적인 지네릭스는 타입 안정성은 얻지만, 여전히 모든 타입이 가능하다. <T extends Fruit> 과 같은 선언을 통해 특정 타입의 (Fruit 타입의) 자손들까지만 대입할 수 있도록 제한할 수 있다.
    • interface라도 implements 키워드 대신에 extends를 사용한다.
    • 클래스 Fruit의 자손이면서 Eatable 인터페이스도 구현해야한다면 <T extends Fruit & Eatable>


와일드 카드

  • static 메서드에는 지네릭을 사용할 수 없는데, 그렇다고 여러 타입의 매개변수를 갖도록 재선언하는 것은 중복을 만든다. 따라서 아래와 같이 선언

    1
    2
    3
    
    static Applebox add(FruitBox<? extends Fruit> box) {
      // ..
    }
    
  • <? extends T>: T와 그 자손들만 가능

  • <? super T>: T와 그 조상들만 가능

  • <?>: 모든 타입 가능. == <? extends Object>


Generic 메서드

: 메서드 한정 지역적 Generic 선언으로 복잡도를 낮춘다.

  • 메서드 한정이므로 static 변수에도 사용 가능하다. 이펙티브 자바에서는 Generic 메소드가 사용이 가능한 부분이라면 무조건 이걸 사용하는 것을 권장한다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    static void printAll(ArrayList<? extends Product> list, ArrayList<? extends Product> list2) {
      // ..
    }
      
    // 위의 내용을 지네릭 메서드로 변환하면
      
    static <T extends Product> void printAll(ArrayList<T> list, ArrayList<T> list2) {
      // ..
    }
    



Reference)

자바의 정석 3판

This post is licensed under CC BY 4.0 by the author.

절차지향과 객체지향

커밋의 상태와 .git