Java

[Java] 제네릭 generic

junga 2022. 9. 17. 17:49
  • 제네릭이란 클래스 내부에서 사용할 데이터 타입을 외부에서 지정하는 기법이다.


제네릭 선언 및 생성


// 선언
class MyClass<T> {
    T element;

    void setElement(T element) { this.element = element; }

    T getElement() {
        return element;
    }
}


public class Main {
    public static void main(String[] args) {
        // 생성: 예시로 Integer타입 명시함
        MyClass<Integer> mc = new MyClass<Integer>();
        mc.setElement(777);
        System.out.println(mc.getElement());  // 777
    }
}
// Java SE 7부터 인스턴스 생성 시 타입을 추정할 수 있는 경우에는 타입을 생략할 수 있습니다.
MyClass<Integer> mc = new MyClass<>();
  • 'T'는 타입 변수라고 하며, 임의의 참조형 타입을 의미한다.
  • 타입 변수는 클래스, 메소드의 매개변수나 반환값으로도 사용할 수 있다.
  • 생성 시 타입변수 자리에 사용할 실제타입을 명시해야 한다.

꼭 'T'뿐만 아니라 어떤 문자를 사용해도 상관은 없다.
다만 아래처럼 암묵적(?)인 규칙이 있다 한다.

타입 설명
<T> Type
<E> Element
<K> Key
<V> Value
<N> Number


멀티 타입 파라미터


  • 여러 개의 타입 변수는 쉼표(,)로 구분하여 명시할 수 있다.
class MyClass<T, E> {
    T num;
    E str;
    void setElement(T num, E str) {
        this.num = num;
        this.str = str;
    }

    T getNum() {
        return num;
    }
    E getStr() {
        return str;
    }
}

public class Main {
    public static void main(String[] args) {
        MyClass<Integer, String> mc = new MyClass<Integer, String>();
        mc.setElement(777, "칠칠칠");
        System.out.println(mc.getNum());  // 777
        System.out.println(mc.getStr());  // 칠칠칠
    }
}


제한된 제네릭


  • 타입을 구체적으로 제한하고 싶을 때
  • extends, super, 와일드카드로 제한할 수 있다.
  • 와일드카드는 물음표(?) 기호를 말하며 '알 수 없는 타입'을 의미한다.
  • 메섣, 인터페이스, 클래스에서 동일하게 적용


<T extends 상위타입>

  • T를 대체할 수 있는 타입은 상위타입이나 상위타입을 상속받는 하위타입들만 가능하다.
class Bag<T> {
    T thing;

    Bag(T thing) {
        this.thing = thing;
    }
}

Bag에 담을 수 있는 객체에 제한을 두려고 한다.
책, 필통, 노트처럼 고체인 물건은 담을 수 있지만,
물이나 커피처럼 엑체인 물건은 담을 수 없도록 제한해보자.

class Solid{} 
class Liquid{} 
class Book extends Solid{} // 고체클래스를 상속받은 하위클래스
class NoteBook extends Solid{} // 고체클래스를 상속받은 하위클래스
class Water extends Liquid{} // 액체클래스를 상속받은 하위클래스


// 고채만 넣을 수 있게 제한
class Bag<T extends Solid> {
    T thing;
    Bag(T thing) {
        this.thing = thing;
    }
}

// 메인
public class Main {
    public static void main(String[] args) {
        Bag<Book> bag1 = new Bag<Book>(new Book());
        Bag<NoteBook> bag2 = new Bag<NoteBook>(new NoteBook());
        // Bag<Water> bag3 = new Bag<Water>(new Water()); // 오류 발생
    }
}

액체클래스의 하위클래스인 Water는 T타입으로 들어갈 수 없다.



와일드 카드 <?>

  • 어떤 타입이든 올 수 있다. <? extends Object>와 마찬가지다.

물품 주인이 같은지 확인해보는 메서드를 추가하였다.

class Bag<T extends Solid> {
    T thing;
    String owner;
    Bag(T thing, String owner) {
        this.thing = thing;
        this.owner = owner;
    }

    // 물품 주인이 같은지 확인
    void isSameOwner(Bag<T> obj) {
        if (this.owner.equals(obj.owner)) {
            System.out.println("true");
        } else {
            System.out.println("false");
        }
    }
}

// 메인
public class Main {
    public static void main(String[] args) {
        Bag<Book> bag1 = new Bag<Book>(new Book(), "김오너");
        Bag<NoteBook> bag2 = new Bag<NoteBook>(new NoteBook(), "김오너");
        // bag1.isSameOwner(bag2); // 오류 발생
    }
}

오류가 발생한 이유에 대해 알아보자.
bag1의 타입은 Book이고, bag2의 타입은 NoteBook이다.
bag1의 isSameOwner() 메소드에서 사용하는 T와 인자로 전달된 obj의 T값이 달라서 오류가 발생한 것이다.

이럴 때 사용할 수 있는 것이 와일드카드다.
아래와 같이 와일드카드를 사용하면 오류가 발생하지 않는다.

void isSameOwner(Bag<?> obj) {...}


와일드 카드 제한

  • 와일드카드에도 타입을 제한할 수 있다.
  • <? extends 상위타입>: 상위타입 이하로 제한한다.
  • <? super 하위타입>: 하위타입 이상으로 제한한다.

참고: https://slow-and-steady-wins-the-race.tistory.com/104

'Java' 카테고리의 다른 글

[Java] 컬렉션 프레임워크  (0) 2022.09.19
[Java] 람다식과 함수형 인터페이스  (0) 2022.09.17
[Java] 예외 처리  (0) 2022.09.15
[Java] 입출력 스트림  (0) 2022.09.15
[Java] 내부 클래스와 익명 클래스  (0) 2022.09.14