JAVA

자바의 제네릭(Generic)

ohyujeong 2024. 4. 8. 09:53

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

구현 시에 클래스 내부에서 타입을 지정하는 것이 아니라, 일반화 해두고 선언시에 개발자가 타입을 지정하여 사용하는 것.

public class Box<T> {
    private T content;

    public void setContent(T content) {
        this.content = content;
    }

    public T getContent() {
        return content;
    }

    public static void main(String[] args) {
        // Integer를 위한 Box 생성
        Box<Integer> integerBox = new Box<>();
        integerBox.setContent(10);
        int intValue = integerBox.getContent();
        System.out.println("정수 값: " + intValue);

        // String을 위한 Box 생성
        Box<String> stringBox = new Box<>();
        stringBox.setContent("안녕, 제네릭!");
        String stringValue = stringBox.getContent();
        System.out.println("문자열 값: " + stringValue);
    }
}

 

Box<T> 의 'T' 처럼 일반화된 타입으로 정의함

구현 시에, 명시적으로 구체적인 타입(Integer, String)을 지정함으로써 다용도로 사용할 수가 있게된다.

 

2. 왜 제네릭을 사용하고 이점이 무엇인가?

  • 하나의 코드 ex) Box <T> 클래스처럼 클래스, 인터페이스, 메소드를 정의할 때 타입 매개변수를 사용하고 이를 구현시에 Integer, String 등 다양한 타입의 객체들을 처리할 수 있게 된다 -> 코드의 재사용성과 유연성이 높아짐
  • 컴파일 시에 타입 검사를 해서, 런타임에 타입으로 인해 오류가 발생하는 것을 방지함

3. Collection과 Generic

컬렉션에서는 type에 관계 없이 제공해야 하는 함수들이 있다. add, get... 등

이를 제네릭이라는 개념을 통해 해결하는 것이다.

 

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
        
public void add(E e) {...}
public E get(int index) {...}
    
   
}

 

  • ArrayList<E> 
    • ArrayList를 생성할 때 'E' 에 대한 실제 타입을 지정함
  • add(E e)
    • E는 매개변수로 전달된 객체의 유형
  • E get(int index)
    • E는 해당요소의 반환 유형
import java.util.ArrayList;

public class GenericExample {
    public static void main(String[] args) {
        // String 유형을 저장하는 ArrayList 생성
        ArrayList<String> stringList = new ArrayList<>();

        // add 메소드를 사용하여 요소 추가
        stringList.add("Java");
        stringList.add("Python");
        stringList.add("C++");

        // get 메소드를 사용하여 요소 가져오기
        String language1 = stringList.get(0);
        String language2 = stringList.get(1);
        String language3 = stringList.get(2);

        // 가져온 요소 출력
        System.out.println("첫 번째 언어: " + language1);
        System.out.println("두 번째 언어: " + language2);
        System.out.println("세 번째 언어: " + language3);
    }
}

 

-> 제네릭 덕분에 같은 기능을 수행하는 코드를 타입에 따라 중복으로 구현하지 않아도 됨 

 

4. 와일드 카드 (?)

import java.util.ArrayList;
import java.util.List;

public class WildcardBoundsExample {
    // 메서드 정의: 숫자 요소의 총합 계산 (상한 wildcard 사용)
    public static double sumOfNumbersUpperBound(List<? extends Number> numbers) {
        double sum = 0;
        for (Number number : numbers) {
            sum += number.doubleValue();
        }
        return sum;
    }

    public static void main(String[] args) {
        // Double 리스트 생성
        List<Double> doubleList = new ArrayList<>();
        doubleList.add(1.5);
        doubleList.add(2.5);
        doubleList.add(3.5);

        // 상한 wildcard를 사용하여 숫자 요소의 총합 계산
        System.out.println("Double 리스트의 숫자 요소의 총합: " + sumOfNumbersUpperBound(doubleList));

    }
}

 

  • List<?>
    • 모든 유형의 리스트 처리
  • (상한 )List<? extends Number>
    • Number 또는 Number 상속 받은 하위 클래스의 모든 숫자 유형 처리
    • 반드시 Number 클래스와 관련되어 있는 상속한 클래스가 넘어와야 함

 

와일드 카드를 이용해 제네릭 타입의 경계를 정할 수 있다. ->  특정한 범위를 지정해서 더 정확하고 안전한 타입 변환을 한다.

 

 

'JAVA' 카테고리의 다른 글

[이펙티브 자바] 객체 생성과 파괴  (0) 2024.07.13
싱글톤 패턴과 프록시 패턴  (1) 2024.05.01
IoC, DI, AOP 와 Spring  (1) 2024.05.01
자바의 신 인사이트 정리  (0) 2024.03.21