원시 타입과 기본 타입

유우선·2026년 2월 12일

Kotlin Study📚

목록 보기
19/32

개요

  • 코틀린 → 원시 타입과 래퍼 타입을 구분하지 않음
    • 그 이유와 작동 원리
  • Object, void 등의 자바 타입과 코틀린 타입의 대응 관계

Int, Float, Char, Boolean을 원시 타입으로 표현

자바 → 원시 타입과 참조 타입을 구분함

  • 원시 타임 → 값이 직접 들어감
  • 참조 타입 → 메모리 상의 객체 위치가 들어감
  • 원시 타입은 값에 대한 메서드를 호출할 수 없음
  • 원시 타입을 래퍼 타입으로 감싸서 참조 타입으로 사용함
    • Collection

코틀린 → 원시 타입과 참조 타입을 구분하지 않음

  • 래퍼 타입을 따로 구분하지 않기 때문에 원시 타입의 값에 대해 메서드를 호출할 수 있음
    fun showProgress(progress: Int) {
        val percent = progress.coerceIn(0, 100)
        println("We're $percent % done!")
    }
    fun main() {
        showProgress(146)
        // We're 100 % done!
    }
    • 원시 타입과 참조 타입이 같다고 항상 객체로 표현하는 것은 아님
    • 대부분의 경우(변수, 프로퍼티, 파라미터, 반환 타입 등) 코틀린의 Int는 자바의 int로 컴파일 됨
      • 컬렉션과 같은 제네릭 클래스는 다름
      • Int 타입을 타입 파라미터로 넘기면 java.lang.Integer 객체가 들어감

자바 원시 타입에 해당하는 코틀린 타입

  • 정수 타입 : Byte, Short, Int, Long
  • 부동소수점 숫자 타입 : Float, Double
  • 문자 타입 : Char
  • 불리언 타입 : Boolean

양수 표현을 위한 모든 비트 범위 사용 : 부호 없는 숫자 타입

코틀린은 JVM의 일반적인 원시 타입을 확장해 부호 없는 타입을 제공

타입크기값 범위
UByte8비트0 ~ 255
UShort16비트0 ~ 65535
UInt32비트0 ~ 2^32 - 1
ULong64비트0 ~ 2^64 - 1
  • 상응하는 부호 있는 타입의 범위를 시프트
  • 같은 메모리 크기를 사용해 더 넓은 양수 범위를 표현
    • Int = -20억 ~ 20억
    • UInt = 0 ~ 40억
  • 부호 없는 수도 필요할 때만 래핑됨

null이 될 수 있는 기본 타입

코틀린의 null이 될 수 있는 타입 → 자바 원시 타입으로 표현할 수 없음

  • null 참조를 자바의 참조 타입에만 대입할 수 있음
  • 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
    }
}

fun main() {
    println(Person("sam", 35).isOlderThan(Person("Amy", 42)))
    // false
    println(Person("sam", 35).isOlderThan(Person("Jane")))
    // null
}
  • Int? 타입의 두 값은 null이 될 가능성이 있으므로 두 값을 직접 비교할 수 없음
    • 두 값에 대한 null 검사를 진행해야 함
  • Person 클래스에 선언된 age → java.lang.Integer로 저장됨
    • 자바에서 가져온 클래스를 다룰 때 필요함
    • 코틀린에선 변수나 프로퍼티에 null이 들어갈 수 있는지만 고려하면 됨

제네릭 클래스 → 래퍼 타입을 사용함

  • 클래스의 타입 인자로 원시 타입을 넘기면 코틀린은 그 타입에 대한 박스 타입을 사용
  • val listOfInts = listOf(1, 2, 3)
    • null 값이나 null이 될 수 있는 타입을 사용하지 않았지만 Integer 래퍼 타입으로 컴파일 됨
  • JVM은 타입 인자로 원시 타입을 허용하지 않음
  • 자바나 코틀린 모두 박스 타입을 사용해야 함

원시 타입 수 변환

코틀린 → 자동 변환이 안됨

val i = 1
val l: Long = i
// ERROR: type missmatch 오류 발생

직접 변환 메서드를 호출해야 함

val i = 1
val l: Long = i.toLong()
  • 코틀린은 Boolean을 제외한 원시타입에 변환 함수를 제공함

두 박스 타입 비교

  • 두 박스 타입 간의 equals 메서드 → 박스 타입의 객체를 비교함
    • 타입을 명시적으로 변환하여 같은 타입끼리 비교해야 함

      fun main() {
          val x = 1
          val list = listOf(1L, 2L, 3L)
      
          println(x.toLong() in list)
          // true
      }

숫자 리터럴

  • 숫자 리터럴에 대해선 따로 변환 작업을 하지 않아도 됨
  • 컴파일러가 필요한 변환을 자동으로 넣어줌
  • 산술 연산자는 적당한 타입의 값을 받도록 이미 오버라이드 되어 있음
fun printALong(l: Long) = println(l)

fun main() {
    val b: Byte = 1
    val l = b + 1L // +는 Byte와 Long을 인자로 받을 수 있음
    printALong(42) // 컴파일러는 42를 Long으로 해석함
}

