본문 바로가기

Dev Book/Kotlin IN ACTION

CH6(6.2). 코틀린의 원시 타입

1. 원시 타입: Int, Boolean 등

- 원시 타입의 변수에는 그 값이 직접 들어가지만, 참조 타입의 변수에는 메모리상의 객체의 위치가 들어간다. 자바는 참조 타입이 필요한 경우 특별한 래퍼 타입으로 원시타입 값을 감싸서 사용한다. 원시 타입의 값에 대해 메소드를 호출하거나 컬렉션에 담을 수 없기때문에 Collection<Integer> 등으로 래퍼 타입으로 감싸서 사용한다. 코틀린은 원시 타입과 래퍼 타입을 구분하지 않으므로 항상 같은 타입을 사용한다.

- 코틀린에서는 숫자 타입 등 원시 타입의 값에 대해 메소드를 호출할 수 있다.

 

fun showProgress(progress: Int){
    val percent = progress.coerceIn(0, 100)
    println("We are $percent% done!")
}

 

- 코틀린의 Int 타입은 자바 int 타입으로 컴파일 된다. 이런 컴파일이 불가능한 경우는 컬렉션과 같은 제네릭 클래스를사용하는 경우 뿐이다. Int와 같은 코틀린 타입에는 널 참조가 들어갈 수 없으므로 이에 상응하는 자바 원시 타입으로 컴파일 되며, 반대로 자바 원시 타입의 값 역시 절대 널이 될 수 없으므로 코틀린에서 플랫폼 타입이 아닌 널이 될 수 없는 타입으로 취급된다.

2. 널이 될 수 있는 원시 타입: Int?, Boolean? 등

- 널 참조를 자바의 참조 타입의 변수에만 대입할 수 있기 때문에 널이 될 수 있는 코틀린 타입은 자바 원시 타입으로 표현할 수 없다. 따라서 코틀린에서 널이 될 수 있는원시 타입을 사용하면 자바의 래퍼 타입으로 컴파일 된다.

 

data class Person(val name:String, val age:Int?=null){
    fun isOrderThan(other:Person): Boolean?{
        if( age==null || other.age==null ) //age는 자바에서 java.lang.Integer로 저장된다.
            return null
        return age > other.age
    }
}

 

- 제네릭 클래스의 경우 래퍼 타입을 사용한다. JVM은 타입 인자로 원시 타입을 허용하지 않는다. 따라서 자바나 코틀린 모두에서 제네릭 클래스는 항상 박스 타입을 사용해야 한다.

3. 숫자 변환

- 코틀린은 한 타입의 숫자를 다른 타입의 숫자로 자동 변환하지 않는다. 대신 직접 변환 메소드를 호출해야 한다. 코틀린은 모든 원시 타입에 대한 변환 함수를 제공한다. 

- 코틀린에서는 타입을 명시적으로 변환해서 같은 타입의 값으로 만든 후 비교해야 한다.

 

val x = 1
    println(x.toLong() in listOf(1L, 2L, 3L)) //명시적 변환을 해줘야 true. 묵시적 변환은 false.

 

- 숫자 리터럴을 사용할 때는 보통 변환 함수를 호출할 필요가 없다. 또한 산술 연산자는 적당한 타입의 값을 받아들일 수 있게 오버로드돼 있다.

4. Any, Any?: 최상위 타입

- 코틀린에서는 Any 타입이 모든 널이 될 수 없는 타입의 조상 타입이다. 자바에서는 참조 타입만 Object를 정점으로 하는 타입 계층에 포함되며, 원시 타입은 그런 계층에 들어있지 않은 반면, 코틀린에서는 Any가 Int 등의 원시 타입을 포함한 모든 타입의 조상 타입이다. 

- Any는 널이 될 수 없는 타입이며, 널을 포함하는 모든 값을 대입할 변수의 선언에는 Any? 타입을 사용해야 한다. 내부에서 Any 타입은 자바의 java.lang.Object에 대응한다.(널이 될 수 있는지 없는지 여부를 알 수 없으므로 플랫폼 타입인 Any!로 취급)

5. Unit 타입: 코틀린의 void

- 코틀린 Unit 타입은 자바 void와 같은 기능을 한다. Unit을 반환타입으로 쓰는 함수는 반환 타입 선언 없이 정의한 블록이 본문인 함수와 같다.

- Unit이 void와 다른 점은, Unit은 모든 기능을 갖는 일반적인 타입이며, void와 달리 Unit을 타입 인자로 쓸 수 있다. Unit 타입에 속한 값을 단 하나뿐이며, 그 이름도 Unit이다. Unit 타입의 함수는 Unit값을 묵시적으로 반환한다.

 

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

class NoResultProcessor: Processor<Unit>{
	override fun process(){ //Unit을 반환하지만 타입을 지정할 필요는 없다.
    	//... 
        //return을 명시할 필요가 없다.
    }
}

 

명시적으로 Unit을 반환하지 않아도 컴파일러가 묵시적으로 return Unit을 넣는다.

6. Nothing 타입: 이 함수는 결코 정상적으로 끝나지 않는다.

- 테스트 라이브러리에 특별한 메시지가 들어있는 예외를 던져서 현재 테스트를 실패시키는 fail 함수나, 무한루프를 도는 함수는 결코 값을 반환하며 정상적으로 끝나지 않는다. 이런 함수를 호출하는 코드를 분석하는 경우 함수가 정상적으로 끝나지 않는다는 사실을 알면 유용하다. 그런 경우를 표현하기 위해 코틀린에서는 Nothing이라는 특별한 반환 타입이 있다.

 

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

>>>fail("Error occurred")
java.lang.IllegalArgumentException: Error occurred

 

- Nothing 타입은 아무 값도 포함하지 않는다. 따라서 Nothing은 함수의 반환 타입이나 반환 타입으로 쓰인 타입 파라미터로만 쓸 수 있다.

'Dev Book > Kotlin IN ACTION' 카테고리의 다른 글

CH7(7.1). 산술 연산자 오버로딩  (0) 2022.02.13
CH6(6.3). 컬렉션과 배열  (0) 2022.02.06
CH6(6.1-2). 널 가능성  (0) 2022.01.20
CH6(6.1-1). 널 가능성  (0) 2022.01.16
CH5(5.5). 수신 객체 지정 람다  (0) 2022.01.08