Java - Generic

각 관련 개념의 예제는 다음의 경로에서 확인이 가능하다 관련 예제 패키지

  • 개요

    • Java 5 부터 추가

    • 클래스, 인터페이스, 메소드를 정의시 Type 을 파라미터로 사용할 수 있도록한다.

  • 장점

    • 컴파일 시 강한 타입 체크를 할 수 있다.

      잘못된 타입이 사용될 수 있는 문제를 컴파일 과정에서 제거할 수 있다.

    • Casting 과정을 제거한다.

      제네릭을 활용하지 않고 사용하는 경우, 객체를 읽고 쓰는 과정에서 캐스팅 과정이 필요로 한다.`

      List list = new ArrayList(); 
      list.add("hello"); 
      String str = (String) List.get(0);
      
      List list = new ArrayList;
      list.add("hello"); 
      String str = list.get(0);
  • 제네릭 타입

    • class, interface 과 같은 타입(T)을 파라미터로 가지는 클래스와 인터페이스를 의미한다.

    • "타입 파라미터는 변수명과 동일한 규칙에 따라서 작성이 가능하나 일반적으로 대문자 하나만 쓴다" 라고 하길래 무슨 소릴 하는건지 보아하니 다음을 의미 하는 것 같다.

      public class 클래스명<T>
      public interface 인터페이스명<T>
      
      public class 클래스명<AnyThingVariableNaming>           ----> 변수명 쓰듯 쓰는게 가능
      public interface 인터페이스명<AnyThingVariableNaming>    ----> 마찬가지

      "<>" 안에는 자바에서 변수명 규칙과 동일하게 정할 수 있다. 하지만 통상 대문자 한 글자로 정하는 편이다. 위와같은 이유는 타입 파라미터를 일반 클래스 및 인터페이스와 구분하기 어렵기 떄문이다. 그리고 통상 타입 파라미터의 정의로 쓰는 것은 다음과 같다.

      E - Element (used extensively by the Java Collections Framework)
      K - Key
      N - Number
      T - Type
      V - Value
      S,U,V etc. - 2nd, 3rd, 4th types
    • 제네릭 타입을 쓰는 이유는 제네릭 타입을 쓰지 않고 모든 타입을 저장하려는 경우 Object 를 사용할 수 있으나 이럴 경우 Casting 과정을 겪기 때문이다. 성능상 좋지 못한 결과가 나오기 때문에 제네릭 타입은 좋은 해결책이 되어준다.

      public class Box<T> {
          private T t;
      
          public T get() {
              return t;
          }
      
          public void set(T t) {
              this.t = t;
          }
      }
    • 위와 같이 클래스를 제네릭 타입으로 구성 후 자신이 사용하고자 하는 클래스를 제네릭 타입으로 지정하여 사용하면 내부적으로 자동으로 재구성 된다고 한다.

      Box<String> box = new Box<String>;
      
      public class Box<String> {
          private String t;
      
          public String get() {
              return t;
          }
      
          public void set(String t) {
              this.t = t;
          }
      }
    • 결론적으로, 실제 클래스가 사용될 때 구체적인 타입을 지정하여 타입변환을 최소화 하기 위한 목적으로 활용한다.

  • 원천 타입 (Raw Type)

    • 제네릭 타입에는 원천 타입(Raw Type) 이 존재한다.

      List<String> list; ----> 일반적인 제네릭타입 형식의 컬렉션
      List list2          ----> Raw Type
    • 위와 같을 경우 list2 같은 경우 문제가 발생한다. 그에 대한 예제는 다음과 같다.

      public class RawType<T> {
        public List<String> getStrs() {
            return Arrays.asList("str");
        }
        public static void main(String[] args) {
            RawType t = new RawType();          ---->> Raw Type
            for (String str : t.getStrs()) {    ---->> compile error
                System.out.println(str);
            }
        }
      }
    • 컴파일 에러내용은 incompatible types: Object cannot be converted to String 이고,

      getStr 은 String Type Prameter를 가지는 List 을 반환하는데 어째서 에러가 나는 것인가?

    • Raw Type 으로 선언되면, 해당 클래스에 정의된 모든 생성자, 인스턴스 메서드, 인스턴스 필드의 타입 파라미터를 인식하지 않기 때문이다.

    • 결론적으로 이야기해서 제네릭스 타입을 가지는 클래스를 활용시 제네릭스 타입을 빼먹지 말자.

  • 멀티 타입 파라미터

    • 제네릭 타입은 여러 개로 구성 될 수 있다.

  • 제네릭 메소드

    • 제네릭 메소드는 파라미터 타입, 리턴 타입으로 타입 파라미터를 갖는 메소드를 의미한다

      public <타입파라미터,...> 리턴타입 메소드명(매개변수,...) {...}
      public <T> Box<T> boxing(T t) {...}
    • 두가지 방식으로 호출할 수 있다. 코드에서 타입 파라미터의 구체적인 타입을 명시적으로 지정하거나 컴파일러가 매개값의 타입을 보고 구체적인 타입을 추정하도록 하는 방법이 있다.

      리턴타입 변수 = <구체적인 타입> 메소드명(매개값);
      리턴타입 변수 = 메소드명(매개값);
      
      Box<Integer> box = <Integer>boxing(100);
      Box<Integer> box = boxing(100);
    • 결론적으로 리턴 타입 앞에오는 제네릭스로 표현된 타입 파라미터를 매개변수에도 동일하게 표현하여 사용하면 된다.

  • 제한된 타입 파라미터

    • 타입 파라미터에 지정되는 구체적인 타입을 제한해야 하는 경우 사용

    • 예를들어 숫자를 연산하는 제네릭 메소드는 매개값으로 숫자와 관련된 인스턴스만 소유해야한다.

      public <T extends Number> int compare(T t1, T t2) {...}
  • 와일드카드 타입

    • "?" : 모든 클래스와 인터페이스 타입을 소유할 수 있다.

    • "<? extends 상위타입>" : 상위타입 제한방식으로 상위타입 자신과 상위타입을 상속받는 클래스를 소유하게 한다.

    • "<? super 하위타입>" : 하위타입 제한방식으로 하위타입과 하위타입의 상위타입을 소유하게한다.

    • 말이 참 어렵자만 결론적으로 extends,super 뒤에 지정되는 타입을 기준으로 하여 부모로 제한할지 자식으로 제한할지 정하는 것으로 이해하면 될것 같다.

  • 제네릭 타입의 상속과 구현

    • 제네릭 타입도 다른 타입의 부모 클래스가 될 수 있다.

      public class Product<T> {...}   
      public class ChildProduct<T,M> extends Project<T> {...}
      public class EtcProduct<T,M,C> extends ChildProduct<T,M> {...}
    • 이렇게 늘려갈 수도 있겠지만, 애초에 설계시 이런 식으로 확장해 나가는 방식을 고수하진 않을 것 같다.

  • 타입 파라미터와 제네릭 와일드 카드의 차이점은 무엇인가?

    • 와일드 카드는 제네릭의 제한을 상위와 하위 타입으로 제한할 수 있는 반면 오직 상위 타입으로만 제한할 수 있다. 이 말은 곧 타입 파라미터는 super 키워드를 활용하여 제한할 수 없다.

    • 타입 파라미터는 Multiple Bounding 이 가능하지만 와일드 카드는 불가하다.

Last updated