자바

자바 제네릭

thebasics 2024. 8. 23. 17:00

목차
1. 제네릭이란 무엇인가?
2. 제네릭의 장점
3. 제네릭 사용 방법
   - 제네릭 클래스
   - 제네릭 메서드
   - 제네릭 인터페이스
4. 제네릭의 타입 제한 (Bounded Type)
5. 제네릭 타입 소거 (Type Erasure)
6. 제네릭과 컬렉션
7. 와일드카드 사용
8. 예제와 분석
9. 결론 및 추가 학습 자료


1. 제네릭이란 무엇인가?

제네릭(Generics)은 자바 5부터 도입된 강력한 기능으로, 클래스, 인터페이스, 메서드를 정의할 때 사용할 데이터 타입을 일반화하여 코드를 더욱 유연하고 타입 안전하게 작성할 수 있게 해줍니다. 제네릭을 사용하면 특정 데이터 타입에 의존하지 않고, 다양한 타입을 처리할 수 있는 일반화된 코드 작성이 가능해집니다. 즉, 코드 재사용성을 높이고, 컴파일 시 타입 체크를 통해 런타임 오류를 줄일 수 있습니다.


2. 제네릭의 장점

제네릭을 사용하면 다음과 같은 주요 장점을 얻을 수 있습니다:

- 타입 안정성: 제네릭은 컴파일 시점에 타입을 체크하므로, 잘못된 타입이 사용되는 것을 방지하여 런타임 오류를 줄일 수 있습니다.
- 코드 재사용성: 제네릭을 사용하면 동일한 코드로 여러 타입을 처리할 수 있어, 코드 중복을 줄이고 재사용성을 높일 수 있습니다.
- 명확한 코드: 제네릭을 사용하면 코드가 보다 명확해지며, 어떤 타입의 객체가 사용될지 미리 알 수 있습니다.


3. 제네릭 사용 방법

제네릭은 주로 클래스, 메서드, 인터페이스에서 사용되며, 다양한 데이터 타입을 처리할 수 있는 유연한 코드를 작성하는 데 사용됩니다.

제네릭 클래스

제네릭 클래스는 클래스 정의 시 데이터 타입을 일반화하여 다양한 타입을 처리할 수 있도록 합니다. 클래스 선언 시 '<T>'와 같은 형식을 사용하여 제네릭 타입을 정의합니다.

예제 코드:

public class Box<T> {
    private T item;

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

    public T getItem() {
        return item;
    }

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

        Box<Integer> intBox = new Box<>();
        intBox.setItem(123);
        System.out.println("Integer value: " + intBox.getItem());
    }
}

설명:
- 'Box<T>' 클래스는 제네릭 클래스로, 'T'는 타입 매개변수입니다.
- 'setItem'과 'getItem' 메서드는 'T' 타입을 사용하여 정의되었습니다.
- 'Box<String>'와 'Box<Integer>'를 사용하여 'String'과 'Integer' 타입의 데이터를 처리할 수 있습니다.

제네릭 메서드

제네릭 메서드는 메서드 선언 시에만 타입을 일반화할 수 있으며, 클래스나 인터페이스 전체가 아닌 특정 메서드에서만 제네릭 타입을 사용할 수 있습니다.

예제 코드:

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

    public static void main(String[] args) {
        Integer[] intArray = {1, 2, 3, 4, 5};
        String[] stringArray = {"A", "B", "C", "D"};

        printArray(intArray);
        printArray(stringArray);
    }
}

설명:
- 'printArray' 메서드는 제네릭 메서드로, 'T' 타입 매개변수를 사용합니다.
- 'Integer[]'와 'String[]' 타입의 배열을 처리할 수 있습니다.

제네릭 인터페이스

제네릭 인터페이스는 인터페이스 선언 시에 타입을 일반화할 수 있습니다. 이를 통해 다양한 데이터 타입을 처리할 수 있는 인터페이스를 정의할 수 있습니다.

예제 코드:

interface Pair<K, V> {
    K getKey();
    V getValue();
}

class OrderedPair<K, V> implements Pair<K, V> {
    private K key;
    private V value;

    public OrderedPair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public K getKey() {
        return key;
    }

    public V getValue() {
        return value;
    }

