Kotlin 공식 문서 살펴보기 (5) - Basic types

숫자

정수

type size(bits) 최소값 최대값
Byte 8 -128 127
Short 16 -32768 32767
Int 32 -2,147,483,648(-2^31) 2,147,483,647( 2^31 -1)
Long 64 -9,223,372,036,854,775,808 (-2^63) 9,223,372,036,854,775,807 (2^63 - 1)

Int의 최대값을 초과하지 않는 정수 값으로 초기화된 모든 변수는 추론된 Int타입을 가진다. 만약 초기화 값이 이 값을 초과하면 타입은 Long이 된다. Long타입을 명화갛게 하고 싶을 때는 L 접미사를 붙이도록 한다.

val one = 1 // Int
val threeBillion = 3000000000 // Long
val oneLong = 1L // Long
val oneByte: Byte = 1

부동 소수점

부동 소수점 타입으로 FloatDouble 가 있다. IEEE 754에 따르면 부동 소수점 타입은 소수 자릿수에 따라 다르다. Float은 단정밀도를, Double은 배정밀도에 해당한다.

type size(bits) 가수부 지수부 10진수
Float 32 24 8 6-7
Double 64 53 11 15-16

DoubleFloat은 소수가 있는 숫자로 초기화 할 수 있으며 .으로 구분된다. 컴파일러는 Double 타입으로 추론한다.

val pi = 3.14 // Double
// val one: Double = 1 // Error
val oneDouble = 1.0 // Double

Float 타입을 명시하고 싶을 때는 f 또는 F 접미사를 붙인다. 만약 그 값에 6-7자리 이상의 10진수가 포함되면 반올림된다.

val e = 2.7182818284 // Double
val eFloat = 2.7182818284f // Float, 실제 값은 2.7182817

Kotlin에서는 다른 언어와 다르게 숫자에 대해 암시적으로 변환해주지 않는다.

fun main() {
    fun printDouble(d: Double) { print(d) }
    
    val i = 1
    val d = 1.0
    val f = 1.0f
    
    printDouble(d)
    // printDouble(i) // Error: Type mismatch
    // printDouble(f) // Error: Type mismatch
}

리터럴 상수

  • 10진수: 123

  • Long 타입: 123L

  • 16진수: 0x0F

  • 바이너리: 0b00001011

  • Double 타입: 123.4, 123.5e10

  • Float 타입: 123.4f

  • _ 사용 가능

      val oneMillion = 1_000_000
      val creditCardNumber = 1234_5678_1352_3823L
      val socialSecurityNumber = 999_99_9999L
      val hexBytes = 0xFF_EC_DE_5E
      val bytes = 0b11010010_01101001_10010100_10010010
    

JVM 숫자 표현

JVM에서는 숫자는 원시형으로 저장되지만, Int? 같은 nullable한 숫자 참조를 생성하거나 제네릭을 사용할 때는 예외이다. 이런 경우에는 Integer, Double 등의 Java 클래스에 박싱된다.

val a: Int = 100
val boxedA: Int? = a
val anotherBoxedA: Int? = a

val b: Int = 10000
val boxedB: Int? = b
val anotherBoxedB: Int? = b

println(boxedA === anotherBoxedA) // true
println(boxedB === anotherBoxedB) // false

위의 예제에서 a에 대한 모든 nullable 참조는 같은 객체이다. 이는 -128 ~ 127 사이의 Integer 에 JVM이 적용하는 메모리 최적화 때문이다. b에는 적용되지 않으므로 모두 다른 객체이다.

val b: Int = 10000
val boxedB: Int? = b
val anotherBoxedB: Int? = b

println(boxedB == anotherBoxedB) // true

명시적 변환

모든 숫자형 타입은 다른 타입으로 변환할 수 있다.

  • toByte()
  • toShort()
  • toInt()
  • toLong()
  • toFloat()
  • toDouble()
  • toChar()

연산

+, -, *, /, %의 표준 산술 연산을 제공하며 사용자 정의 클래스에서 오버라이드 할 수 있다.

println(1 + 2)
println(2_500_000_000L - 1L)
println(3.14 * 2.71)
println(10.0 / 3)
  • 나눗셈

    • 정수의 나눗셈에서 소수는 버려진다.
      val x = 5 / 2
      println(x == 2) // true
        
      val y = 5L / 2
      println(y == 2L) // true
    
    • 부동 소수점 타입을 반환하기 위해서는 인수 중 하나를 부동 소수점 타입으로 명시적으로 변환해야 한다.
      val x = 5 / 2.toDouble()
      print(x == 2.5) // true
    
  • 비트 연산

    • 정수에서 비트 연산 제공
    • 바이너리 레벨에서 작용
    • Int, Long에만 적용
      val x = (1 shl 2) and 0x000FF000
    
    • shl(bits) : 부호있는 왼쪽 시프트
    • shr(bits) : 부호있는 오른쪽 시프트
    • ushr(bits) : 부호없는 오른쪽 시프트
    • and(bits) : 비트 and
    • or(bits) : 비트 or
    • xor(bits) : 비트 xor
    • inv() : 비트 반전
  • 부동소수점 비교
    • a == b & a != b
    • a < b, a > b, a <= b, a >= b
    • 범위 인스턴스화 & 범위 확인: a..b, x in a..b, x !in a..b
  • 부호 없는 정수
    • UByte: 0 ~ 255 사이의 부호 없는 8비트 정수
    • UShort: 0 ~ 65535 사이의 부호 없는 16비트 정수
    • UInt: 0 ~ 2^32 - 1 사이의 부호 없는 32비트 정수
    • ULong: 0 ~ 2^64 -1 사이의 부호 없는 64비트 정수
  • 부호 없는 배열 및 범위
    • UByteArray : 부호 없는 byte 배열
    • UShortArray : 부호 없는 short 배열
    • UIntArray : 부호 없는 int 배열
    • ULongArray : 부호 없는 long 배열
  • Literal

    • 부호 없는 정수를 더 쉽게 사용하기 위해 정수 리터럴에 태그 지정
    • u, U, uL, UL 로 부호 없는 리터럴 태그
      val b: UByte = 1u
      val s: UShort = 1u
      val l: ULong = 1u
        
      val a1 = 42u
      val a2 = 0xFFFF_FFFF_FFFFu
        
      val a = 1UL
    

