[JAVA] Generic

handa·2024년 12월 10일
0
post-thumbnail

제네릭(Generic)은 Java에서 타입을 일반화하여 코드의 재사용성과 안정성을 높이는 기능입니다. 제네릭을 사용하면 컴파일 시점에 타입을 명확히 지정할 수 있어, 타입 안정성을 보장하고 형 변환(casting)의 필요성을 줄일 수 있습니다.


제네릭의 주요 특징

  1. 타입 안정성(Type Safety)
    • 제네릭을 사용하면 컴파일러가 타입을 체크하므로, 실행 중 발생할 수 있는 타입 관련 예외를 방지할 수 있습니다.
  2. 코드 재사용성 증가
    • 다양한 타입에 대해 동일한 코드를 사용할 수 있어 코드 중복을 줄이고 가독성을 높입니다.
  3. 형 변환 불필요
    • 제네릭을 사용하면 명시적인 형 변환이 필요하지 않습니다. 컴파일러가 자동으로 처리합니다.

제네릭 사용 예제

1. 제네릭 없는 코드

import java.util.ArrayList;

public class WithoutGenerics {
    public static void main(String[] args) {
        ArrayList list = new ArrayList(); // 제네릭 사용 안 함
        list.add("Hello");
        list.add(10); // 다른 타입도 추가 가능

        for (Object obj : list) {
            String str = (String) obj; // 명시적 형 변환 필요
            System.out.println(str);  // 실행 중 오류 가능
        }
    }
}
  • 단점
    • 타입 안정성이 보장되지 않아 실행 중 ClassCastException 발생 가능.

2. 제네릭을 사용한 코드

import java.util.ArrayList;

public class WithGenerics {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>(); // 제네릭 사용
        list.add("Hello");
        // list.add(10); // 컴파일 오류 발생: Integer는 String 타입에 맞지 않음

        for (String str : list) {
            System.out.println(str); // 형 변환 불필요
        }
    }
}
  • 장점
    • 컴파일 시점에 타입 체크가 이루어져 타입 오류 방지.
    • 명시적인 형 변환이 필요 없습니다.

제네릭 클래스

제네릭 클래스를 사용하면 클래스의 필드나 메서드에서 사용하는 타입을 동적으로 설정할 수 있습니다.

public class Box<T> { // T는 타입 파라미터
    private T item;

    public void setItem(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }
}

public class Main {
    public static void main(String[] args) {
        Box<String> stringBox = new Box<>();
        stringBox.setItem("Hello");
        System.out.println(stringBox.getItem());

        Box<Integer> intBox = new Box<>();
        intBox.setItem(123);
        System.out.println(intBox.getItem());
    }
}
  • <T>
    • 클래스 정의 시에만 존재하며, 호출할 때 실질적인 타입으로 대체됩니다.
    • Box<String> : TString으로 대체됨.
    • Box<Integer> : TInteger로 대체됨.

제네릭 메서드

제네릭 메서드는 메서드 수준에서 타입 파라미터를 사용할 수 있습니다.

public class GenericMethodExample {
    public static <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.println(element);
        }
    }

    public static void main(String[] args) {
        String[] strings = {"A", "B", "C"};
        Integer[] integers = {1, 2, 3};

        printArray(strings);  // T는 String으로 대체
        printArray(integers); // T는 Integer로 대체
    }
}

와일드카드(Generic Wildcards)

와일드카드는 제네릭 타입의 경계를 지정하여 보다 유연한 코드를 작성할 수 있게 합니다.

  1. ? (Unbounded Wildcard)
    • 어떤 타입이든 허용.
      public void printList(List<?> list) {
        for (Object obj : list) {
            System.out.println(obj);
        }
      }
  2. <? extends Type> (Upper Bound Wildcard)
    • 특정 타입이나 하위 타입만 허용.
    public void processNumbers(List<? extends Number> list) {
     for (Number num : list) {
         System.out.println(num.doubleValue());
     }
    }
  3. <? super Type> (Lower Bound Wildcard)
    • 특정 타입이나 상위 타입만 허용.
    public void addNumbers(List<? super Integer> list) {
     list.add(1);
     list.add(2);
    }

제네릭 제한 (Bounded Type Parameters)

제네릭 타입에 제한을 두어 특정 클래스나 인터페이스를 상속받은 타입만 사용하도록 설정할 수 있습니다.

public class BoundedTypeExample<T extends Number> {
    private T value;

    public BoundedTypeExample(T value) {
        this.value = value;
    }

    public double square() {
        return value.doubleValue() * value.doubleValue();
    }

    public static void main(String[] args) {
        BoundedTypeExample<Integer> intBox = new BoundedTypeExample<>(5);
        System.out.println(intBox.square());

        BoundedTypeExample<Double> doubleBox = new BoundedTypeExample<>(3.5);
        System.out.println(doubleBox.square());

        // BoundedTypeExample<String> strBox = new BoundedTypeExample<>("Hello"); // 컴파일 오류
    }
}

제네릭의 한계

  1. 프리미티브 타입 사용 불가
    • 제네릭은 int, double 같은 기본 타입을 지원하지 않습니다.
    • 대신 래퍼 클래스(Integer, Double) 를 사용해야 합니다.
  2. **런타임 타입 정보 소실 (Type Erasure)
    • 컴파일 후 제네릭 타입 정보는 제거됩니다.
    • 제네릭은 런타임이 아니라 컴파일 타임에만 동작합니다.

요약

  • 제네릭은 타입 안정성, 코드 재사용성, 가독성을 높이는 Java의 중요한 기능입니다.
  • 제네릭 클래스와 메서드는 동적으로 타입을 설정하며, 와일드카드와 타입 제한으로 유연한 코드 작성이 가능합니다.
  • 하지만 런타임 타입 정보 소실(Type Erasure)과 프리미티브 타입 미지원 같은 한계도 있습니다.

profile
진짜 해보자

0개의 댓글