    public static void main(String[] args) {
        Pair<String, Integer> pair = new OrderedPair<>("One", 1);
        System.out.println("Key: " + pair.getKey() + ", Value: " + pair.getValue());
    }
}

설명:
- 'Pair<K, V>' 인터페이스는 제네릭 인터페이스로, 키와 값을 일반화하여 다양한 타입을 처리할 수 있습니다.
- 'OrderedPair<K, V>' 클래스는 'Pair' 인터페이스를 구현하며, 제네릭 타입으로 'String'과 'Integer'를 사용했습니다.


4. 제네릭의 타입 제한 (Bounded Type)

제네릭 타입 매개변수에 특정한 타입을 제한할 수 있습니다. 이를 통해 특정 타입 이상의 데이터만 처리할 수 있도록 할 수 있습니다. 예를 들어, 'Number' 클래스나 그 하위 클래스만 사용할 수 있도록 제한할 수 있습니다.

예제 코드:

public class BoundedTypeExample {

    public static <T extends Number> void printDoubleValue(T value) {
        System.out.println("Double value: " + value.doubleValue());
    }

    public static void main(String[] args) {
        printDoubleValue(5); // Integer
        printDoubleValue(5.5); // Double
        // printDoubleValue("Test"); // 컴파일 오류
    }
}

설명:
- 'T extends Number' 구문을 통해 'T' 타입 매개변수가 'Number' 클래스의 하위 클래스만 받을 수 있도록 제한했습니다.
- 'printDoubleValue' 메서드는 'Number' 클래스와 그 하위 클래스의 객체를 받아, 'doubleValue()' 메서드를 호출합니다.


5. 제네릭 타입 소거 (Type Erasure)

제네릭은 컴파일 시에 타입 검사를 수행하고, 런타임 시에는 모든 제네릭 타입 정보가 제거되는 "타입 소거(Type Erasure)"를 거칩니다. 즉, 런타임에서는 제네릭 타입이 아닌 구체적인 타입으로 변환되어 동작합니다. 이는 제네릭의 타입 안정성을 보장하면서도 기존의 자바 코드와의 호환성을 유지하기 위한 것입니다.

예제 코드:

public class ErasureExample {
    public static void main(String[] args) {
        Box<Integer> intBox = new Box<>();
        Box<String> strBox = new Box<>();

        System.out.println(intBox.getClass() == strBox.getClass()); // true
    }
}

설명:
- 'intBox'와 'strBox'는 서로 다른 제네릭 타입을 사용하지만, 런타임 시에는 동일한 'Box' 클래스 타입으로 처리됩니다.
- 'getClass()' 메서드를 사용해 두 객체의 클래스 타입을 비교하면, 동일한 클래스로 인식됩니다.


6. 제네릭과 컬렉션

제네릭은 자바의 컬렉션 프레임워크에서 매우 중요한 역할을 합니다. 제네릭을 사용함으로써, 컬렉션이 특정 타입의 객체만을 포함하도록 제한할 수 있습니다. 예를 들어, 'ArrayList<String>'은 문자열만을 저장하는 리스트를 의미합니다.

예제 코드:

import java.util.*;

public class GenericCollectionExample {
    public static void main(String[] args) {
        List<String> stringList = new ArrayList<>();
        stringList.add("Hello");
        stringList.add("World");

        for (String str : stringList) {
            System.out.println(str);
        }
    }
}

설명:
- 'List<String>'는 문자열만을 저장하는 리스트를 나타내며, 다른 타입의 객체를 저장하려고 하면 컴파일 오류가 발생합니다.
- 제네릭을 사용하여 타입 안정성을 보장할 수 있습니다.


7. 와일드카드 사용

제네릭의 와일드카드는 '?' 기호를 사용하여 불특정 다수의 제네릭 타입을 나타냅니다. 와일드카드는 제네릭 타입 매개변수의 불확실성을 처리할 때 유용합니다.

와일드카드의 종류:
- 제한 없는 와일드카드: 'List<?>'는 어떠한 타입의 리스트도 받을 수 있습니다.
- 상한 경계 와일드카드: 'List<? extends Number>'는 'Number' 클래스와 그

 하위 클래스만 받을 수 있습니다.
- 하한 경계 와일드카드: 'List<? super Integer>'는 'Integer' 클래스와 그 상위 클래스만 받을 수 있습니다.

