728x90
반응형
✅ 코틀린의 원시 타입
- 이번 절에서는 프로그램에서 사용하는 Int, Boolean, Any 등의 원시 타입에 대해 살펴본다.
- 코틀린은 원시 타입과 래퍼 타입을 구분하지 않는 이유와 코틀린 내부에서 어떻게 원시 타입에 대한 래핑이 작동하는지 살펴본다.
- Object, Void 등의 자바 타입과 코틀린 타입 간의 대응 관계에 대해 살펴본다.
📌 원시 타입 : Int, Boolean 등
- 자바는 원시 타입과 참조 타입을 구분한다.
- 원시 타입의 변수에는 그 값이 직접 들어가지만 참조 타입의 변수에는 메모리상의 객체 위치가 들어간다.
- 코틀린은 원시 타입과 참조 타입을 구분하지 않아 항상 같은 타입을 사용한다.
- 자바에서는 원시 타입이 있지만 참조 타입이 필요한 경우 특별한 래퍼 타입으로 (ex. Integer) 원시 타입 값을 감싸서 사용한다.
- 하지만 코틀린은 Integer와 int 구분 없이 int 하나만 사용하게 된다.
val i : Int = 1
val list : List<Int> = listOf(1, 2, 3)
- 원시 타입과 참조 타입이 같다면 코틀린이 항상 타입을 객체로 표현하는 걸까라는 의문이 들 수 있다.
- 타입을 항상 객체로 표현하게 된다면 비효율적이겠지만 코틀린은 그러지 않는다.
- 코틀린은 컴파일 시 타입을 가능한 효율적인 방식으로 표현한다.
- 예를 들어 보통의 경우 코틀린의 Int 타입은 자바의 int 타입으로 컴파일된다.
- 하지만 컬렉션이나 제네릭 클래스를 사용하는 경우에는 참조 타입으로 변경되어 컴파일된다.
- Int 타입의 컬렉션 -> java.lang.Integer
📌 널이 될 수 있는 원시 타입 : Int?, Boolean? 등
- null 참조를 자바의 참조 타입의 변수에만 대입할 수 있기 때문에 널이 될 수 있는 코틀린 타입은 자바 원시 타입으로 표현할 수 없다.
- 따라서 코틀린에서 널이 될 수 있는 원시 타입을 사용하면 그 타입은 자바의 래퍼 타입으로 컴파일된다.
/* 널이 될 수 있는 원시 타입 */
data class Person(val name: String, val age: Int? = null) {
fun isOlderThan(other : Person) : Boolean? {
if(age == null || other.age == null)
return null
return age > other.age
}
}
println(Person("Sam", 35).isOlderThan(Person("Amy", 42)))
// false
println(Person("Sam", 35).isOlderThan(Person("Jane")))
// null
- 위 예제의 Person 클래스에 선언된 age 프로퍼티의 경우 java.lang.Integer로 저장된다.
- 또한 앞에서 제네릭 클래스의 경우 래퍼 타입을 사용한다고 했다.
- 이렇게 컴파일 하는 이유는 자바 가상머신에 제네릭을 구현하는 방법 때문이다.
- JVM은 타입 인자로 원시 타입을 허용하지 않기 때문에 자바나 코틀린 모두 제네릭 클래스는 항상 박스 타입을 사용해야 한다.
📌 숫자 변환
- 코틀린과 자바의 가장 큰 차이점 중 하나는 숫자를 변환하는 방식이다.
- 코틀린은 한 타입의 숫자를 다른 타입의 숫자로 자동 변환하지 않는다.
- 결과 타입이 허용하는 숫자의 범위가 원래 타입의 범위보다 넓은 경우조차도 자동 변환이 불가능하다.
val i = 1
val l: Long = i // "Error : type mismatch" 컴파일 오류 발생
- 그러나 직접 변환 메소드를 호출하면 형변환이 가능하다.
val i = 1
val l : Long = i.toLong()
- 이처럼 코틀린은 모든 원시 타입에 대한 변환 함수를 제공한다.
- toByte(), toShort(), toChar() 등등
- 양방향 변환 함수도 제공
- 즉 어떤 타입을 더 표현 범위가 넓은 타입으로 변환하는 함수(ex. Int.toLong())도 있고 타입을 범위가 더 좁은 타입으로 변환하면서 값을 벗어나면 일부를 잘라내는 함수(ex. Long.toInt())도 있다.
❗ 문자열을 숫자로 변환하기
- 코틀린 표준 라이브러리는 문자열을 원시 타입으로 변환하는 여러 함수를 제공한다.
- toInt, toByte, toBoolean
- 하지만 이런 함수는 문자열의 내용을 각 원시 타입을 표기하는 문자열로 파싱하는데 파싱에 실패하면 NumberFormatException이 발생한다.
"613".toInt()
// 613
"dahoon613".toInt()
// NumberFormatException 발생
📌 Any, Any? : 최상위 타입
- 자바에서 Object가 클래스 계층의 최상위 타입이듯 코틀린에서는 Any 타입이 모든 널이 될 수 없는 타입의 조상 타입이다.
- 여기서 유의할 점은 Any가 널이 될 수 없는 타입이라는 것이다.
- 따라서 Any 타입의 변수에는 null이 들어갈 수 없으며 널을 포함한 모든 값을 대입할 변수를 선언하려면 Any? 타입을 사용해야 한다.
- 코틀린 함수가 Any를 사용하면 자바 바이트코드의 Object로 컴파일된다.
📌Unit 타입 : 코틀린의 void
- 코틀린 Unit 타입은 자바 void와 같은 기능을 한다.
- 리턴 값이 없는 함수의 반환 타입으로 Unit을 쓸 수 있다.
fun f() : Unit { ... }
fun t() { ... } // 반환 타입 생략
- 하지만 void와 다르게 Unit은 차별점이 하나 있다.
- Unit은 모든 기능을 갖는 일반적인 타입이며, void와 달리 Unit을 타입 인자로 쓸 수 있다.
- Unit 타입에 속한 값은 단 하나뿐이며, 그 이름도 Unit이다.
- Unit 타입의 함수는 Unit 값을 묵시적으로 반환한다.
- 이 두 특성은 제네릭 파라미터를 반환하는 함수를 오버라이드하면서 반환 타입으로 Unit을 쓸 때 유용하다.
interface Processor<T> {
fun process() : T
}
class NoResultProcessor : Processor<Unit> {
override fun process() { // Unit을 반환하지만 타입을 지정할 필요는 없다.
// 업무 처리 코드
} // 여기서 return을 명시할 필요가 없다. -> 컴파일러가 묵시적으로 return Unit을 넣어준다.
}
📌 Nothing 타입 : 이 함수는 결코 정상적으로 끝나지 않는다
- 특정 함수가 정상적으로 끝나지 않는다는 사실에 대해 분석하고 싶은 경우 Nothing이라는 특별한 반환 타입을 활용한다.
fun fail(message : String) : Nothing {
throw IllegalStateException(message)
}
fail("Error occurred")
// java.lang.IllegalStateException: Error occurred
- Nothing은 아무 값도 포함하지 않는다.
- 따라서 Nothing은 함수의 반환 타입이나 반환 타입으로 쓰일 타입 파라미터로만 쓸 수 있다.
- 그 외의 다른 용도로 사용하는 경우 아무 값도 저장할 수 없기 때문에 의미가 없게 된다.
- Nothing을 반환하는 함수를 엘비스 연산자의 우항에 사용해 전제 조건을 검사할 수 있다.
val address = company.address ?: fail("No Address")
println(address.city)
- Nothing이 반환 타입인 함수가 결코 정상 종료되지 않음을 알고 그 함수를 호출하는 코드를 분석할 때 사용하며 널인지 아닌지 추론이 가능하다.
728x90
반응형
'Studying > Kotlin' 카테고리의 다른 글
[Kotlin In Action] 7장. 연산자 오버로딩과 기타 관례(1) - 산술 연산자 오버로딩 (0) | 2023.06.02 |
---|---|
[Kotlin In Action] 6장. 코틀린 타입 시스템(4) - 컬렉션과 배열 (0) | 2023.06.01 |
[Kotlin In Action] 6장. 코틀린 타입 시스템(2) - 널 가능성[2] (0) | 2023.05.30 |
[Kotlin In Aciton] 6장. 코틀린 타입 시스템(1) - 널 가능성[1] (0) | 2023.05.25 |
[Kotlin In Action] 5장. 람다로 프로그래밍(5) - 수신 객체 지정 람다 : with와 apply (0) | 2023.05.23 |