반응형

Generic 이란?

  • List, Map, Set 등을 선언할 때, List<String>, List<Integer> 와 같은 코드를 사용함

  • 위와 같이 <> 안에 Data Type을 지정해 줌

  • 이 때, <> 안에 특정 Data Type을 미리 지정하지 않고 개발자의 필요에 의해 지정할 수 있도록 하는것이 Generic

  • 한마디로 Generic은 Data Type을 일반화(generalize) 한다는 의미

  • 우리가 같은 List를 List<Integer>, List<String>과 같이 Data Type만 달리해서 사용할 수 있는것도 List에서 Generic 문법이 사용되기 때문

  • JDK 1.5 버전 이후로 사용 가능

Wrapper Class 란?

  • List<Integer>같은 코드를 보면 왜 int가 아닌 Integer을 사용하는데, 이 때 Integer와 같은 것들을 Wrapper Class라 함
  • Primitive Type(byte, short, char, int, long, float, double, boolean)과 달리
  • Wrapper Class(Byte, Short, Character, Integer, Long, Float, Double, Boolean)는 Primitive Type을 감싼 Object(객체)
    • Boxing : Primitive Type -> Wrapper Class
    • Unboxing : Wrapper Class -> Primitive Type
    • ex) Integer n = Integer.valueOf(10); // Boxing

Generic 사용 이유

  • Generic 사용 이전의 코드로 예제를 만들어 봄
public class GenericTest {

    public static void main(String[] args) {
        FruitBox appleBox = new FruitBox();

        appleBox.set(new Apple());

        Apple apple = (Apple) appleBox.get();
        System.out.println(apple);      // Apple 출력
    }

    static class FruitBox {
        private Object obj;
        public void set(Object obj) { this.obj = obj; }
        public Object get() { return obj; }
    }

    static class Apple {
        @Override
        public String toString() { return "Apple"; }
    }
}
  • 크게 문제가 없어보이지만 아래의 세가지 문제가 발생할 수 있음
  1. 꺼내는 상황에서 매번 형변환이 필요함
    • FruitBox에 Apple을 넣는다해도 Object로 들어가기 때문에, 들어갈땐 괜찮지만 get할때는 형변환 필요
  2. 아래 코드를 사용할 수 있음
appleBox.set("Apple");
  • appleBox에는 Apple Object만 넣기를 의도했지만 String도 Object이기 때문에 넣는 것이 가능함
  • 나중에 꺼낼 때 형변환 과정에서 컴파일 에러 발생
  1. 아래 코드를 사용할 수 있음, 위 코드와 비슷하지만 꺼낼 때, 직접 print 찍는 방법
appleBox.set("Apple");
System.out.println(appleBox.get());
  • 이렇게 출력해버리면 처음 코드와 같이 "Apple"이 출력됨
  • 개발자의 의도와는 전혀 다르지만 에러도 발생하지 않고 실행결과까지 같음

Generic 사용 예제

  • FruitBox 클래스 옆에 <T>를 붙여주고, 전에 Object가 있던 자리를 모두 T로 바꿔줌
  • 또한 객체를 생성할 때 FruitBox<Apple> 이런 식으로 생성
    • 이 때, T를 타입 매개변수, Apple을 타입 인자라고 부름
public class GenericTest {

    public static void main(String[] args) {
        FruitBox<Apple> appleBox = new FruitBox();

        appleBox.set(new Apple());

        Apple apple = appleBox.get();
        System.out.println(apple);
    }

    static class FruitBox<T> {
        private T obj;
        public void set(T obj) { this.obj = obj; }
        public T get() { return obj; }
    }

    static class Apple {
        @Override
        public String toString() { return "Apple"; }
    }
}
  • 이런 식으로 Generic을 사용했을 때의 장점으로는
  1. 꺼낼 때 형변환이 필요 없음 => appleBox에는 Apple만 들어갔기 때문에 뺄때는 Apple임을 바로 알 수 있음
  2. set 과정에서 아까처럼 String을 넣을 수도 없음 (에러 발생 시켜줌) => Apple만 들어가야 되기 때문

여러개의 타입 매개변수 사용 가능

public static void main(String[] args) {
    Box<String, Integer> box1 = new Box<>();
    box1.set("Apple", 25);
    System.out.println(box1);   // Apple & 25 출력

    Box<Integer, String> box2 = new Box<>();
    box2.set(25, "Apple");
    System.out.println(box2);   // 25 & Apple 출력
}

static class Box<T1, T2> {
    private T1 t1;
    private T2 t2;
    public void set(T1 t1, T2 t2) {
        this.t1 = t1;
        this.t2 = t2;
    }

    @Override
    public String toString() {
        return t1 + " & " + t2;
    }
}

Generic class의 타입 인자 제한

  • class Box <T extends Number> 등과 같이 생성해서 타입 인자를 Number(int, double, long, ...)으로 제한 할 수 있음
  • 이런 식으로 제한하면 intValue()와 같이 특정 타입에만 적용할 수 있는 메소드를 에러 없이 사용할 수 있게되는 등의 장점

Generic Method

  • Class 뿐만 아니라 Method에도 Generic을 적용시킬 수 있음
  • 차이점이 있다면 앞에 <T>를 한 번 더 붙여줌으로써, 이 메소드가 Generic Method임을 나타내 줘야함
public class GenericTest {
    public static void main(String[] args) {
        Box<String> stringBox = BoxFactory.makeBox("Sweet");
        Box<Double> doubleBox = BoxFactory.makeBox(7.59);

        System.out.println(stringBox.get());
        System.out.println(doubleBox.get());
    }

    static class BoxFactory {
        public static <T> Box<T> makeBox(T o) {
            Box<T> box = new Box<>();
            box.set(o);
            return box;
        }
    }
    static class Box<T> {
        private T x;

        public void set(T x) {
            this.x = x;
        }

        public T get() {
            return x;
        }
    }
}
반응형

↓ 클릭시 이동

복사했습니다!