본문 바로가기

JVM

코틀린 제네릭에 대해 정리 해보자

요즘 코틀린에 대해 기본 개념들을 다시 정리 해보고 있다.

특히 제네릭 쪽이 매번 보때마다 헷갈리고 어렵다 ... 한번 쉽게 풀어서 적어보려고한다.

 

일단 제네릭에 대해서 알기 전에

공변과 무공변의 개념부터 정리하고 가보자!

 

공변이란?

위와 같이 할인(Discount) 이라는 상위 클래스에 쿠폰(Coupon) 이라는 하위클래스가 존재할때

쿠폰(Coupon) 할인(Discount) 의 타입이 될수 있다

 

대표적으로 자바의 배열은 공변이다

String[] strs = new String[]{"userA", "userB", "userC"}
Object[] objts = strs;
object[0] = 2

 

하지만 위 코드의 경우 런타임시에 오류가 발생하게 된다 ( 공변의 치명적인 단점 ... )

 

 

무공변이란?

할인(Discount) 쿠폰(Coupon) 이 상속관계를 갖고 있다고 하더라고 계층관계를 갖을 수 없는 것을 

무공변이라고 한다.

 

대표적인 예시로 자바의 리스트가 존재한다.

리스트의 경우 제네릭을 사용하여 타입의 안정성을 높히는데, 제네릭의 경우 무공변이기에 컴파일 오류가 발생하게된다

List<Discount> discountList = new ArrayList<Coupon>(); // 타입 불일치 컴파일 오류발생

Q. 그럼  리스트처럼 제네릭을 사용할 때 는 상속관계를 갖을 수 없는것인가...?

 

그건아니다!  갖게 끔 할 수 있다.

우리는 제네릭 클래스간의 상속관계를 줬냐 안줬냐를 어려운말로 변성을 줬냐 안줬냐라고 말을한다.

 

그럼 예시를 통해 변성을 주는 방법에 대해서 알아보자

코드를 간단하게 설명하자면

Mart 라는 클래스가 있고, Carts 에는 여러가지 상품들 을 담을 수 있다.

그리고 Cart 에 담은 상품을 다른 Cart로 옮길 수 있는 코드이다

 

한번 Cart를 옮겨보자

Apple의 경우 Food의 하위타입이다.

제네릭클래스는 무공변 이기 때문에  AppleFood가 상속관계를 갖더라도, 컴파일 오류가 발생하게 된다.

 

코틀린에서는 이런 상황에서 변성을 주기 위해 out 이라는 키워드를 제공한다.

 

chageCart에 out 이라는 키워드를 제네릭 앞에 달고 있으니 컴파일 오류가 발생하지 않는다.

이렇게 out 을 이용하여 제네릭 간의 상속관계를 허용한 경우를 공변하다 라고 부른다.

 

out을 이용하여 공변하게 만든경우에는 데이터를 꺼내는 기능만 수행이가능하다

 

이와 대조되는 기능은 in 이 있다.

in의 경우 데이터를 넣는 기능만 수행가능하다.

out 데이터 조회만 가능 공변하다
in 데이터 삽입만 가능 반공변하다

 

outin을 우리는 어려운 말로 변성 어노테이션이라고 부른다


Q. 메소드에만 공변 or 반공변 하게 할 수 있는가?

 

그건 아니다! 코틀린에서는 클래스 전체에도 줄 수 있다.

 

클래스 옆에 out 을 붙히게 되면 클래스 전체가 공변을 갖게 된다.

이 말을 어렵게 표현하자면 선언 지점 변성이라고한다.

 

그럼 메소드에 변성을 주는것은 ?

사용 지점 변성이라고 부른다. (너무 용어들을 부르는 말이 어렵다 ....)


Q. 제네릭의 타입을 제한 하고 싶다 이럴땐 코틀린에서 어떻게 할까?

위 와 같이 제네릭 타입 옆에 제한하고자 하는 타입을 붙히면 된다.

 

우리는 어려운말로 제네릭 제약이라고한다.

 

여러개의 타입을 제약하고 싶은 경우에는 where를 이용하여 제약을 줄 수 도있다.