예제 코드:

import java.util.*;

public class WildcardExample {
    public static void printList(List<?> list) {
        for (Object elem : list) {
            System.out.print(elem + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5);
        List<String> strList = Arrays.asList("A", "B", "C");

        printList(intList);
        printList(strList);
    }
}

설명:
- 'printList' 메서드는 와일드카드를 사용하여 어떠한 타입의 리스트도 처리할 수 있습니다.
- 상한 또는 하한 경계 와일드카드를 사용하여 특정 범위의 타입만을 허용할 수도 있습니다.


8. 예제와 분석

지금까지 배운 제네릭 개념들을 종합적으로 적용한 예제를 살펴보겠습니다.

종합 예제:

import java.util.*;

class Pair<K, V> {
    private K key;
    private V value;

    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public K getKey() { return key; }
    public V getValue() { return value; }
}

public class GenericExample {
    public static void main(String[] args) {
        Pair<String, Integer> pair1 = new Pair<>("One", 1);
        Pair<Double, String> pair2 = new Pair<>(2.5, "Two point five");

        printPair(pair1);
        printPair(pair2);

        List<Pair<String, Integer>> list = new ArrayList<>();
        list.add(new Pair<>("Three", 3));
        list.add(new Pair<>("Four", 4));

        printPairList(list);
    }

    public static <K, V> void printPair(Pair<K, V> pair) {
        System.out.println("Key: " + pair.getKey() + ", Value: " + pair.getValue());
    }

    public static void printPairList(List<Pair<String, Integer>> list) {
        for (Pair<String, Integer> pair : list) {
            System.out.println("List Pair - Key: " + pair.getKey() + ", Value: " + pair.getValue());
        }
    }
}

코드 분석:
- 'Pair<K, V>' 클래스는 키-값 쌍을 저장하는 제네릭 클래스로, 다양한 타입의 데이터를 처리할 수 있습니다.
- 'printPair' 메서드는 제네릭 메서드로, 'Pair<K, V>' 객체를 출력합니다.
- 'printPairList' 메서드는 'Pair<String, Integer>' 객체의 리스트를 출력합니다.
- 이 예제에서는 제네릭 클래스, 제네릭 메서드, 와일드카드를 사용하여 유연한 코드를 작성하는 방법을 보여줍니다.


9. 결론 및 추가 학습 자료

이번 글에서는 자바의 제네릭에 대해 살펴보았습니다. 제네릭은 자바 프로그래밍에서 타입 안전성을 보장하고, 코드 재사용성을 높이며, 더욱 명확하고 유지보수하기 쉬운 코드를 작성할 수 있게 해줍니다. 제네릭 클래스, 메서드, 인터페이스, 와일드카드 등 다양한 제네릭 기능을 이해하고 적절하게 활용하면, 더욱 견고한 자바 애플리케이션을 개발할 수 있습니다.

추가 학습 자료:
- 자바 공식 문서: [Oracle Java Documentation - Generics](https://docs.oracle.com/javase/tutorial/java/generics/index.html)
- 온라인 자바 튜토리얼: [W3Schools Java Generics](https://www.w3schools.com/java/java_generics.asp)
- 자바 코딩 연습 사이트: [GeeksforGeeks - Generics in Java](https://www.geeksforgeeks.org/generics-in-java/)

제네릭을 잘 이해하고 활용하면, 다양한 데이터 타입을 처리할 수 있는 유연한 코드를 작성할 수 있으며, 컴파일 시점에 타입 오류를 미리 방지할 수 있습니다. 제네릭은 자바의 중요한 개념 중 하나로, 지속적인 학습과 실습을 통해 더욱 깊이 이해하고 활용할 수 있을 것입니다.


이제 자바의 제네릭에 대해 자세히 이해하게 되었습니다. 다음 글에서는 자바의 고급 기능에 대해 다루도록 하겠습니다. 자바의 더 깊은 이해를 위해 계속해서 학습해나가세요!

반응형

'자바' 카테고리의 다른 글

자바 스트림 API  (0) 2024.08.25
자바 람다 표현식  (0) 2024.08.24
자바 컬렉션 프레임워크  (0) 2024.08.22
자바 예외 처리  (0) 2024.08.21
자바 인터페이스  (0) 2024.08.20