[Java] 제네릭 (Generic) 1

22. 제네릭 (Generic)

☑️ 제네릭은 왜 필요할까?

문제점1. 코드의 중복

  • 다양한 타입을 담는 박스가 필요할 때, 각각의 타입별로 클래스를 새로 만들어야 한다.
  • Integer 타입을 보관하는 IntegerBox

      public class IntegerBox {
        
          private Integer value;
        
          public void set(Integer value) {
              this.value = value;
          }
        
          public Integer get() {
              return value;
          }
      }
    
  • String 타입을 보관하는 StringBox

      public class StringBox {
        
          private String value;
        
          public void set(String value) {
              this.value = value;
          }
        
          public String get() {
              return value;
          }
      }
    

문제점2. Object 타입의 박스에서 발생하는 타입 안전성 문제

  • 다형성을 활용하여 모든 타입의 부모인 Object로 박스를 만들어보자.

      public class ObjectBox {
        
          private Object value;
        
          public void set(Object object) {
              this.value = object;
          }
        
          public Object get() {
              return value;
          }
      }
    
  • Object 타입의 박스로 IntegerBoxStringBox를 만들면 코드의 중복을 제거하고, 기존 코드를 재사용할 수 있게 된다.

      public class BoxMain2 {
          public static void main(String[] args) {
              ObjectBox integerBox = new ObjectBox();
              integerBox.set(10);
              Integer integer = (Integer) integerBox.get();
        
              ObjectBox stringBox = new ObjectBox();
              stringBox.set("hello");
              String str = (String) stringBox.get();
          }
      }
    
  • 하지만, 잘못된 타입의 인수가 전달되어 문제가 발생할 수 있다.

      integerBox.set("문자100");
      Integer result = (Integer) integerBox.get();
    
    • 예외 발생
      Exception in thread "main" java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Integer (java.lang.String and java.lang.Integer are in module java.base of loader 'bootstrap')
    

☑️ Generic 적용

public class GenericBox<T> {

    private T value;

    public void set(T value) {
        this.value = value;
    }

    public T get() {
        return value;
    }
}
  • Generic 클래스 (타입 매개변수)를 선언해야 한다.
  • 클래스 내부에는 T 타입이 필요한 곳에 T value 처럼 작성하면 된다.
  • T라는 타입 매개변수에 String, Integer와 같은 타입 인자를 전달해서 사용할 타입을 결정한다.

예제

public class BoxMain3 {
    public static void main(String[] args) {
        GenericBox<Integer> integerBox = new GenericBox<>();
        integerBox.set(10);
       //integerBox.set("100"); // Integer 타입만 허용, 컴파일 오류
        Integer integer = integerBox.get(); // Integer 타입 반환(캐스팅 X)

        GenericBox<String> stringBox = new GenericBox<>();
        stringBox.set("hello");
        String str = stringBox.get();
    }
}
  • 생성 시점에 GenericBox<Integer> , GenericBox<String> 와 같이 <> 안에 타입 매개변수를 정의하면 된다.
  • Integer 타입으로 선언된 integerBoxInteger 숫자만 담을 수 있고, String 타입으로 선언된 stringBoxString 문자열만 담을 수 있다.
    • 타입 인자로 기본형(int, double ..)은 사용할 수 없기 때문에 래퍼 클래스(Integer, Double ..)을 사용해야 한다.
  • get() 메서드를 쓸 때도 타입 캐스팅 없이 각각의 타입으로 조회할 수 있다.

타입추론

GenericBox<Integer> integerBox = new GenericBox<Integer>(); // [1]
GenericBox<Integer> integerBox2 = new GenericBox<>(); // [2]
  • 보통의 경우 [1]과 같이 객체 생성 시 <Integer>를 보고 필요한 타입 정보를 얻는다.
  • 하지만, [2]와 같이 타입정보를 생략 하여도 자바는 스스로 타입 정보를 추론한다.
    • 읽을 수 있는 타입 정보가 주변에 있는 경우만 추론 가능하다.

Raw Type

GenericBox integerBox = new GenericBox();
integerBox.set(10);
Integer result = (Integer) integerBox.get();
  • <> 에 타입 인자를 지정하지 않고 사용한다면 GenericBox<Object> 으로 사용되는 것이다.
  • 제네릭이 없던 시절의 코드와 하위 호환이 필요해서 어쩔 수 없이 이런 Raw Type을 지원한다고 한다.




김영한의 실전 자바 중급 2편을 참고하였습니다

© 2021. All rights reserved.

yaejinkong의 블로그