JAVA

지네릭스

woohap 2025. 2. 6. 15:44

지네릭스

- 다양한 타입의 객체들을 다루는 메서드나 컬렉션 클래스에 컴파일 시 
  타입 체크를 해주는 기능
- 저장된 객체를 꺼낼 때, 형변환 필요 없음
  → 어떤 타입의 객체들이 저장되어있는지 알고 있기 때문
- 생성자의 지네릭 타입은 항상 일치해야 한다.

ArrayList<Tv> tvList = new ArrayList<>();

[장점]
- 타입 안정성 제공
- 형변환 생략 가능 → 코드 간결

타입 변수

- ArrayList<E> 여기서 E는 타입 변수
- 타입 변수가 여러 개인 경우 콤마로 구분 HashMap<String, Member>
- 타입변수는 실제 타입에 대응된다.  ArrayList<Tv>

public class MyList<T> {
    private T[] listData; // T는 임의의 타입 
}

제한된 지네릭 클래스

- 타입문자를 모든 클래스가 아닌 일부 클래스 타입으로 제한하는 방법
- < T extends E > 를 사용하는 것
  E 혹은 E의 자손인 경우만 가능

class FruitBox<T extends Fruit> { ... }

지네릭스 예약

- static 멤버는 지네릭스를 적용할 수 없다.
  → 모든 인스턴스에 대해 동일하게 동작해야 하기 때문
- 지네릭스 타입의 배열 생성 불가
  → 연산자는 컴파일 시점에 타입 T가 뭔지 정확히 알아야 하기 때문
- instanceof 연산자도 마찬가지

T[] itemArr = new T[itemArr.length]; // 지네릭 타입 배열 생성불가 

와일드 카드

- 지네릭 타입에 다형성을 적용하려면 [와일드 카드]를 사용하면 된다.

<? extends T> - 와일드 카드 상한 제한, T와 그 자식들만 가능
<? super T> - 와일드 카드 하한 제한, T와 그 부모들만 가능
<?> - 제한 없음, 모든 타입 가능 < ? extends Object > 와 동일

T extends Product VS ? extends Product

[T extends Product]
- 클래스나 메서드 정의 시 특정 타입을 고정하기 위해 사용
- 즉, 정의할 때, 타입 제한

class Box<T extends Product> {
    private T item;

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

[? extends Product]
- 메서드 호출 시 혹은 컬렉션 제네릭 타입을 지정할 때 사용
- 타입을 고정하지 않고, 특정 범위 내에서 유연하게 처리할 수 있도록 함
- 데이터를 읽기만 하는 범용적인 메서드에서 사용 됨

ArrayList<? extends Product> list = new ArrayList<>();

기타

- ArrayList<T extends Object>가 불가능한 이유
  → 컴파일 시점에 타입 T가 명확하지 않기 때문
- ArrayList<? extends Object> 가 가능한 이유 
  → 컴파일 시점에 범위가 명확하기 때문에 가능
- 와일드카드가 쓰기가 불가능한 이유
  → 컴파일 시점에 범위는 명확하지만 정확히 어떤 타입인지 명확하지 않기 때문

지네릭 메서드

- 메서드의 선언부에 지네릭 타입이 선언된 메서드를 지네릭 메서드라고 한다.
- 메서드 정의시 해당 메서드에서 사용할 타입의 범위를 지정하는 것

static <T> void method() { ... };

- 지네릭 클래스의 타입 변수와 지네릭 메서드의 타입변수가 같은 문자여도 서로 다른 것 

class Main<T> {
    static <T> void main() { };  // 두 타입 변수 T는 다름
}

public <U extends Parent> void test(List<? extends U> list) { ... };

지네릭 타입 형변환

- 지네릭 타입과 원시 타입 간 형변환 가능
  단, 경고 발생
- 서로 다른 지네릭 타입 간의 형반환 불가능

Hello<Integer> h1 = new Hello<>();
Hello<Object> h2 = h1; // 불가능 

- <? extends T>의 타입 참조변수로 형변환은 
  T 타입 혹은 그의 자손으로 제니릭 타입을 가지면 가능 
  → 컴파일 시점에 정확한 타입이 뭔지는 모르지만 범위가 정해져있기 때문

Hello<Integer> h1 = new Hello<>();
Hello<? extends Number> h2 = h1;

지네릭 타입 제거

- 컴파일러는 지네릭 타입을 이용하여 소스파일을 체크하고
  필요한 곳에 형변환을 넣어준다.
- 그리고 소스파일에소 지네릭 타입을 제거한다.
- 즉, 컴파일된 파일에는 지네릭 타입에 대한 정보가 없다.

'JAVA' 카테고리의 다른 글

JAVA 쓰레드 1  (0) 2025.02.11
JAVA 상속, 캡슐화, 인터페이스  (0) 2025.02.08
Object 클래스의 메서드  (0) 2024.12.04
객체지향 프로그래밍 1  (0) 2024.11.27
JAVA Collection Framework 3  (0) 2024.11.26