[JAVA] Generic

java에서 Generic이 무엇인지 알아보자.


Generic이란?

  • 클래스나 메서드에서 사용할 데이터 타입을 지정하지 않고, 나중에 사용할 때 타입을 결정하는 기능
    • 하나의 값이 여러 다른 데이터 타입들을 가질 수 있도록 하는 방법
    • 클래스 내부에서 지정하는 것이 아닌, 외부에서 사용자에 의해 지정되는 것을 의미
      • specific 타입을 미리 지정해주는 것이 아닌 필요에 의해 지정할 수 있도록 하는 generic 타입
  • 제네릭을 사용하면 코드의 재사용성을 높이고, 타입 안정성을 보장할 수 있음
  • ArrayList<T>, HashMap<K, V>같은 자바 컬렉션 프레임워크에서 많이 사용됨

Generic의 장점

  1. 타입 안정성 (Type Safety) 보장
    • 제네릭을 사용하면 컴파일 시점에 타입을 검사해서 타입 오류 방지 가능
    • 예를 들어, ArrayList에 String만 저장하도록 지정하면 다른 타입을 추가하는 실수를 막을 수 있음
  2. 형변환(Casting) 불필요
    • 제네릭을 사용하면 클래스 외부에서 타입을 지정해주기 때문에 따로 타입을 체크하고 변환할 필요 없음
    • 관리하기 편함
  3. 코드의 재사용성 증가
    • 같은 로직을 다양한 데이터 타입에 대해 사용할 수 있어 코드의 중복을 줄일 수 있음

Generic 사용법

  •   import java.util.ArrayList;
    
      public class WithGenerics {
          public static void main(String[] args) {
              ArrayList<String> list = new ArrayList<>(); //제네릭 사용
              //ArrayList list = new ArrayList(); 제네릭 미사용
    
              list.add("Hello");
              //list.add(123);  오류 발생 (타입 안정성 보장)
              //제네릭을 사용하지 않으면 오류 발생 X -> 문제 발생 가능
    
              String str = list.get(0); //형변환 없이 바로 사용 가능
              System.out.println(str);
          }
      }
    
  • 제네릭 클래스 만들기

    •   // 제네릭 클래스 선언 (T는 타입 매개변수)
        class Box<T> {
            private T value;
      
            public void setValue(T value) {
                this.value = value;
            }
      
            public T getValue() {
                return value;
            }
        }
      
        public class GenericExample {
            public static void main(String[] args) {
                Box<String> stringBox = new Box<>();
                //String으로 구체적인 타입 지정
      
                stringBox.setValue("Hello");
                System.out.println(stringBox.getValue());
      
                Box<Integer> intBox = new Box<>();
                intBox.setValue(100);
                System.out.println(intBox.getValue());
            }
        }
      
  • 제네릭 메서드 만들기

    •   class Util {
            // 제네릭 메서드 선언
            public static <T> void printData(T data) {
                System.out.println(data);
            }
        }
      
        public class GenericMethodExample {
            public static void main(String[] args) {
                Util.printData("Hello"); // String 타입
                Util.printData(123); // Integer 타입
                Util.printData(3.14); // Double 타입
            }
        }
      
  • 제네릭 타입 제한 (Bounded Type Parameter)

    •   // Number를 상속받은 타입만 사용 가능 (Integer, Double 등)
        class NumberBox<T extends Number> {
            private T number;
      
            public void setNumber(T number) {
                this.number = number;
            }
      
            public T getNumber() {
                return number;
            }
        }
      
        public class BoundedGenericExample {
            public static void main(String[] args) {
                NumberBox<Integer> intBox = new NumberBox<>();
                intBox.setNumber(100);
                System.out.println(intBox.getNumber());
      
                NumberBox<Double> doubleBox = new NumberBox<>();
                doubleBox.setNumber(3.14);
                System.out.println(doubleBox.getNumber());
      
                // NumberBox<String> strBox = new NumberBox<>(); // 오류 발생 (String은 Number가 아님)
            }
        }
      
  • 와일드카드 (? - 제네릭 타입 미지정)

    • 특정 타입을 제한하지 않고 유연하게 사용하고 싶은 경우
    •   import java.util.ArrayList;
        import java.util.List;
      
        class WildcardExample {
            public static void printList(List<?> list) { // 와일드카드 사용
                for (Object obj : list) {
                    System.out.println(obj);
                }
            }
      
            public static void main(String[] args) {
                List<String> stringList = new ArrayList<>();
                stringList.add("Apple");
                stringList.add("Banana");
      
                List<Integer> intList = new ArrayList<>();
                intList.add(1);
                intList.add(2);
      
                printList(stringList); // 가능
                printList(intList); // 가능
            }
        }