코틀린 타입 시스템(2)

tkppp·2021년 12월 26일
0

Kotlin

목록 보기
7/11

코틀린의 원시 타입

원시 타입 - Int, Boolean 등

자바에서는 원시 타입과 객체 타입을 구분한다. 원시 타입에 객체 타입의 메소드를 사용할 경우 자동 박싱을 통해 객체 타입으로 변환한 후 사용된다.

자바의 경우 원시 타입과 객체 타입을 구분하므로 제네릭을 사용할 때 원시타입의 사용이 불가능하다. Collection<int> 가 아닌 Collection<Integer>를 사용해야 한다. 코틀린은 이런 원시 타입과 객체 타입을 구분하지 않으므로 항상 같은 타입을 사용한다.

그렇다면 원시 타입과 참조 타입을 구분하지 않는다는 것은 사실 리터럴만 원시 타입일뿐 객체로 취급되는 것이 아닌가? 라는 의문을 가질 수 있다. 하지만 코틀린도 두 타입을 구분하기는 한다. 이를 컴파일 타임에 적절하게 구분한다. 변수, 파라미터, 반환 타입의 Int는 대부분 int 타입으로 컴파일 된다. 하지만 제네릭이나 컬렉션을 사용하는 경우 Integer로 컴파일 된다.

코틀린 원시 타입

  • 정수 타입 : Byte, Short, Int, Long
  • 부동소수점 타입 : Float, Double
  • 문자 타입 : Char
  • 불리언 타입 : Boolean

코틀린의 원시 타입에는 널 참조가 들어갈 수 없기 대문에 쉽게 그에 상응하는 원시 타입으로 컴파일 할 수 있다. 반대로 자바 원시 타입을 코틀린에서 사용할 때는 플랫폼 타입이 아닌 널이 될 수 없는 타입으로 취급된다.

널이 될 수 있는 원시 타입 - Int?, Boolean? 등

널 참조는 자바의 참조 타입 변수에만 대입할 수 있으므로 원시 타입은 Nullable 할 수 없다. 따라서 널이 될 수 있는 원시 타입을 사용하면 자바의 래퍼 타입으로 컴파일 된다.

숫자 변환

코틀린에서는 자바와 같이 숫자 타입을 자동으로 변환하지 않는다. 결과 타입이 허용하는 숙자의 범위가 원래 타입보다 넓은 경우에도 불가하다.

val i = 1
val l: Long = i	// 컴파일 에러

같은 정수 타입이지만 변환이 불가하다. 변환하고 싶다면 toType() 메소드로 변환해주어야 한다.

val i = 1
val l: Long = i.toLong()

숫자 타입의 비교도 마찬가지다

val x = 1
val list = listOf(1L, 2L, 3L)

x in list	// false

타입 추론을 이용하더라도 숫자 리터럴을 통해 타입을 명시하지 않아도 된다.

val l = 1L	// Long
val f = 1.0f	// Float

Any, Any? - 최상위 타입

자바의 Object 클래스는 모든 참조 타입의 조상이다. 즉 원시 타입은 Object 클래스의 메소드를 사용할 수 없다는 것이다. 이를 사용하기 위해서 박싱을 통해 원시 타입을 객체 타입으로 변환하여 사용한다.

코틀린은 Any가 널이 될 수 없는 모든 타입의 조상이며 이는 원시 타입도 해당된다. 그렇기 때문에 코틀린의 모든 클래스에는 toString, equals, hashCode 메소드가 들어있다. Any 클래스를 상속했기 때문이다. 하지만 Object 클래스의 다른 메소드는 존재하지 않는다. 다른 메소드를 사용하고 싶다면 Object 클래스로 형변환 후 사용해야 한다.

Unit - 코틀린의 void

코틀린은 void 대신 Unit 타입을 사용한다. 대부분의 상황에서 void와 Unit은 큰 차이가 없다.

차이점은 void와 달리 Unit을 타입 인자로 사용할 수 있다는 것이다. Unit 클래스에 속한 값은 단 하나이며 그 이름은 Unit이다. Unit 타입의 함수는 Unit 값을 묵시적으로 반환한다. 따라서 타입 파라미터에 Unit을 사용할 수 있다.

interface Processor<T>{
    fun process() : T
}

class NoResultProcessor : Processor<Unit> {
	// Unit을 제네릭 타입 파라미터로 썻기 때문에 반환 값이 필요하지 않다.
    override fun process(){
        ...
    }
}

Nothing - 정상적이지 않는 종료

결코 성공적으로 값을 돌려주지 않는 반환값이라는 개념 자체가 없는 함수가 있다. Unit 타입은 void로 취급되긴 하지만 묵시적으로 Unit 값을 반환한다. 정상적이지 않은 종료를 위한 함수가 존재한다. 예를 들어 테스트 라이브러리는 fail 함수를 제공하는 경우가 많은데 fail 함수는 특별한 메세지가 들어있는 예외를 던져서 현재 테스트를 실패시킨다. 그런 함수를 분석하는 경우 함수가 정상적으로 종료되지 않는다는 사실을 알면 유용하다

fun fail(message: String) : Nothing {
    throw IllegalStateException(message)
}

val address = company.address ?: fail("No Address")

Nothing은 아무것도 반환하지 않기 때문에 타입 파라미터나 반환 타입으로만 사용할 수 있다. 위의 코드에서 fail을 엘비스 연산자의 우항에서 예외가 발생한다는 사실을 컴파일러가 인식하고 address가 널이 될 수 없는 타입임을 추론할 수 있다.

0개의 댓글