[코틀린 docs] 코틀린의 타입
코틀린의 공식 문서, Basic Types(https://kotlinlang.org/docs/reference/basic-types.html)를 간단히 정리하며 한 번더 생각해볼만한 것을 정리한 기록입니다.
코틀린 primitive type과 NULL
코틀린에서 중요하게 생각하는 철학 중 하나는 NULL이 될 수도 있는 타입과 NULL이 절대 들어가지 않을 타입을 명확히 구분하겠다는 것이다.
또한, 내부적으로 primitive type이 존재하고, 실제 연산을 할 때도 primitive type으로 처리하지만 타입은 모두 객체 타입으로 간주된다.
이게 무슨 말인가 하면
int number = 3;
이라는 자바 코드가 존재한다고 해보자.
이때 int
는 Integer
라는 객체와 명확히 구분되며, primitive type이기 때문에 null이 들어갈 수 없다.
val number: Int = 3
반면 코틀린에서는 Int
라는 타입을 처음 봤을때 엇? 객체 타입은 없나? 라고 생각할 수 있다.
하지만 실제 Int
는 null이 들어갈 수 없으며, null이 들어갈 수도 있는 정수는 Int?
라고 표현해야 한다
var number: Int? = 3
number = null // 에러가 나지 않는다
var number: Int = 3
number = null // 에러 발생, Null can not be value of non-null type int
코틀린의 다양한 타입들
코틀린의 기본적인 타입들을 아래와 같다.
- Byte (UByte)
- Short (UShort)
- Int (UInt)
- Long (ULong)
- Float
- Double
- Char
- Boolean
- String
타입간의 비교
코틀린에는 ===
라는 연산자가 존재하며, 이 연산자는 두 객체의 주소값을 비교하게 된다.
그렇기 때문에 당연히 Boxed Number 의 주소 비교는 false가 나올 수도 true가 나올 수도 있다
val number: Int = 127
val firstBoxedNumber: Int? = b
val secondBoxedNumber: Int? = b
println(firstBoxedNumber === secondBoxedNumber) // ture or false
하지만 흥미로운 한 가지는 -128 ~ 127 사이의 정수에 대해서는 true가 나오고 그 외에 정수에 대해서는 false가 나온다. 내부적으로도 객체 처리가 될텐데 자주 쓰일법한 1byte level 수에 대해서는 singleton 처리를 해둔 것 같다 (Kotlin 1.4버전 기준)
핵심을 '보장하지 않는다'로 생각하면 될 것 같다.
타입간의 연산
흔히 생각하는 사칙연산은 null이 가능한 타입이 있으면 적용되지 않는다.
var number1: Int? = 3
var number2: Int? = 4
println(number1 + number2) // 에러
이를 더해주고 싶으면 ?:
연산자를 적절히 활용해서 null 일 경우에 대한 처리를 모두 해줘야 한다.
확실히 NPE 날 일은 없을 것 같다.
참/거짓 연산자
||
연산자와 &&
연산자는 모두 LAZY 하다.
바꿔 말해, A || B
에서 A가 참이면 B를 확인하지 않고, A && B
에서 A가 거짓이면 B를 확인하지 않는다.
String Templates
무언가 모던(?)한 느낌을 주는 언어에는 모두 존재하는 string templates 기능이다.
val i = 10
println("i = $i") // $i 자리에 10이 들어간다
val s = "abc"
println("$s.length is ${s.length}") // ${ }를 사용하면 expression을 넣을 수 있다
만약 $를 출력하고 싶다면, $
양 옆에 작은 따옴표('
)를 붙이면 된다
println("'$'i9.99") // $i9.99가 출력된다
배열
배열의 생성은 arrayOf
를 통해 만들어진다. []
를 통해 배열 원소에 접근할 수 있으며, Array(size) { i -> i }
를 통해 배열을 만들 수도 있다.
val array1 = arrayOf(1, 2, 3)
array1[0] // 1
array1[1] // 2
val array2 = Array(3) { it * it } // [0, 1, 4]
배열은 Invariant 하다 (Array<String>
이 Array<Any>
타입으로 들어가지 않는다는 뜻)
IntArary
, ByteArray
와 같은 primitive type arrays가 별도로 존재한다