오버플로우

  • 코틀린에서도 산술연산에 대한 오버플로우가 발생할 수 있음
fun main() {
    println(Int.MAX_VALUE + 1)
    // -2147483648
    println(Int.MIN_VALUE - 1)
    // 2147483647
}

문자열 변환 (toBoolean())

  • 인자로 받은 문자열이 null이 아니고 문자열의 내용이 true와 같으면 true를 반환
    • 문자열 내용은 대소문자를 구분하지 않음

      fun main() {
      		println("trUE".toBoolean())
      		// true
      		println("7".toBoolean())
      		// false
      		println(null.toBoolean())
      		// false
      }
      
  • toBooleanStrict()를 사용하면 true, false 이외에는 전부 false를 반환한다.

Any와 Any? : 코틀린 타입 계층의 뿌리

자바와 코틀린의 최상위 타입

  • 자바 → object라는 최상위 클래스가 있음
    • 참조 타입은 object 클래스를 참조하지만 원시 타입은 object 클래스를 참조하지 않음
    • 원시 타입에서 Object 타입 객체가 필요한 경우 래퍼 타입으로 감싸야 함
  • 코틀린 → Any라는 최상위 클래스가 있음
    • Any가 원시 타입을 포함한 모든 타입의 부모 클래스임
    • 원시 타입의 값을 Any 타입의 변수에 저장하면 자동으로 값을 객체로 감쌈(boxing)
      val answer: Any = 42 // 42가 박싱됨

Any 타입의 null 가능성

  • Any는 기본적으로 null이 될 수 없는 타입
  • Any 타입에 null을 저장하려면 물음표( ? )를 붙여야 함

Any 타입과 Object 타입

  • Any 타입은 java.lang.Object에 대응함
    • 자바에서 Object를 인자로 받거나 반환하면 코틀린에선 Any 타입으로 취급됨
  • 코틀린에서 Any를 사용하면 자바 바이트코드의 Object로 컴파일 됨

코틀린 클래스의 기본 함수

  • toString(), equals(), hashCode() 메서드는 Any 클래스에 정의된 메서드를 상속한 것
  • java.lang.Object에 정의된 다른 메서드는 Any 클래스에서 사용할 수 없음
    • java.lang.Object에 정의된 메서드를 사용하려면 java.lang.Object타입으로 캐스트 해야 함

Unit 타입: 코틀린의 void

  • Unit타입 → 자바의 void와 같은 역할
  • 아무 값도 반환하지 않는 함수에 사용할 수 있음
    fun f(): Unit { /*...*/ }
  • 문법적으로 아무 반환 타입도 선언하지 않은 함수와 같음
    fun f() { /*...*/ }
  • 코틀린 함수의 반환타입이 Unit이고 그 함수가 제네릭 함수를 오버라이드하지 않는다면 자바 void 함수로 컴파일 됨

Unit과 void의 차이

  • Unit → 모든 기능을 갖는 일반적인 타입
  • void와 달리 Unit을 타입 인자로 쓸 수 있음

제네릭 파라미터 반환 함수 오버라이드

  • Unit 타입에 속한 값은 단 하나이며 그 이름도 Unit임
  • Unit 타입의 함수는 Unit 값을 암시적으로 반환
interface Processor<T> {
    fun process(): T
}

class NoResultProcessor : Processor<Unit> {
    override fun process() { // Unit을 반환하지만 타입을 지정하지 않아도 됨
        // 처리 코드
    } // 명시적으로 return해 줄 필요 없음
}

자바에서의 ‘값 없음’

  • 자바에서는 값 없음에 대해 처리해줘야 한다는 점이 코틀린과의 차이점임

처리 방법

  1. 별도의 인터페이스를 통해 값을 반환하는 경우와 반환하지 않는 경우를 분리
  2. 타입 파라미터로 java.lang.Void 타입을 사용
    • void 타입에 대응하는 null값을 반환하기 위해 return null을 명시해줘야 함
    • 반환 타입이 void가 아니므로 명시적으로 처리

코틀린에서 void가 아닌 Unit이라는 이름을 채택한 이유

  • 함수형 프로그래밍에서 Unit → 단 하나의 인스턴스만 갖는 타입
    • 이 인스턴스의 유무가 void 타입과의 가장 큰 차이점

Nothing 타입

  • 반환값 자체가 필요 없는 함수에서 사용
fun fail(message: String) : Nothing {
    throw IllegalStateException (message)
}

fun main() {
    fail("Error occurred")
    // java.lang.IllegalStateException: Error occurred
}
  • Nothing 타입 → 아무 값도 포함하지 않음
    • 함수의 반환 타입이나 반환 타입으로 쓰일 타입 파라미터로만 쓸 수 있음
  • Nothing 타입으로 변수를 선언해도 그 변수에 아무 값도 저장할 수 없음
  • 엘비스 연산자의 오른쪽에 사용할 수 있음
    val address = company.address ?: fail("No address")
    println(address.city)

0개의 댓글