개발 공부 기록하기/01. JAVA & Kotlin

[코틀린 docs] 코틀린의 타입

lannstark 2021. 1. 26. 08:35

코틀린의 공식 문서, Basic Types(https://kotlinlang.org/docs/reference/basic-types.html)를 간단히 정리하며 한 번더 생각해볼만한 것을 정리한 기록입니다.

코틀린 primitive type과 NULL

코틀린에서 중요하게 생각하는 철학 중 하나는 NULL이 될 수도 있는 타입과 NULL이 절대 들어가지 않을 타입을 명확히 구분하겠다는 것이다.

또한, 내부적으로 primitive type이 존재하고, 실제 연산을 할 때도 primitive type으로 처리하지만 타입은 모두 객체 타입으로 간주된다.

이게 무슨 말인가 하면

int number = 3;

이라는 자바 코드가 존재한다고 해보자.

 

이때 intInteger 라는 객체와 명확히 구분되며, 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가 별도로 존재한다