변수와 자료형 (2)


본 게시글은 부스트코스의 코틀린 기본프로그래밍을

시청한 후 학습한 정보를 기록하는 목적의 게시글입니다.

부스트코스

 

다 함께 배우고 성장하는 부스트코스

부스트코스(boostcourse)는 모두 함께 배우고 성장하는 비영리 SW 온라인 플랫폼입니다.

www.boostcourse.org

코틀린 기본프로그래밍(1/2)

 

코틀린 프로그래밍 기본1/2(함수편)

부스트코스 무료 강의

www.boostcourse.org

본 게시글은 부스트코스 서포터즈 3기 활동 게시글입니다.


#2.2. 나를 괴롭히는 널(Null)!

- 들어가기 전에

코틀린은 변수를 사용할 때 반드시 값이 할당되어 있어야 한다는 원칙이 있습니다. 만약 값이 할당되지 않은 변수를 사용하면 코틀린에서 오류가 발생합니다. 한편 값이 없는 상태는 null이라고 부릅니다. 우리를 괴롭히는 널(null)에 대해 알아봅시다.

- 핵심 키워드

  • Non-null 타입과 nullable 타입 (타입?)
  • NPE
  • 세이프콜(?.)
  • 넌널 단정 기호(!!.)
  • 엘비스 연산자 (?:)

 

1) 널을 허용하는 변수 선언하기

코틀린은 변수를 사용할 때 반드시 그 값이 할당되어 있어야 한다.

값이 할당되지 않은 변수를 사용하면 에러가 발생한다.

이러한 값이 없는 상태를 null이라고 부르며 널 변수 허용을 위해서는 물음표 `?` 기호를 사용하여야 한다.

 

fun main() {
    var str1 : String = "Hello Kotlin"
    str1 = null  // null을 허용하지 않음(오류 발생)
    println("str1: $str1")
}

위 코드를 실행하면 null 값이 할당될 수 없다는 오류 메시지를 마주할 수 있다.

앞서 명시한 대로 물음표 기호를 사용해 다시 선언해보자.

fun main() {
    var str1 : String? = "Hello Kotlin"
    str1 = null
    println("str1: $str1")
}

String과 String? 은 서로 다른 자료형임을 확실히 이해해야 할 것이다.

 

null을 허용한다면, null을 사용할 수 없는 함수에서 사용할 수 없게 된다.

fun main() {
    var str1 : String? = "Hello Kotlin"
    str1 = null
    println("str1: $str1 lenghth: ${str1.length}") // null을 허용하면 length가 실행될 수 없음
}

위 예제에서 str1 변수에 문자열이 할당되어 있는 상태라면 길이를 구할 수 있겠지만,

null이 할당되어 있는 상태라면 문자열의 길이를 구할 때 NPE가 발생할 것이다.

What is NPE in Kotiln? - Null Safety

 

Null Safety - Kotlin Programming Language

 

kotlinlang.org

String? 형에서는 세이프 콜(?.)이나 non-null 단정 기호(!!.)만 허용한다는 팁을 볼 수 있다.

length의 접근하려면 이 두 기법 중 하나를 사용하여야 할 것이다.

println("str1: $str1 length: ${str1?.length}")
println("str1: $str1 length: ${str1!!.length}")

세이프콜의 경우는 변수 str1을 검사한 다음 null이 아니면 멤버 함수에 접근하도록 동작하고,

non-null 단정기호의 경우 컴파일러가 null이 아님을 알려주어 null 검사를 수행하지 않도록 한다.

 

fun main() {
    var str1 : String? = "Hello Kotlin"
    str1 = null
    // 조건식을 통해 null 상태 검사
    val len = if(str1 != null) str1.length else -1 
    println("str1: $str1 length: ${len}")
}

이 처럼 조건식을 통해서도 null을 허용한 변수를 검사할 수 있다.

위 코드에서는 str1 != null을 통해 str1 변수에 할당된 값이 null이 아닌 경우에만

length로의 접근을 허용하고 있다.

(그렇지 않다면 len의 값으로 -1이 호출된다.)

 

null을 허용한 변수를 조금 더 안전하고 간단하게 사용하려면 세이프 콜과 엘비스 연산자를 함께 사용하면 된다.

엘비스 연산자는 변수가 null인지 아닌지 검사하여 null이 아니라면 왼쪽의 식을 그대로 실행하고,

null이라면 오른쪽의 식을 실행한다.

C++나 JavaScript의 삼항 연산자와 유사하다.

fun main() {
    var str1 : String? = "Hello Kotlin"
    str1 = null
    println("str1: $str1 length: ${str1?.length ?: -1}") // 세이프 콜과 엘비스 연산자 활용
}

 

2) 요약

  • Kotlin에서는 기본적으로 NotNull이고 Nullable 표현에만 `?`가 사용된다.
  • 세이프 콜 : ?.
  • non-null 단정 기호 : !!.
  • if-else를 활용한 null 검사
  • 엘비스 연산자를 사용한 null 검사

 

#2-3. 검사와 자료형을 변환해보기

- 들어가기 전에

코틀린에서는 자료형이 다르면 변환 함수를 사용해야 하지만 자바에서 자료형이 서로 다를 때 자동적으로 변환됩니다. 만일 좀 더 추상적인 상위 자료형을 사용하면 자료형은 해당 할당 값에 따라 스마트 캐스팅 됩니다.

- 핵심 키워드

  • 자료형의 변환
  • 스마트 캐스팅
  • 기본형과 참조형의 비교
  • Number형
  • Any형

 

1) 코틀린 자료형 변환 함수

코틀린의 자료형은 모두 참조형으로 선언된다.

코틀린에서 서로 다른 자료형을 비교하거나 연산하고자 하는 경우에,

