제네릴은 자바 5부터 사용할 수 있다. 제네릭을 지원하기 전에는 컬렉션에서 객체를 꺼낼 때 마다 캐스팅을 해야 했다.
실수로 인한 캐스팅 오류가 런타임에 자주 나타났다.
제네릭을 사용하면 켈렉션이 담을 수 있는 타입을 컴파일러에 알려주게 된다.
때문에 타입이 달라서 발생하는 캐스팅 오류를 차단할 수 있고 안정성을 높일 수 있다.
용어.
캘래스의 인ㅌ페이스 선언에 타입 매개변수가 사용되면 이를 제네릭 클래스 혹은 제네릭 인터페이스라 부른다.
List 인터페이스는 원소의 타입을 나타내는 타입 매개변수 E를 받는다.
그래서 인터페이스의 완전한 이름은 List<E>이다.
제네릭 클래스와 제네릭 인터페이스를 통들어 제네릭 타입이라 한다.
각각의 제네릭 타입은 일련의 매개변수화 타입을 정의한다.
<E>에 들어가는 타입이 셀제 타입 매개변수 이다.
제네릭 타입을 하나 정의하면 그에 딸린 로 타입도 함께 정의된다.
로 타입이란 제네릭 타입에서 타입 매개변수를 전혀 사용하지 않을 때를 말한다.
List<E>의 로 타입은 List이다.
로 타입은 타입 선언에서 제네릭 타입 정보가 전부 지워진 것처럼 동작하는데, 제네릭이 나오기 전 코드와 호환하기 위한 방책이였다.
private final Collection stamps = ...;
stamps.add(new Coin());
/////// 제네릭 사용
private final Collection<Stamp> stamps = ...;
예제 코드이다.
도장 ( stamp )만 들어가야 하는 공간에 동전 ( Coin ) 이 들어갔지만 오류 없이 컴파일 되고 실행된다.
이러한 에러는 런타임에서야 오류가 발생하여 한참 뒤에 알아챌 수 있는데 문제 지점을 찾기 위해 코드 전체를 훑어봐야 할 수도 있다.
하지만 아래와 같이 제네릭을 사용하면 다른 타입이 들어가는 것을 방지해줄 수 있다.
제네릭을 활용하면 한 타입만을 사용해야 한다는 정보가 주석조차 필요없이 타입 선언 자체에 녹아든다.
이제 stamps에 다른 타입을 넣으면 컴파일 오류가 발생하여 무엇이 잘못됐는지 정확히 알 수 있다.
로 타입을 쓰는 것은 언어 차원에서 막아 놓지 않았지만 절대로 써서는 안된다.
로 타입을 쓰면 제네릭이 안겨주는 안전성과 표현력을 모두 잃게 된다.
버전의 호환성 때문에 로 타입이 존재 할 뿐, 이 규칙은 반드시 지켜야 한다.
하지만 List<Object>와 같이 임의 객체를 허용하는 매개변수화 타입은 괜찮다.
둘의 차이점은
로 타입은 List는 제네릭 타입에서 완전히 발을 뺀 것이고
List<Object>는 모든 타입을 허용한다는 의미이다.
만약 타입 매개변수가 무엇이든 신경쓰고 싶지 않을 때에는 뒤에서 포스팅 할 비한정적 와일드카드인 List<?>를 사용하자.
단. 소소한 예외가 몇가지 있는데 , class 리터럴을 사용할 때에는 로 타입을 사용한다.
ex ) List.class
또한, instanceof 연산자를 사용할 때도 로 타입을 사용해도 된다.
ex) if( o insatnceof List) {
Set<?> s = (Set<?>)o;
}
! instanceof 연산자란?
원래 인스턴스의 형이 맞는지 여부를 체크하는 키워드이다.
class Parent{}
class Child extends Parent{}
...
parent instanceof Parent // true
child instanceof Parent // true
parent instanceof Child // false
child instanceof Child // true
질문 1. 로 타입이란 무엇인가? 왜 사용하면 안되는가?
로 타입은 제네릭 타입이 도입 되기 전 언어의 호환성을 위해 도입되었다.
제네릭 타입을 로 타입으로 사용하면 컴파일 타임에 오류를 감지하기 어렵게 만들고 안정성과 표현력을 모두 잃게 된다.
단, 클래스 리터럴이나 instanceof 연산자를 쓸 때에는 사용한다.
질문 2. 제네릭 타입을 사용하는 이점은?
1. 잘못된 타입을 사용하면 컴파일 타임에 타입 체크를 수행하여 불필요한 형변환 오류를 막아준다.
2. 주석 등을 사용하지 않아도 제네릭 선언 자체가 표현력을 가진다.
3. 코드의 중복을 줄여서 재사용성을 높일 수 있다.
질문 3. 제네릭 타입을 사용할 때에 주의점은?
1. 타입 소거로 인해 컴파일러가 타입 정보를 지우기 때문에, 런타임에는 제네릭 타입의 파라미터화된 타입 정보를 알 수 없다.
따라서 컴파일러 경고를 무시하지 않고 제대로된 타입 안정성을 유지해야 한다.
2. 와일드카드 타입과 상한/하한 타입 제한을 올바르게 사용해야 한다.
와일드카드 타입은 불필요한 제한을 피하고 유연성을 높일 수 있지만, 타입 안정성을 저하시킬 수 있다.
'JAVA > 이펙티브 자바' 카테고리의 다른 글
28. 배열보다는 리스트를 사용하라 (0) | 2023.07.16 |
---|---|
27. 비검사 경고를 제거하라 (0) | 2023.07.16 |
25. 톱레벨 클래스는 한 파일에 하나만 담으라 (0) | 2023.07.10 |
24. 멤버 클래스는 되도록 static으로 만들라 ( 멤버 클래스,어댑터 패턴 ) (0) | 2023.07.10 |
23. 태그 달린 클래스보다는 클래스 계층구조를 활용하라 ( 클래스 계층구조 ) (0) | 2023.07.06 |