제네릭, 와일드카드
2024. 2. 11. 22:58ㆍBackend 취업준비/Java
- 제네릭 ⇒ 클래스, 인터페이스, 그리고 메소드를 정의할 때 타입을 파라미터로 사용할 수 있도록 한다
- 타입 체크를 통하여 런타임 시 발생할 수 있는 에러를 컴파일 타임에 알 수 있도록 한다.
- 불필요한 타입 변환을 막아 성능을 향상 한다.
그럼 그냥 일반 클래스로 하고 필드에 타입을 주면 되는 거 아닌가?
- 위와 같이 필드에서 타입을 주게 되면 당연히 필드로 Integer만 올 수 있다.
- 하지만, 제네릭을 사용한다면 다양한 타입으로 설정할 수 있고, 동일한 기능의 메서드들을 다양한 타입에 적용할 수 있다.
- 타입은 객체를 생성할 때 결정 된다.
타입 파라미터 컨벤션
- 제네릭에서 사용하는 타입 파라미터에는 아무런 문자나 넣어도 코드가 동작한다.
- 특히 자바 내장 클래스들과 같은 이름의 문자로 넣어도 동작하는데, 이렇게 한다고 하더라도 해당 타입으로 제한이 생기는 것은 아니다. ⇒ 밑의 두 사진은 동일하게 동작하는 클래스
![]() |
![]() |
- 하지만 이름을 마음대로 정하게 된다면 자칫 타입 제한이 있다고 착각하는 등 혼란을 야기할 수 있으므로 컨벤션이 존재한다. 컨벤션은 다음과 같다
제한된 타입 파라미터 (제네릭 클래스 선언부)
- 제네릭 클래스를 설계할 때 사용
- 제네릭타입<T> : Unbounded Wildcards (제한 없음)
- 모든 클래스나 인터페이스 타입 가능, <T extends Object>와 동일
- 제네릭 타입<T extends M> : Upper Bounded Wildcards (상한 제한)
- M와 그 자손들 가능
- 제네릭 타입<T super M> : 사용 불가. T가 M의 부모이면 클래스 설계할 때 M클래스의 속성이나 메서드를 보장할 수 없으며, 제네릭의 취지와 맞지 않음
제네릭 메서드
- 메서드의 선언부에 제네릭 타입이 선언된 형식
- 앞서 살펴본 제네릭 타입과 유사해 보일 수 있다.
- 타입 매개변수의 범위가 메서드에서 국한된다는 차이점 이 있다.
- 제네릭 메서드는 제네릭 클래스가 아닌 다른 곳에서도 정의할 수 있다.
- 제네릭 메서드의 매개변수 타입을 더 우선한다
와일드카드
- 꺽쇠 안에 물음표 기호<?>를 써서 나타내며, 이는 알 수 없는 타입을 의미한다.
- 위의 제네릭 클래스 선언부와 원리는 비슷하나, 차이점이 있고, 클래스 선언부에서 <?>를 쓸 수 없다 (클래스 내부에서는 가능)
- 제네릭 클래스 설계를 할 때 쓰이는 것이 아니다.
- 제네릭 타입의 유연성을 위해 존재한다
- 제네릭 클래스 선언부에서 쓰이는 <T>, <T extends M>
- <T> : 모든 클래스나 인터페이스 타입이 가능하며, 객체 생성 시 해당 타입으로 결정된다
- <T extends M> : M와 그 자손들 가능하며, 객체 생성 시 해당 타입으로 결정된다
- 와일드카드
- <?> : 모든 클래스나 인터페이스 타입이 가능하며, 특정 타입 하나로 결정되지는 않는다.
- <? extends T> : T와 그 자손들 타입을 받아들일 수 있으며, 특정 타입 하나로 결정되지는 않는다.
- <? extends T> : T와 그 조상들 타입으로 받아들일 수 있으며, 특정 타입 하나로 결정되지는 않는다.
와일드카드 사용 예시
![]() |
![]() |
- Integer가 Object의 자손이기 때문에 List<Object>를 매개변수로 하는 함수에 List<Integer>타입을 넣어줘도 될 것 같지만, 이는 컴파일 오류가 발생한다.
- 이는 제네릭 타입이 무공변 이어서, 해당 타입(List<Object>)으로만 인자를 받을 수 있기 때문이다. 이를 해결하기 위해 우측처럼 와일드 카드를 사용한다.
- 컬렉션 프레임워크 상속관계는 당연히 유효함으로 이를 혼동하지 말자
![]() |
![]() |
- 와일드카드는 함수의 매개변수에서 가장 많이 사용된다
- 매개변수에서 T extends Object와 같은 형태로 쓰려는 것은 불가능하며, 컴파일 오류가 발생한다.
와일드 카드 값 꺼내기/넣기 intro
![]() |
![]() |
- 간단한 getter와 setter로 구성된 제네릭 클래스 Product 이다.
- 객체를 생성할 때 와일드 카드<?>를 사용하고, 이후 getter를 사용하여(값 꺼내기) 출력할 때는 잘 작동한다
- 후에 setter를 사용(값 넣기)할 때는 오류가 발생한다.
- <?> 는 <? extends Object>와 같다
와일드카드 값 꺼내기 / 넣기
상속관계 ⇒ Object - Machine - Car - 아반떼, 소나타, 그랜져
![]() |
![]() |
<? extends T> 값 꺼내기 / 넣기
- 꺼내기 (getter) : 안전하게 꺼내려면 T타입으로 꺼낸다
- T 가 Car이라면 Car, 아반떼, 소나타, 그랜져 취급 가능
- 아반떼로 값을 꺼내면 소나타, 그랜져 가 전해질 때 오류 발생
- 넣기 (setter) : 모든 타입 불가
- 하위 타입에 상위 타입이 대입 될 위험이 존재 ⇒ 불가
- null은 가능
<? super T> 값 꺼내기 / 넣기
- 꺼내기 (getter) : Object 타입으로만 꺼내기 가능
- Car타입으로 꺼내고, 매개변수로 Machine타입이 들어오면 오류 발생할 수 있으므로
- 최상위 Object라면 그럴 위험 없음
- 넣기 (setter) : T와 T 자손 가능
- 매개변수로 오는 최상위 타입이 Car로 정해져 있으므로, Car와 그 자손을 넣게 되면 하위 타입에 상위 타입이 대입 될 위험이 없다.
와일드카드 extends / super 사용 시기
PECS 공식 : PECS란, Producer-Extends / Consumer-Super 라는 단어의 약자인데 다음을 의미한다.
- 외부에서 온 데이터를 생산(Producer) 한다면<? extends T> 를 사용
- 매개변수로 온 <? extends T> 는 T타입으로 데이터를 꺼낼 때 안전
- 외부에서 온 데이터를 소비(Consumer) 한다면<? super T> 를 사용
- 매개변수로 온 <? super T> 는 T타입과 T자손으로 데이터 저장할 때 안전
class MyArrayList<T> {
Object[] element = new Object[5];
int index = 0;
// 외부로부터 리스트를 받아와 매개변수의 모든 요소를 내부 배열에 추가하여 인스턴스화 하는 생성자
public MyArrayList(Collection<? extends T> in) {
for (T elem : in) {
element[index++] = elem;
}
}
// 외부로부터 리스트를 받아와 내부 배열의 요소를 모두 매개변수에 추가해주는 메서드
public void clone(Collection<? super T> out) {
for (Object elem : element) {
out.add((T) elem);
}
}
}
출처
(도서) 이것이 자바다
자바의 정석 유튜브 강의
https://inpa.tistory.com/entry/JAVA-☕-제네릭-와일드-카드-extends-super-T-완벽-이해
https://tecoble.techcourse.co.kr/post/2020-11-09-generics-basic/
'Backend 취업준비 > Java' 카테고리의 다른 글
람다식 (0) | 2024.02.21 |
---|---|
자바 함수형 프로그래밍 특징 (0) | 2024.02.18 |
예외 처리, 재귀, 반복문, 좋은 예외 처리 (2) | 2024.02.07 |
객체 지향 프로그래밍 OOP - 다형성 (0) | 2024.02.03 |
객체 지향 프로그래밍 OOP - intro (0) | 2024.02.03 |