이번 정리 글은 Generic이 뭔지는 알지만 왜쓰는지를 더 정확히 정리하는데에 목적을 둡니다.
다양한 타입을 수용할 수 있는 자료구조를 만들기 위해? 다양한 타입을 지원하는 Class를 구현하기 위해.
타입 형변환의 에러를 피하기위해 등 여러 목적에 있습니다.
자바는 여러 타입들이 존재하기 때문에, 형변환(casting)을 하면서 많은 예외가 발생할 수 있다.
import java.util.List
class TestProgram {
public static void main(String[] args) {
List<Object> items = new List<>();
items.add(new Item1());
items.add(new Item2());
}
}
다음과 같이 부모 클래스 or 인터페이스 타입으로 정의된 List가 있을 경우, 값을 넣을 때는 상관없지만 꺼내서 알맞은 클래스 형태로 얻고 싶다면 명시적인 형변환(casting)이 필요합니다.
Item2 item = (Item2)items.get(1); // Item1 object를 Item2로 형변환.
하지만 적절하지않은 타입으로의 형변환은 런타임 Exception 을 발생시킵니다. 예외처리를 위해서 아래와같은 코드들이 추가될 것 입니다.
Object item = items.get(1);
if(item instanceof Item1) {
...
} else if (item instanceof Item2) {
...
}
instanceof를 사용하는 것은 좋지않은 방식임이 분명합니다.
Probably most of you have already heard that using “instanceof” is a code smell and it is considered as a bad practice.
Java 5부터 추가된 Generic이라는 것이 있습니다.
형변환에 의해 발생할 수 있는 문제점을 컴파일 시점에 점검할 수 있는 방법입니다.
사용방법은 간단합니다.
package d.generic;
import java.io.Serializable;
public class CastingGenericDTO<T> implements Serializable {
private T object;
public void setObject(T obj) {
this.object = obj;
}
public T getObject() {
return this.object;
}
}
<T>
꺽쇠로 매개변수화 타입(parameterized type)를 정의해서 사용하면됩니다.
매개변수화 타입의 이름에도 컨벤션이 있으니 지키는 것이 좋습니다.
아마 이렇게 제네릭을 활용해서 작성하면 형 변환을 하는 로직이 사라지게됩니다. (instanceof 키워드를 사용할 일이 거의 없어지기 때문에 가독성이 좋아질 것입니다.) 왜냐하면 코드에서 명시적으로 타입을 지정하기 때문에 잘못된 타입으로 치환하면 컴파일 자체가 안되기 때문입니다.
Generic 타입을 사용할 때 여러가지의 타입을 사용하는 제네릭타입을 인자로 받고 싶을 때를 위해 Wildcard라는 것을 사용합니다.
public void wildcardStringMethod(WildcardGeneric<?> c) {
...
}
위 코드에서는 어떤 타입이 적용된 WildcardGeneric 객체라도 인자로 받을 수 있는 함수가 됩니다.
public void wildcardStringMethod(WildcardGeneric<? extends Car> c) {
...
}
필요하다면 범위가 제한된 wildcard를 사용가능합니다. 이또한 컴파일 시간에 정확한 타입이 사용됐는지 검증될 수 있습니다.
Class 선언과 무관하게 특정 Method에서 사용될 매개변수의 Type을 Generic하게 적용하고 싶을 수 도 있습니다.
public class GenericWildcard {
public statuc <T> void genericMethod(T value) { ... }
}
위와 같이 사용하면 Generic하게 Mehtod를 사용할 수 있습니다. Generic Class를 선언할 때와는 달리 따로 <>꺽쇠를 사용해서 타입을 명시하지않고 매개변수를 넣어서 암묵적으로 타입 처리를 할 수 있다.
GenericWildcard.genericMethod("Hello"); // 암묵적으로 T는 String Type으로 치환된다.
아주 간단히 사용법 정도만 남겼습니다. 생각보다 제네릭을 활용할 경우가 많지않을 수 있지만 잘 사용한다면 견고한 설계를 가능하게 해줍니다.
기본적인 내용만 정리하는 글이었고 Effective Java에서 설명하는 Generic 관련 주제들로 넘어가겠습니다.