티스토리 뷰

 

공변,무공변,반공변에 대한 개념들은

"타입 S가 T의 하위타입이면 List<S>도 List<T>의 하위타입인가?"에 대한 질문으로 시작한다.

<무공변> 타입 S가 T의 하위 타입일때 -> Box<S>와 Box<T>가 어떤 관계도 아닌 경우

<공변>: 타입 S가 T의 하위 타입일때 -> Box<S>가 Box<T>의 하위타입인 경우

<반공변>: 타입 S가 T의 상위 타입일때 -> Box<S>가 Box<T>의 상위타입인 경우

  • 일반적으론 무공변이다.
    • List<Object>랑 List<String>은 어떤 관계도 없다.
  • 왜그러냐면, 제네릭은 "타입소거"방식으로 동작한다.
    • 타입소거란? 컴파일시에만 타입 검사를 하고 런타임시에는 제네릭의 인스턴스에 대한 타입정보를 갖고 있지 않는 것
    • 예를 들어) List<String>은 런타임시에는 그냥 List로 간주된다. (개발자눈에만 타입이 보인다는뜻)
    • 그래서 is 키워트로 List의 타입 체크를 할 수가 없음
if (value is List<String>) {...}
// ERROR : Cannot check for instance of erased type
  • 공변은 <out T>키워드로, 반공변은 <in T>키워드를 사용
  • 공변(out)
    • 자기자신이나 자식의 클래스를 타입으로 받을 수 있음
  • 반공변(in)
    • 자기자긴이나 부모 클래스를 타입으로 받을 수 있음

 

실 코드의 예

sealed class CommonResult<out T>{
    class Success<T>(val value: T) : CommonResult<T>()
    class Error(val exception: Exception) : CommonResult<Nothing>()
    object Loading : CommonResult<Nothing>()
}

fun <T, R> CommonResult<T>.map(transform: (T) -> R): CommonResult<R> {
    return when (this) {
        is CommonResult.Success -> {
            try {
                CommonResult.Success(transform(value))
            } catch (e: Exception) {
                CommonResult.Error(e)
            }
        }
        else -> {
            CommonResult.Loading
        }
    }
}

out을 빼면 에러남

→CommonResult.Error에서 문제가 발생함

왜냐 CommonResult.Error의 타입은 CommonResult<Nothing>이다.

map함수의 리턴값은 CommonResult<R>이다.

Nothing은 R의 자식이므로 out을 붙여야함

 

내가 헷갈렸던 점

  • CommonResult sealed class에서는 CommonResult.Error의 타입은 CommonResult<Nothing>인것

 

코드

github.com/yoonah5991/study-functional-programming/blob/main/src/%EC%A0%9C%EB%84%A4%EB%A6%AD_%EB%B3%80%EC%84%B1.kt

 

참고

- 코틀린으로 배우는 함수형 프로그래밍 책

medium.com/hongbeomi-dev/kotlin-generics-%EC%8B%A4%EC%B2%B4%ED%99%94%ED%95%9C-%ED%83%80%EC%9E%85-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-cfb2436946e3

'개발 > 코틀린' 카테고리의 다른 글

코틀린 코루틴 내부 동작 이해하기  (0) 2023.12.28