자바 제네릭(Java Generic)

By | 2022년 8월 12일
Table of Contents

자바 제네릭(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> { ... }

답글 남기기