Learn business/Java

[4편] 제네릭이란?


제네릭은 Overloading 이 가능할까? Type Erasure에 대해서 알아보자.

 

한 번쯤은 제네릭 메소드를 Overloading 해서 메소드를 구현하고 싶은 생각을 해봤을 것이다.(물론, 아닐 수도 있다.) 제네릭은 Overloading 이 가능할까? 결론부터 말하면 제네릭 메소드는 Overloading 이 불가능하다. 이번 포스팅에서는 왜 Overloading 이 안되는지? 왜 제네릭과 와일드카드를 혼합해서 쓰는지? 를 알아보자.

 

아래와 같이 제네릭 오버로딩을 해보자

두 outBox 메소드의 매개변수 타입은 분명 서로 다른 것을 알 수 있다. 첫 번째 메소드는 Toy 타입을 상한 제한 하였으며, 두 번째 메소드는 Robot 타입을 상한 제한 하였다.  분명 두 메소드는 서로 다른 타입을 매개변수로 받고 있다. 그러므로 Overloading 이 될 것이라고 생각할 수 있을 것 같다. 다음 예시를 살펴보자.

마찬가지로 두 inBox 메소드 역시 서로 다른 타입의 매개변수를 받고 있으므로 Overloading 이 되었다고 할 수 있다. 첫 번째 메소드는 Toy 타입을 하한 제한하는 매개변수와 Toy 타입의 매개변수를 받고 있으며, 두 번째 메소드는 Robot 타입을 하한 제한하는 매개변수와 Robot 타입의 매개변수를 받고 있다. 

 

참고로 Overloading 조건은 아래와 같다.

1. 메서드 이름이 같아야 한다.

2. 매개변수의 개수가 달라야 한다.

3. 매개변수의 타입이 달라야 한다.

 

결론은 첫 번째 예시 코드인 outBox 메소드들은 첫 번째 매개변수[Box<? extend Toy> box, Box<? extend Robot> box] 에 의해서 Overloading 이 안되며, 두 번째 예시 코드인 inBox 메소드들은 첫 번째 매개변수[Box<? super Toy> box, Box<? super Robot> box] 에 의해서 Overloading 이 성립되지 않지만 두 번째 매개변수[Toy toy, Robot robot] 에 의해서 Overloading 이 성립된다. 왜 그럴까?

 

자바의 변화 제네릭의 등장

자바 1.5 버전부터 등장한 제네릭을 기준으로 자바를 나누어 볼 수 있다. 새로운 문법이 나오고 난 이후 언어적 측면에서 봤을 때 그 이전 언어의 문법에 구애받지 않고(하위 호환하지 않고) 새로운 언어라고 하면서 사용자들에게 지금부터 이거 사용해! 라고하면 가장 깔끔할 것이다. 하지만 그렇게 언어 체계를 바꿔버리면 컴파일러도 바꿔야하며, 기존에 만들어 놓은 코드가 동작하지 않을 수 있다. 그래서 자바 개발자들은 기존 코드도 동작을 하도록 만들어야 하며, 새로운 기능을 추가하여 언어적 추세에 맞도록 발전시켜나가야 한다. 

 

Generic Type Erasure

자바 개발자들은 이러한 하위 호환성을 위해서 Type Erasure 라는 개념을 도입하였다. 자바 1.5 버전 이하에서는 다이아몬드 연산자(<>) 라는 것이 없었다. 그래서 자바 컴파일러는 1.5 이전과 이후 버전의 상호 호환성을 위해서 다이아몬드 연산자를 지워버린다. 위 예시에 outBox 메소드를 보면 첫 번째 매개변수[Box<? extend Toy> box, Box<? extend Robot> box] 를 지워버리는 것이다. (정확히 지워버린다는 표현보다는 1.5 이전 버전도 이해할 수 있는 형태로 변경한다는 것이 맞는 표현이다.)

 

그럼 다시 예제 코드를 확인해 보자. 자바 컴파일러의 Type Erasure 때문에 아래와 같은 코드로 컴파일이 됨을 알 수 있다.

 Type Erasure 때문에 메소드 선언이 똑같아져 버리니 Overloading 이 성립이 안되는 것이다.  아래 사진을 통해서 컴파일러는 Type Erasure와 관련된 에러를 노출한다.

Type Erasure Error

 

그렇다면 방법이 없을까?

물론 자바에서 아주 멋진 대응책을 제시한다. 바로 와일드 카드와 제네릭의 혼합이다. 우리는 위 예시에서 보았듯이, 매개변수에 Toy 타입도 넣고 싶고, Robot 타입도 넣고 싶다. 그렇다. 그 타입을 제네릭으로 바꿔주면 그만이다. 아래 처럼 말이다.

이전 코드에서는 와일드 카드 기반의 메소드 형태였다면, Overloading 을 가능하게 함으로써 일드 카드 기반의 메소드인 동시에 제네릭 기반의 메소드가 되었다. 결국 이번 포스팅을 통해 제네릭과 와일드 카드가 합쳐졌을때 갖는 확장성은 Overloading 임을 알 수 있다. 위 예시의 선언이 생소하다고 느껴졌을 수 있는데, 왜 저런 형태가 나왔는지 지금까지의 설명을 들어보니 많은 부분 수긍이 될 것이다. 최종적으로 지금까지의 내용을 정리해서 위 코드를 해석해 본다면, 다음과 같다.

1. Box<? extends T> 에서 "extends T"을 통해 꺼내는 것만 가능하다.

2. Box<? extends T> 에서 Box<T> 타입으로 줄여 생각할 수 있으며, Box<T> 타입으로 선언된 어떠한 인스턴스든 올 수 있다.

 

이제 컬렉션 프레임 워크의 선언문을 이해하는데 준비는 끝났다. 다음 글에서 아래와 같은 코드가 어떻게 왜 저렇게 선언될 수 밖에 없는지 포스팅하도록 하겠다.

'Learn business > Java' 카테고리의 다른 글

자바 클래스패스  (1) 2020.03.31
[5편] 제네릭이란?  (3) 2020.01.05
[3편] 제네릭이란?  (1) 2019.11.02
[2편] 제네릭이란?  (0) 2019.11.02
[1편] 제네릭이란?  (0) 2019.10.31