자바 제네릭(Java Generic)
Generic Type 은 범용 타입을 지원한다.
Generic 장단점
장점
코드 중복이 줄어든다.
// Generic 이 없으면
public class MyIntegerMapper { ... }
public class MyFloatMapper { ... }
public class MyStringMapper { ... }
// Generic 을 사용하면
public class MyMapper<T> { ... }
단점
Generic 은 Java 문법중 난해한 문법에 속한다.
RestController CRUD 에 적용해 보았는데,
가독성이 매우 안좋았다.
Mapper 같은 아주 단순한 기능에 Generic 을 적용하는게 좋다.
기본 문법
class, interface 에 Generic 을 적용할 수 있다.
enum 은 interface 구현을 통해서만 적용할 수 있다.
public class MyClass<T> { ... }
public class MyClass<K, V> { ... }
public interface MyInterface<T> { ... }
public enum MyEnum implements MyInterface<Integer> { ... }
기본 문법 설명
public class MyMapper<T> {
private T value;
public String toString() {
return value.toString();
}
public MyMapper(T value) {
this.value = value;
}
}
MyMapper<Integer> a = new MyMapper<>(1);
MyMapper<Float> b = new MyMapper<>(1.0f);
MyMapper<Double> c = new MyMapper<>(1.0);
Generic 파라미터 T 는 암묵적인 규칙이고,
대문자 알파벳 한글자라는 규칙은 없다.
알파벳 두글자 이상이어도 되고,
소문자여도 상관없다.
하지만 암묵적으로 대문자로 파라미터를 지정해 준다.
Generic 파라미터는 Object 를 상속받은 객체만 가능하다.
따라서 int, float 등은 파라미터가 될 수 없다.
메소드 Generic
메소드에 대해서만 Generic 을 적용하는 것도 가능하다.
접근 제어자(public 등) 다음에 Generic 파라미터를 명시해 준다.
public class MyClass {
public <K> String toString(K k) {
return k.toString();
}
public static <T> T origin(T t) {
return t;
}
}
MyClass cls = new MyClass();
System.out.println(cls.toString(1));
System.out.println(MyClass.origin(2));
메소드 Generic 은 동일한 파라미터 K 를 사용했다고 해도,
클래스 Generic 과는 별개의 파라미터이다.
하지만, IntelliJ 가 불평불만을 쏟아낸다.
public class MyClass<K extends BaseDocument> {
public static <K> K origin(K k) {
return k;
}
}
범위 제한 Generic
<K extends T>
특정 클래스를 상속받는 하위 클래스만,
파라미터로 받는 Generic 을 생성할 수 있다.
공통적으로 필요한 메소드(예를 들면 데이타의 PK 반환받기 등) 가 필요한 경우,
해당 메소드를 부모 클래스로 만들어 놓고,
모든 클래스를 부모 클래스를 상속받게 해서,
해당 메소드를 이용하게 할 수 있다.
interface 를 상속받는 하위 클래스에 대해서도,
extends 를 사용한다.(implements XXX)
public class BaseDocument {
private final String id;
public String getId() {
return id;
}
public BaseDocument(String id) {
this.id = id;
}
}
public class ItemDocument extends BaseDocument {
public ItemDocument(String id) {
super(id);
}
}
public class MyClass<K extends BaseDocument> {
private final K document;
public String getId() {
return document.getId();
}
public MyClass(K document) {
this.document = document;
}
}
MyClass<ItemDocument> cls = new MyClass<>(new ItemDocument("a"));
System.out.println(cls.getId());
<K super T>
특정 클래스를 상속받는 자식 클래스가 아니라,
부모 클래스만 파라미터로 받는 Generic 을 생성할 수 있다.
하지만 코드를 작성해 본 적은 없다.
<?> (와일드 카드 : Wild Card)
과일 클래스를 상속받는 사과 클래스, 바나나 클래스가 있다고 할 때,
<T extends E>
는 사과면 사과, 바나나면 바나나,
한가지의 클래스만 데이타로 받을 수 있다.
반면에, <? extends E>
는 사과, 바나나를 동시에 데이타로 받을 수 있다.
public boolean addAll(Collection<T extends E> c);
public boolean addAll(Collection<? extends E> c);
하지만 코드를 작성해 본 적은 없다.
클래스와 인터페이스 동시 상속
public class SomeClass<T extends MyClass & MyInterface> { ... }