💡 해당 글은 『자바의 신 3판』을 복습하며 도서의 내용과 본인의 주관적인 생각을 정리한 글입니다.
☁️ 내용정리
Generic
제네릭이란 여러 타입을 컴파일 타임에 지정하여 저장하기 위한 개념이다. 제네릭을 사용하면 형 변환 시 발생할 수 있는 문제를 사전에 제거할 수 있다. 예를 들어, 아래와 같은 상황이다.
public class GenericTest {
private final Objcet value;
public GenericTest(Object value) {
this.value = value;
}
public Object getValue() {
return value;
}
}
GenericTest g1 = new GenericTest(new String());
GenericTest g2 = new GenericTest(new StringBuilder());
GenericTest g3 = new GenericTest(new StringBuffer());
// g1.getValue() 해당 변수의 선언부를 알지 못하는 코드는
// g2.getValue() 과연 해당 코드가 어떤 타입의 변수를 반환할지
// g3.getValue() 판단하고 저장할 수 있을까?
이를 해결하기 위해 제네릭이라는 타입을 사용할 수 있다.
public class GenericTest<T> {
private final T value;
public GenericTest(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
// 객체를 생성하는 시점에 타입을 명시해주기 때문에 어떤 타입이 반환되는지 파악할 수 있다.
GenericTest<String> g1 = new GenericTest("aa");
String aa = g1.getValue();
제네릭 타입은 명명 규칙이 존재한다. 무조건 지켜야 하는 것은 아니다.(타입 명을 HYUNSB로 해도 됨)
E
: element 요소라는 의미, 컬렉션에서 사용됨.K
: key, map에서 사용됨N
: number, 숫자T
: typeV
: value
와일드카드
와일드카드란 어떤 타입이든 올 수 있는 타입을 의미한다. ?
를 통해 선언한다.
타입 지정하기
제네릭과 와일드카드는 extends
, super
키워드를 통해 타입의 범위를 지정할 수 있다.
// Upper Bounded
// Number를 상속받는 객체만 타입으로 지정될 수 있음, 내가 Number 이거나 상속 받는다.
// Number, Integer, Double ...
public static <T extends Number> void printGeneric(GenericTest<T> genericTest) {
T value = genericTest.getValue();
System.out.println(String.valueOf(value));
}
// Lower Bounded
// Integer 혹은 상위 타입만 지정될 수 있음. 내가 Integer 이거나 부모이다.
// Integer, Number, Object ...
public static void printWildcard(GenericTest<? super Integer> genericTest) {
Object value = genericTest.getValue();
System.out.println(String.valueOf(value));
}
☁️ 내 생각
와일드카드의 사용처에 대해
자바의 신 2권 62p 에서는 아래와 같은 상황에서 wildcard를 사용한다고 한다.
// 간단하게 제네릭 타입을 저장하고 꺼내는 클래스
public class GenericTest<T> {
private final T value;
public GenericTest(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
class Main {
public static void main(String[] args) {
GenericTest<String> genericTest = new GenericTest<>(12);
printGeneric(genericTest); // 컴파일 오류
printWildcard(genericTest);
}
// 스트링 타입 GenericTest 객체를 출력하는 메서드
public static void printGeneric(GenericTest<String> genericTest) {
String value = genericTest.getValue();
System.out.println(value);
}
// 와일드카드로 모든 GenericTest 객체를 출력하는 메서드
public static void printWildcard(GenericTest<?> genericTest) {
Object value = genericTest.getValue();
System.out.println(value);
}
}
그런데 printGeneric 메서드를 아래와 같이 변경하면 문제가 없다.
// 메서드 레벨에서 제네릭을 사용함
// 해당 메서드가 호출될 때 파라매터의 타입으로 해당 메서드에서 사용될 제네릭 타입이 결정됨.
public static <T> void printGeneric(GenericTest<T> genericTest) {
T value = genericTest.getValue();
System.out.println(String.valueOf(value));
}
따라서 와일드카드가 왜 언제 사용되어야 하는지 찾아보려 한다.
☁️ 질문
- generic이란 무엇인가?
- generic은 여러 타입을 받을 수 있는 타입이고, 컴파일 타임에 어떤 타입을 저장할지 명시적으로 선언해주어야 한다.
- generic을 사용함으로써 얻을 수 있는 이점은?
- 여러 타입을 받을 수 있기 때문에 확장성이 용이한 기능을 구현할 수 있다. 또한 컴파일 타임에 어떤 타입을 저장할 지 명시해주기 때문에 타입 안정성 또한 확보할 수 있다.
- wildcard란 무엇인가?
- 제네릭과 동일하게 컴파일 타임에 타입 안정성을 확보하기 위해 사용하는 타입이다. generic과 다르게 super 키워드를 통한 low bounded를 설정할 수 있어 컬렉션의 element에 대한 타입 범위를 지정할 때 사용된다.
GenericTest g1 = new GenericTest(new String()); GenericTest g2 = new GenericTest(new StringBuilder()); GenericTest g3 = new GenericTest(new StringBuffer()); // g1.getValue() 해당 변수의 선언부를 알지 못하는 코드는 // g2.getValue() 과연 해당 코드가 어떤 타입의 변수를 반환할지?
'JAVA > 자바의 신' 카테고리의 다른 글
23, 24장. 자바랭 다음으로 많이 쓰는 애들은 컬렉션 - Set, Queue, Map (2) | 2024.01.10 |
---|---|
22장. 자바랭 다음으로 많이 쓰는 애들은 컬렉션 - List (1) | 2024.01.10 |
20장. 가장 많이 쓰는 패키지는 자바랭 (1) | 2024.01.09 |
19장. 이쯤에서 자바의 역사와 JVM에 대해서 알아보자 (0) | 2024.01.08 |
18장. 이제 기본 문법은 거의 다 배웠으니 정리해 봅시다 (1) | 2024.01.08 |