포도가게의 개발일지

[JAVA] Generic 본문

Spring boot

[JAVA] Generic

grape.store 2022. 1. 21. 16:02
반응형

제네릭

- 제네릭(Generic)은 클래스 내부에서 사용할 데이터 타입을 외부에서 지정하는 기법을 의미한다. 

 

why?

- 제네릭 타입을 사용함으로써 잘못된 타입이 사용될 수 있는 문제를 컴파일 과정에서 제거할 수 있기 때문입니다. 자바 컴파일러는 코드에서 잘못 사용된 타입 때문에 발생하는 문제점을 제거하기 위해 제네릭 코드에 대해 강한 타입 체크를 합니다. 실행 시 타입 에러가 나는것보다는 컴파일 시에 미리 타입을 강하게 체크해서 에러를 사전에 방지하는 것이 좋습니다.

ArrayList list = new ArrayList(); //제네릭을 사용하지 않을경우
list.add(10);
Integer temp = (Integer) list.get(0); //타입변환이 필요함
        
ArrayList<Integer> list2 = new ArrayList(); //제네릭을 사용할 경우
list2.add(10);
temp = list2.get(0); //타입변환이 필요없음

how?

- 제네릭 타입은 타입을 파라미터로 가지는 클래스와 인터페이스를 말합니다. 제네릭 타입은 클래스 또는 인터페이스 이름 뒤에 < > 부호가 붙고 사이에 타입 파라미터가 위치합니다.

public class 클래스명<T> {...}
public interface 인터페이스명<T> {...}
public class SimpleArrayList {
    private int size;
    private Object[] elementData = new Object[5];

    public void add(Object value) {
        elementData[size++] = value;
    }

// 타입을 object로 리턴하게 된다
    public Object get(int idx) {
        return elementData[idx];
    }
}

public class SimpleArrayListTest {
    public static void main(String[] args) {
        SimpleArrayList list = new SimpleArrayList();

        list.add(50);
        list.add("100");

// object로 return 되게 되면서 형변환작업이 필요하다
// 하지만 String으로 넣게 되버리면 String -> Integer로 캐스팅 에러가 발생하는 일이 생긴다.
// 하지만 이것을 컴파일시 잡을수없고 런타임 중에 error가 발생한다.
        Integer value1 = (Integer) list.get(0);
        Integer value2 = (Integer) list.get(1);

        System.out.println(value1 + value2);
    }
}
// 이를 해결 하기 위해
// generic type을 받아와
public class GenericArrayList<T> {

// object 타입으로 어떤 value든 받을수있고
// 그러면 Object도 T로 쓰게 되면 되는데 왜 Object로 쎃을까?
// new 연산자는 heap 영역에 충분한 공간이 있는지 확인한 후 메모리를 확보하는 역할을 한다. 
// 충분한 공간이 있는지 확인하려면 타입을 알아야한다. 그런데 컴파일 시점에 타입 T 가 무엇인지 
// 알 수 없기 때문에 new T[5] 와 같이 제네릭으로 배열을 생성할 수는 없다.
    private Object[] elementData = new Object[5];
    private int size;

// 형변환 없이 사용할수있게 만들어 준다.
    public void add(T value) {
        elementData[size++] = value;
    }

// return 형변환 없이 사용할수있게 만들어 준다.
    public T get(int idx) {
        return (T) elementData[idx];
    }
}

 

디컴파일한 결과!(Test.java -> Test.class -> decompile)

Test.java

class Test {
    public static void main(String[] args) {
        GenericArrayList<Integer> intList = new GenericArrayList<>();
        intList.add(1);
        intList.add(2);

        int intValue1 = intList.get(0); // 형변환이 필요없다
        int intValue2 = intList.get(1); // 형변환이 필요없다

    }
}

decompile

class Test {
    Test() {
    }

    public static void main(String[] var0) {
        GenericArrayList var1 = new GenericArrayList(); // 제네릭이 사라졌다
        var1.add(1);
        var1.add(2);
        int var2 = (Integer)var1.get(0); // 형변환이 추가되었다
        int var3 = (Integer)var1.get(1); // 형변환이 추가되었다
    }
}

* 제네릭을 사용하면 컴파일러가 형변환을 알아서 진행한다는 것을 확인했다.

 

한정적 타입 매개변수

- GenericArrayList 가 Number 의 서브클래스만 타입으로 가지도록 하고 싶은 경우 아래와 같이 제네릭의 타입을 제한할 수 있다.

public class GenericArrayList<T extends Number>

타입인자

https://yaboong.github.io/java/2019/01/19/java-generics-1/

 

자바 제네릭 이해하기 Part 1

개요 제네릭이란? 제네릭을 사용하는 이유 제네릭을 사용할 수 없는 경우 제네릭 메서드란? 제네릭 타입 제한하기 (Bounded Type Parameter)

yaboong.github.io

 

'Spring boot' 카테고리의 다른 글

[Java] JVM과 자바 메모리 구조  (0) 2022.01.22
[Spring] java.lang.IllegalStateException: Failed to load ApplicationContext  (0) 2022.01.21
[Spring]Transaction?  (0) 2022.01.20
[JAVA] Annotation?  (0) 2022.01.20
[JAVA] 빌드 과정  (0) 2022.01.13
Comments