Boolean

  • true, false 값을 가짐
  • Nullable 을 대응할 수 있는 Boolean? 제공
  • 빌트인 연산자
    • || : 논리적 OR
    • && : 논리적 AND
    • ! : 논리적 NOT
  • ||&&은 느리게 동작(work lazily)
val myTrue: Boolean = true
val myFalse: Boolean = false
val boolNull:  Boolean? = null

println(myTrue || myFalse)	// true
println(myTrue && myFalse)	// false
println(!myTrue)			// fasle

Characters

  • Char로 표현
  • 작은따옴표(') 사용
  • 특수 문자는 \로 시작
  • 다른 문자를 인코딩 할 때는 유니코드 이스케이스 시퀀스 사용
val aChar: Char = 'a'

println(aChar)
println('\n')
println('\uFF00')

문자열

  • 쌍따옴표(") 사용
  • 문자열의 요소는 인덱스로 접근 가능 s[i]
  • for loop로 문자 반복 가능
for (c in str) {
    println(c)
}
  • 문자열은 변경할 수 없다(immutable)
  • 초기화하면 값을 바꾸거나 새 값을 할당할 수 없음
  • 문자열을 변환하는 모든 작업은 새 String 객체를 반환하고 원래 문자열을 변하지 않게 함
val str = "abcd"
println(str.uppercase())	// ABCD
println(str)				// abcd
  • 문자열을 연결할 때는 + 사용
  • 첫번째 요소가 문자열이면 다른 타입의 값과 연결 가능
val s = "abc" + 1
println(s + "def")  // abc1def

문자열 리터럴

  • 이스케이프 된 문자열: 이스케이프 문자열 포함
val s = "Hello, world!\n"
  • 원시 문자열: 개행문자와 임의의 텍스트 포함
val text = """
	for (c in "foo")
		print(c)
"""
  • 공백을 제거할 때는 trimMargin() 사용
    • |는 여백 접두사로 사용되지만 다른 문자열을 선택할 때는 trimMargin()에 매개변수로 전달 가능
      val text = """
        |Tell me and I forget.
        |Teach me and I remember.
        |Involve me and I learn.
        |(Benjamin Franklin)
      """.trimMargin()
      

문자열 템플릿

  • 평가되고 문자열에 연결된 코드 조각
  • $로 시작
val i = 10
println("i = $i")  // i = 10
  • 중괄호에 표현식이 들어갈 수 있음
val s = "abc"
println("$s.length is ${s.length}") // abc.length is 3
  • 원시 문자열과 이스케이프 문자열 모두 사용 가능
val price = """
${'$'}_9.99
"""

// $_9.99

배열

  • Array 클래스로 표현
  • get, set 함수를 가지며 연산자 오버로딩 컨벤션에 의해 []로 변환됨
  • size 속성 가짐
class Array<T> private constructor() {
    val size: Int
    operator fun get(index: Int): T
    operator fun set(index: Int, value: T): Unit
    
    operator fun iterator(): Iterator<T>
}
  • 배열을 만들기 위해서 arrayOf() 함수 사용
  • arrayOfNulls()는 주어진 크기에 null로 채워진 배열 생성
  • 배열 크기와 인덱스를 가진 배열 요소의 값을 반환하는 함수를 가지는 Array 생성자 사용 가능
// ["0", "1", "4", "9", "16"] 을 가지는 배열 생성
val asc = Array(5) { i -> {i * i}.toString() }
asc.forEach( println(it) )
  • 코틀린 배열은 불변으로서 Array<String>Array<Any>에 할당하는 것을 막으며 런타임 에러 방지

원시 타입 배열

원시 타입 배열

  • ByteArray, ShortArray, IntArray 등의 원시 타입 배열을 표현하는 클래스
  • Array 클래스와 상속 관계가 없지만 같은 메소드와 속성을 가짐
val x: IntArray = intArrayOf(1, 2, 3)
x[0] = x[1] + x[2]

// 크기는 5이며 [0, 0, 0, 0, 0]의 값을 가짐
val arr = IntArray(5)

// 크기는 5이며 [42, 42, 42, 42, 42]의 값을 가짐
val arr2 = IntArray(5) { 42 }

// 람다를 이용해 값을 초기화
val arr3 = IntArray(5) { it * 1 }