ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Kotlin in Action: 6.2 코틀린의 원시 타입
    Book/Kotlin in Action 2022. 11. 25. 00:25

    6.2 코틀린의 원시 타입

    코틀린은 원시 타입과 래퍼 타입을 구분하지 않는다. 

     

    6.2.1 원시 타입: Int, Boolean 등

    자바는 원시 타입과 참조 타입을 구분한다. 

    원시타입의 변수에는 그 값이 직접 들어가지만, 참조 타입의 변수에는 메모리상의 객체 위치가 들어간다.

    이 방법으로 원시 타입의 값을 더 효율적으로 저장하고 여기저기 전달할 수 있지만, 그런 값에 대해 메소드를 호출하거나 컬렉션에 원시 타입 값을 담을 수는 없다.

    만일 참조 타입이 필요한 경우 특별한 래퍼 타입등으로 원시 타입 값을 감싸서 사용한다. 정수의 컬렉션을 정의하려면 Collection<int>가 아니라 Collection<Integer>를 사용하는 것처럼 말이다.

     

    코틀린은 원시 타입과 래퍼 타입을 구분하지 않으므로 항상 같은 타입을 사용한다. 다음 예제는 정수를 표현하는 Int 타입을 사용한다.

    val i : Int = 1 
    val list: List<Int> = listOf(1,2,3)

    코틀린에서는 숫자 타입 등 원시 타입 값에 대해 메소드를 호출할 수 있다. 

    fun showProgress(progress: Int) {
    		val percent = progress.coerceIn(0, 100)
    		println("We're ${percent}% done!")
    }

    원시 타입과 참조 타입이 같다면 코틀린이 그들을 항상 객체로 표현하는 것인지, 그렇다면 너무 비효율적이지 아닌지에 대해 궁금할 것이다.

     

    코틀린은 이것을 항상 객체로 표현하지 않는다. 실행 시점에 숫자 타입은 가능한 한 가장 효율적인 방식으로 표현된다.

     

    대부분의 경우 코틀린의 Int타입은 자바의 int타입으로 컴파일 된다. 이런 컴파일이 불가능한 경우는 컬렉션과 같은 제네릭 클래스를 사용하는 것 뿐이다. 예를 들어 Int 타입을 컬렉션의 타입 파라미터로 넘기면 그 컬렉션에는 Int의 래퍼타입에 해당하는 Integer객체가 들어간다.

    자바 원시 타입에 해당하는 타입은 다음과 같다.
    • 정수 타입 : Byte, Short, Int, Long
    • 부동소수점 수 타입 : Float, Double
    • 문자 타입 : Char
    • 불리언 타입 : Boolean

    Int와 같은 코틀린 타입에는 널 참조가 들어갈 수 없기 때문에 쉽게 그에 상응하는 자바 원시 타입으로 컴파일 할 수 있다. 마찬가지로 반대로 자바 원시 타입의 값은 결코 널이 될 수 없으므로 자바 원시 타입을 코틀린에서 사용할 때도 널이 될 수 없는 타입으로 취급할 수 있다.

     

    6.2.2 널이 될 수 있는 원시 타입 : Int?, Boolean? 등

    코틀린에서 널이 될 수 있는 원시 타입을 사용하면 그 타입은 자바의 래퍼 타입으로 컴파일 된다. 

    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(args: Array<String>) {
        println(Person("Sam", 35).isOlderThan(Person("Amy", 42)))
        println(Person("Sam", 35).isOlderThan(Person("Jane")))
    }
    
    // false
    // null

    Person 클래스에 선언된 age 프로퍼티의 값은 Integer로 저장된다. 코틀린에서 적절한 타입을 찾으려면 그 변수나 프로퍼티에 널이 들어갈 수 있는지만 고민하면 된다.

     

    앞에서 얘기한 대로 제네릭 클래스의 경우 래퍼 타입을 사용한다. 어떤 클래스의 타입 인자로서 원시 타입을 넘기면 코틀린은 그 타입에 대한 박스 타입을 사용한다.

     

     

    이렇게 컴파일 하는 이유는 자바 가상머신JVM에서 제네릭을 구현하는 방법 때문이다. JVM은 타입 인자로 원시 타입을 허용하지 않는다. 따라서 자바나 코틀린 모두에서 제네릭 클래스는 항상 박스 타입을 사용해야 한다.

     

    이제 여러 원시 타입 값을 서로 어떻게 변환하는지 보자.

     

    6.2.3 숫자 변환

    코틀린은 한 타입의 숫자를 다른 타입의 숫자로 자동 변환하지 않는다. 결과 타입이 허용하는 숫자의 범위가 원래 타입의 범위보다 넓은 경우 조차도 자동 변환은 불가능하다.

     

    코틀린은 원시 타입에 대한 변환 함수를 제공한다.

    어떤 타입을 더 표현 범위가 넓은 타입으로 변환하는 함수도 있고, 타입을 범위가 더 표현 범위가 좁은 타입으로 변환하면서 값을 벗어나는 경우에는 일부 잘라내는 함수도 있다.

    문자열을 숫자로 변환하기

    코틀린 표준 라이브러리는 문자열을 원시 타입으로 변환하는 여러 함수를 제공한다. (toInt, toByte, toBoolean 등)
    >>> println("42".toInt()) 42

    이런 함수는 문자열의 내용을 각 원시 타입을 표기하는 문자열로 파싱한다. 파싱에 실패하면 NumberFormatException이 발생한다.

     

    파싱이란? 

    파싱
    (parsing)은 구문 분석이라고 한다. 문장이 이루고 있는 구성 성분을 분해하고 분해된 성분의 위계 관계를 분석하여 구조를 결정하는 것이다. 즉 데이터를 분해 분석하여 원하는 형태로 조립하고 다시 빼내는 프로그램을 말한다. 웹상에서 주어진 정보를 내가 원하는 형태로 가공하여 서버에서 불러들이는 것이다.

    6.2.4 Any, Any? : 최상위 타입

    자바에서 Object가 클래스 계층의 최상위 타입이듯 코틀린에서는 Any 타입이 모든 널이 될 수 없는 타입의 조상 타입이다. 하지만 코틀린에서는 Any가 Int 등의 원시 타입을 포함한 모든 타입의 조상 타입이다.

     

    Any가 널이 될 수 없는 타입임에 유의하라. 따라서 Any 타입의 변수에는 null이 들어갈 수 없다. 코틀린에서 널을 포함하는 모든 값을 대입할 변수를 선언하려면 Any? 타입을 사용해야한다.

     

    그리고 이 함수는 컴파일될 때 Java의 Object로 컴파일된다.

     

    6.2.5 Unit 타입: 코틀린의 void

    코틀린의 Unit 타입은 자바 void와 같은 기능을 한다.

     

    Unit이 자바의 void와 다른 점은 다음과 같다.

     

    1. Unit은 모든 기능을 갖는 일반적인 타입이며, void와 달리 Unit을 타입 인자로 쓸 수 있다.
    2. Unit타입에 속한 값은 단 하나뿐이며, 그 이름도 Unit이다.
    3. Unit 타입의 함수는 Unit 값을 묵시적으로 반환한다.

    이 특성은 제네릭 파라미터를 반환하는 함수를 오버라이드하면서 반환 타이브오 Unit을 쓸 때 유용하다.

     

    전통적으로 Unit은 '단 하나의 인스턴스만 갖는 타입'을 의미해 왔고 바로 그 유일한 인스턴스의 유무가 자바 void와 코틀린 Unit을 구분하는 가장 큰 차이이다.

     

    그리고 코틀린에는 Nothing이라는 전혀 다른 기능을 타입이 존재한다. 

     

    6.2.6 Nothing 타입: 이 함수는 결코 정상적으로 끝나지 않는다.

    코틀린에는 결코 성공적으로 값을 돌려주는 일이 없으므로 '반환 값'이라는 개념 자체가 의미 없는 함수가 일부 존재한다.

     

    이런 함수를 호출하는 코드를 분석할 때 함수가 정상적으로 끝나지 않는 사실을 알기 위해 코틀린에는 Nothing이라는 특별한 반환 타입이 있다.

     

    fun fail(message: String) : Nothing {
    		throw IllegalStateException(message)
    }
    
    val address = company.address ?: fail("No address")
    println(address.city)

    Nothing 타입은 아무 값도 포함하지 않는다. 따라서 Nothing은 함수의 반환 타입이나 반환 타입으로 쓰일 타입 파라미터만 쓸 수 있다.

    컴파일러는 Nothing이 반환 타입인 함수가 결코 정상 종료되지 않음을 알고 그 함수를 호출하는 코드를 분석할 때 사용한다.

     
     
Designed by Tistory.