자료형 변환이 이루어지지 않는다면 이를 동작할 수 없다.

자료형 변환 메서드를 통해 자료형 변환을 수행할 수 있다.

  • toByte: Byte
  • toLong: Long
  • toShort: Short    
  • toFloat: Float
  • toInt: Int
  • toDouble: Double
  • toChar: Char

 

2) 이중 등호와 삼중 등호

자료형을 비교할 때는 단순히 값만 비교하는 방법과 참조 주소까지 비교하는 방법이 있다.

단순히 값만 비교하고자 할 때에는 이중 등호를 사용하고,

참조 주소를 비교하려면 삼중 등호를 사용하여야 한다.

이중 등호의 경우 참조에 상관없이 값이 동일하면 true를, 다르면 false를 반환한다.

삼중 등호는 값과 상관없이 참조가 동일하면 true를, 다르면 fasle를 반환한다.

 

val a: Int = 128
val b: Int = 128
println(a == b) // true
println(a === b) // true

val a: Int = 128
val b: Int? = 128
println(a == b) // true
println(a === b) // false

단순히 int형으로 선언한 변수 a와 b는 참조와 값이 모두 같음을 알 수 있다.

하지만 null을 허용한 변수는 같은 값을 저장해도, 같은 참조를 수행하지 않는 것을 알 수 있다.

이는 변수 a는 스텍에 값을 저장하지만,

null을 허용한 b는 참조형으로 힙에 참조 주소가 저장되기 때문이다.

fun main() {
    val a: Int = 128
    val b = a

    println(a === b) // 자료형이 기본형인 int가 되어 값이 동일 true
    val c: Int? = a
    val d: Int? = a
    val e: Int? = c
    println(c == d) // true
    println(c === d) // false
    println(c === e) // true
}

 

3) 기본형값과 참조형값의 설명

https://www.boostcourse.org/mo132/lecture/59969

 

a와 b는 참조형인 int형으로 선언되었지만 코틀린 컴파일러에 의해 기본형으로 변환되어 저장된다.

그래서 프로그램을 실행하면 a와 b는 스택에 주소가 아닌 128이라는 값이 저장된다.

null을 허용한 변수 c, d는 참조형으로 저장된다.

그래서 a에 들어있는 값인 128이 저장되는 것이 아니라

128을 가리키고 있는 서로 다른 A1과 A2가 저장되는 것이다.

 

4) 스마트 캐스트

만약 어떤 값이 경우에 따라 정수일 수도 있고 실수일 수도 있다면 어떻게 해야 할까?

물론 자료형을 필요에 따라 변환해주어도 좋겠지만, 스마트캐스트를 사용하는 것이 편리하다.

스마트 캐스트란 변수에 할당되는 값에 따라 자료형을 결정한다.

대표적인 스마트 캐스트가 적용되는 자료형은 Number가 있다.

fun main() {
    var test: Number = 12.2 // 12.2에 의해 test는 Float형으로 스마트 캐스트
    println("$test")

    test = 12  // Int형으로 스마트 캐스트
    println("$test")

    test = 120L // Long형으로 스마트 캐스트
    println("$test")

    test += 12.0f  // Float형으로 스마트 캐스트
    println("$test")
}

Number는 숫자를 저장하기 위한 특수한 자료형 객체를 생성한다.

Kotlin Smart Cast

 

Kotlin Smart Cast - javatpoint

Kotlin Smart Cast with introduction, architecture, class, object, inheritance, interface, generics, delegation, functions, mixing java and kotlin, java vs kotlin etc.

www.javatpoint.com

 

5) is를 사용한 자료형 검사

변수의 자료형을 알아내는 방법으로서 is를 사용할 수 있다.

is는 좌항의 변수가 우항의 자료형과 같으면 true를 아니면 false를 반환한다.

fun main() {

    val num = 256

    if (num is Int) { // num이 Int형일 때
        print(num)
    } else if (num !is Int) { // num이 Int형이 아닐 때, !(num is Int) 와 동일
        print("Not a Int")
    }
}

 

6) Any를 통한 묵시적 변환

is는 변수의 자료형을 검사한 다음 그 변수를 해당 자료형으로 변환하는 기능도 있다.

Type Checks and Casts: 'is' and 'as'

 

Type Checks and Casts: 'is' and 'as' - Kotlin Programming Language

 

kotlinlang.org

자료형이 정해지지 않은 경우에 Any를 사용할 수 있다.

Any는 모든 클래스의 뿌리이며 Int나 String 역시 Any형의 자식 클래스이다.

Any는 언제든 필요한 자동형으로 자동 변환할 수 있다.

즉, Any 형을 사용하면 자료형을 결정하지 않은 채 변수를 선언할 수 있다.

fun main() {
    var a: Any = 1 // Any형 a는 1로 초기화될 때 Int형이 됨
    a = 20L // Int형이였던 a는 변경된 값 20L에의해 Long이 됨
    println("a: $a type: ${a.javaClass}")  // a의 자바 기본형을 출력하면 long이 나옴
}

처음에 a의 자료형은 int형이지만, 이후 변경된 값에 의해 Long형으로 변환된다.

 

fun main() {
    checkArg("Hello") // 문자열을 인자로 넣음
    checkArg(5) // 숫자를 인자로 넣음
}

fun checkArg(x: Any) {  // 인자를 Any형으로 받음
    if (x is String) {
        println("x is String: $x")
    }
    if (x is Int) {
        println("x is Int: $x")
    }
}

main 함수에서 checkArg라는 함수를 사용하고 있으며, checkArg의 인자는 Any형인 x이다.

이 함수를 사용할 때 x에 들어오는 인자의 자료형에 따라 문자열 혹은 정수형등으로 받아 처리할 수 있게 된다.

 

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기