본문 바로가기

자바

JAVA Generic

제네릭(Generic)이란

제네릭스(Generics)는 자바 J2SE 5.0 이후에 도입된 개념이다.

제네릭스는 다양한 타입의 객체들을 다루는 메서드나 컬렉션 클래스에 컴파일 시의 타입체크를 해주는 기능이다.

  • 클래스에서 사용할 타입을 클래스 외부에서 결정할 수 있다.
  • 컴파일 시 타입 안정성을 제공한다.(의도하지 않은 타입의 객체의 저장을 막고 꺼내올 때의 오류를 방지한다.)
  • 타입 변환을 하지 않아도 된다.
  • 코드 가독성 및 재사용성이 높아진다.
  • Collection에서 자주 사용된다.

제네릭(Generic) 사용방법

클래스

class Generic {
    public static void main(String[] args) {
        Box<String> box = new Box<>();
        box.setItem("GilSSang's Bag");
        System.out.println("box.getItems() = " + box.getItems()); // box.getItems() = GilSSang's Bag
    }

    static class Box<T> {
        private T item;

        public T getItems() {
            return item;
        }

        public void setItem(T item) {
            this.item = item;
        }
    }
}
  • Box라는 클래스와 item이라는 변수는 별도의 타입을 설정하지 않고 제네릭으로 선언하였다.
  • 외부(main)에서 Generic Class의 타입을 여러 가지로 지정하여 코드의 재사용성을 높이고 여러 타입에 대한 구현을 확장할 수 있다.
  • 다음은 String으로 설정한 모습을 확인할 수 있다.

인터페이스

interface Box<T> {
    public void printItem(T item);
}

class Generic {
    public static void main(String[] args) {
        Box stringBox = new StringBox(); 
        stringBox.printItem("GilSSang"); // (String)박스에 담긴 물건은 GilSSang입니다.

        Box integerBox = new IntegerBox();
        integerBox.printItem(2021); // (Integer)박스에 담긴 물건은 2021입니다.

    }

    static class StringBox implements Box<String> {
        @Override
        public void printItem(String item) {
            System.out.println("(String)박스에 담긴 물건은 " + item + "입니다.");
        }
    }

    static class IntegerBox implements Box<Integer> {
        @Override
        public void printItem(Integer item) {
            System.out.println("(Integer)박스에 담긴 물건은 " + item + "입니다.");
        }
    }
}
  • Box라는 인터페이스를 제네릭으로 선언하였다.
  • 이를 String타입으로 상속받는 StringBox와 Integer타입으로 상속받는 IntegerBox를 구현하였다.

메서드

class Generic {
    public static void main(String[] args) {
        System.out.println(getItem("GilSSang")); // GilSSang
        System.out.println(getItem("2021")); // 2021

    }

    public static <T> T getItem(T item) {
        return item;
    }
}
  • 메서드에도 제네릭으로 선언하여 사용할 수 있다.
  • 타입 매개변수의 사용은 메소드 내부로 제한된다.

멀티 타입

class Generic {
    public static void main(String[] args) {
        Box<String, String> box = new Box<>();
        box.setKey("GilSSang");
        box.setValue("JAVA");
        System.out.println("box.getKey() = " + box.getKey()); // box.getKey() = GilSSang
        System.out.println("box.getValue() = " + box.getValue()); // box.getValue() = JAVA

    }

    static class Box<K, V> {
        private K key;
        private V value;

        public K getKey() {
            return key;
        }

        public void setKey(K key) {
            this.key = key;
        }

        public V getValue() {
            return value;
        }

        public void setValue(V value) {
            this.value = value;
        }
    }
}
  • 두 개 이상의 파라미터를 가진 제네릭을 선언하여 사용할 수 있다.

제한된 타입의 파라미터

class Generic {
    public static void main(String[] args) {
        Box<String> box = new Box<>();
        box.setItem("GilSSang's Bag");
        System.out.println("box.getItem() = " + box.getItem()); // box.getItem() = GilSSang's Bag

        // Box<Integer> box = new Box<>(); -> 컴파일 시점 오류 (빨간 줄)

    }

    static class Box<T extends String> {
        private T item;

        public T getItem() {
            return item;
        }

        public void setItem(T item) {
            this.item = item;
        }
    }
  • 제네릭에 대해 extends한 타입 영역(자식들) 내에서 사용할 수 있다.
  • 또한, 제네릭에 대해 super한 타입 영역(부모들) 내에서 사용할 수 있다.

타입 파라미터

제네릭의 타입 파라미터를 어느 용도로 사용하는 지에 대한 가이드 라인이다.

와일드 카드

제네릭은 특정 타입으로 귀속이 된다.

다양한 타입을 하나의 인스턴스에서 사용하기 위해 와일드 카드를 사용한다.

와일드 카드를 사용하면, 여러가지의 타입을 허용할 수 있다.

와일드 카드도 제네릭과 마찬가지로 extends와 super을 사용해 제한된 타입의 파라미터를 받을 수 있다.

class Generic {
    public static void main(String[] args) {
        Box<Integer> integerBox = new Box<>();
        integerBox.addItem(1);
        integerBox.addItem(2);

        Box<Double> doubleBox = new Box<>();
        doubleBox.addItem(3.5);
        doubleBox.addItem(4.5);

        Box<String> stringBox = new Box<>();
        stringBox.addItem("GilSSang");
        stringBox.addItem("Tommo");

        Printer printer = new Printer();
        printer.printItem(integerBox);
        // item = 1
        // item = 2

        printer.printItem(doubleBox);
        // item = 3.5
        // item = 4.5

        // printer.printItem(stringBox); -> 컴파일 시점 오류 (빨간 줄)
    }

    static class Printer {
        public void printItem(Box<? extends Number> items) {
            for (Number item : items.getItems()) {
                System.out.println("item = " + item);
            }
        }
    }

    static class Box<T> {
        List<T> items = new ArrayList<>();
        public void addItem(T item) {
            this.items.add(item);
        }

        public List<T> getItems() {
            return items;
        }
    }
}

주의점

원시타입은 제네릭에서 사용할 수 없다.

Box<int> box = new Box<>(); // 불가

제네릭 타입을 사용하여 객체 생성 불가.

T box = new T(); 불가

'자바' 카테고리의 다른 글

JAVA Enum  (0) 2021.10.17
JAVA Collection  (0) 2021.10.15
JAVA Lambda, Functional Interface  (0) 2021.10.14
JAVA Exception  (0) 2021.10.13
Java Optional  (1) 2021.10.04