Groo

Kotlin 코틀린의 변수와 자료형 본문

프로그래밍 언어/Kotlin

Kotlin 코틀린의 변수와 자료형

김주엽 2020. 1. 3. 01:17

안녕하세요 오늘은 Kotlin 코틀린 언어의 변수와 자료형에 대해서 알아보려고 합니다.
Kotlin 코틀린 언어가 다른 언어들과 달리 새롭게 가지고 있는 특성에 대해 중점을 두어 이야기를 해보려고 합니다.

🤷‍♂️ Kotlin 코틀린 변수 선언 방법은?

Kotlin 코틀린 언어는 다른 언어들과 달리 새로운 변수를 선언하는 방식이 조금 다릅니다. 하지만 Kotlin 언어는 Java 언어를 기반으로 언어가 생성이 되었기 때문에 Java 언어와 비슷한 부분이 많이 존재합니다. 그래서 Java 언어를 알고 있다면 Kotlin 언어 또한 배우는 것은 크게 어렵지 않을 것입니다. 아래의 코드는 Java 언어와 Kotlin 언어의 변수 선언의 차이점을 보여주고 있습니다.

String userName = "Juyeop" // Java 변수 선언 및 초기화
val userName : String = "Juyeop" // Kotlin 변수 선언 및 초기화

🤷‍♀️ val, var 키워드는 무엇인가?

Kotlin 코틀린 언어에서의 val, var 키워드는 새로운 변수를 생성할 때 필수적으로 포함해야 하는 키워드 중 중요한 한 개입니다. 그럼 val, var 두 키워드의 약어, 기능, 차별점, 사용 시기에 대해 아래의 표를 통해서 설명을 해보도록 하겠습니다.

 

  val 키워드 var 키워드
키워드의 약어 value (immutable) variable (mutable)
키워드의 기능 최초로 지정한 값 이외 변경 X 최초로 지정한 값 이외 변경 O
키워드의 차별점 지속적인 변경 X 지속적인 변경 O
키워드의 사용 시기 상수 변수 생성 일반 변수 생성
val userAge : Int = 17 // val 키워드 선언 (값 변경 X)
var userAge : Int = 17 // var 키워드 선언 (값 변경 O)

🤔 변수 생성 시 자료형 생략이 가능해?

지금까지 새롭게 변수를 생성할 때 저희는 변수의 자료형을 직접 선언을 해주었습니다. 하지만 Kotlin 코틀린에서 새로운 변수를 생성할 때 변수에 추론 가능한 값을 입력 시 그 변수의 자료형을 생략해줄 수 있습니다. 그러나 만약 자료형을 생략한 변수에 추론 가능한 값을 초기화하지 않을 시 그 변수는 어떠한 자료형을 또한 추론할 수 없어 오류를 발생시킵니다. 이것은 꼭 기억하세요, 값을 할당하지 않으면서 변수를 선언하려면 자료형을 반드시 지정을 해야지만 오류가 발생하지 않습니다.

val userSchool = "대구소프트웨어고등학교" // String 자료형 생략
val userSchool // String 자료형 생략, 추론 가능한 값 생략 (오류 발생 O)

💻 변수를 생성해보고 값을 할당해보자!

위에서 배운 내용들을 바탕으로 간단한 예제를 구성해보았습니다. 아래의 코드들을 해석해보면 val 키워드를 사용하여 number 변수를 생성하고 var 키워드를 사용하여 language 변수를 생성하였습니다. 두 변수 모두 자료형을 생략하면서 추론 가능한 값으로 초기화하여 오류를 피하면서 변수를 생성하였습니다. 그 후 var 키워드로 생성한 language 변수의 값을 변경하였습니다.

fun main(){

    val number = 10 // val 키워드 사용 (값 변경 X)
    var language = "Korean" // var 키워드 사용 (값 변경 O)

    language = "English" 

    println("number : $number")
    println("language : $language")
}

🎉 Kotlin 코틀린만의 새로운 기능을 알아보자!

Kotlin 언어는 다른 언어들과 다르게 새롭게 가지고 있는 기능이 많이 존재합니다. 코틀린은 현대 프로그래밍의 트렌드에 맞게 개발자들이 필요로 하는 기능과 코드 작성에 편리한 기능 등을 많이 제공하고 있습니다. 위에서 아래로 코드에 대한 설명을 하면 첫 번째 코드 블록의 코드들은 표현식과 $(달러) 기호를 사용하여 문자열을 출력하고 있으며 두 번째 코드 블록은 형식화된 다중 문자열을 이용하여 형식화된 문자열을 출력합니다. 마지막 코드 블록은 typealias 키워드를 사용하여 자료형에 별명을 붙이는 것입니다.

// 표현식과 $(달러) 기호를 사용하여 문자열 출력
fun main() {

    var a = 1
    var str1 = "a = $a"
    var str2 = "a = ${a + 2}"

    println("str1 : \"$str1\", str2 : \"$str2\"")
}
// 형식화된 다중 문자열을 사용하여 출력하기
fun main() {
    val num = 10
    val formattedString = """
        var a = 6
        var b = "Kotlin"
        println(a + num)
    """.trimIndent()
    println(formattedString)
}
// 자료형에 별명을 붙여 사용하기
typealias Username = String

fun main() {

    val user : Username = "Juyeop"
}

📢 기본형, 참조형 자료형이 무엇인가?

보통 프로그래밍 언어의 대부분 자료형은 기본형 자료형과 참조형 자료형 2가지로 구별을 하며 Kotlin 코틀린 언어에서는 보통 참조형 자료형만을 사용합니다. 그럼 Kotlin 언어는 왜 기본형 자료형보다 속도가 느린 참조 자료형을 사용할까요? 그 이유는 겉으로는 Kotlin 코틀린 언어가 참조형을 사용하여 속도가 기본형 자료형보다 더 느린 것 같아 보이지만 프로그램 컴파일 과정을 거치면서 코틀린 컴파일러가 자동으로 참조형 자료형을 기본형 자료형으로 변환하면서 최적화를 수행시켜줍니다.

 

  기본형 자료형 (Primary Data Type) 참조형 자료형 (Reference Type)
자료형에 대한 의미 가공되지 않은 순수한 자료형 메모리 영역에 할당된 자료형
자료형에 대한 구체화 프로그래밍 언어에 내장되어있다. 객체 생성, 동적 할당을 수행한다.
자료형에 대한 예시 int, long, flaot, double String, Date, ArrayList
자료형의 속도 차이 속도 상대적으로 빠르다. 속도 상대적으로 느리다.

❓ NULL 값 적용이 가능한가?

프로그램이 실행되는 도중에 값이 null인 변수에 접근을 하려고 하면 NullPointException(NPE) 예외 오류가 발생합니다. 그러나 Kotlin 언어에서는 변수에 null 값을 허용하지 않아 이 문제를 미리 방지하고 있습니다. 그럼 변수에 null 값을 추가하려고 하면 어떻게 해야 할까요? 그 방법은 변수를 선언할 시 그 변수에 null 값이 허용된다고 물음표(?) 기호를 꼭 명시해야 합니다.

fun main() {
    var str1 : String? = "Hello Kotlin" // 물음표(?) 세이프 콜 방식 사용
    str1 = null

    println(str1)
}

변수에 null 값을 적용하는 방식은 물음표(?) 세이프 콜뿐만이 아니라 느낌표(!!) non-null 단정 기호 방식 또한 존재합니다. 위에서 세이플 콜 방식을 알아봤으니 아래에서는 non-null 단정 기호 방식을 통해 null 값을 허용해보겠습니다.

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

    println("str1 : $str1, length : ${str1!!.length}") // non-null 단정 기호 방식 사용
}

그럼 null 값을 허용하도록 도와주는 세이프 콜, 단정 기호에 대한 차이점에 대해서 알아보도록 하겠습니다. 먼저 세이프 콜 방식은 변수의 값에 null 값이 할당되어 있을 가능성을 검사하여 안전하게 호출하는 기법을 말합니다. 만약 검사한 변수의 값이 null 값일 경우 그대로 null 값을 출력하며 오류 없이 프로그램을 종료합니다. 그러나 단정 기호는 변수에 할당된 값이 null 값이 아님을 단정하고 컴파일러가 null 값 검사 없이 컴파일을 진행합니다. 그러나 만약 변수에 null 값이 포함 시 NPE 오류를 발생시킵니다.

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

    println("str1 : $str1, length : ${str1?.length ?: -1}") // 세이프 콜과 엘비스 연산자의 활용
}

마지막으로 세이프 콜과 엘비스 연산자를 이용하여 일반적인 세이프 콜, 단정 기호보다 훨씬 더 안전하게 컴파일을 null 값을 대응할 수 있는 방식이 있습니다. 방식은 위의 코드와 같이 세이프 콜과 엘비스 연산자를 함께 사용하는 것입니다. 이 방식은 if~else 문과 비슷하며 또한 삼항 연산자와도 많이 비슷합니다. 이러한 방식을 사용하면 만약 값이 null 값일 시 특정 값으로 대체할 수 있어 null 발생을 대비할 수 있으므로 더욱 안전합니다. 또한 코드를 한 줄에 표현할 수 있어 코드의 가독성에도 더욱 뛰어납니다.

💥 Kotlin 자료형 변환은 어떻게?

코딩을 하다 보면 자료형 변환을 해야 하는 경우가 많이 발생합니다. Java 언어에서의 경우에는 자동형 변환 기능이 다른 언어들에 비해 잘되어있다 보니 별로 신경을 쓰지 않고 코딩을 하여도 되었습니다. 하지만 Kotlin 같은 경우에는 의도치 않게 자료형이 변하는 것을 방지하기 위해서 자동형 변환을 되도록 줄였습니다. 그래서 Kotlin에서는 코틀린에서 만의 형 변환 기능을 사용하여야 합니다.

fun main() {

    val a : Byte = 1
    val b : Int = a.toInt()
    val c : Long = b.toLong()
    val d : Float = c.toFloat()
    val e : Double = d.toDouble()

    println(e)
}

Kotlin 언어에서의 자료형 변환의 종류로는 참조 자료형 대부분으로 모두 변경이 가능하다. 위의 예시처럼 다양하게 형 변환을 하는 예시를 보여주고 있습니다. 그러나 아래의 코드에서는 형 변환을 시켜주지 않더라도 자동으로 형 변환이 되는 경우도 있습니다.

fun main() {

   val result = 1L + 3 // 자동 형 변환 기능 수행
}

💦 기본형, 참조형 자료형의 비교 방식은?

Kotlin 코틀린에서는 자료형을 비교할 때 단순히 값을 비교하는 방식과 참조 주소를 비교하는 방식이 있습니다. 두 방식은 서로서로 약간 씩 다르며 차별점이 존재합니다. 그럼 아래의 표를 통해서 자료형을 비교하는 두 가지 방식에 대해서 소개하겠습니다.

 

  값 비교 참조 주소 비교
등호 형태 이중 등호(==) 삼중 등호(===)
비교 형태 주어진 값만을 비교한다. 주어진 값의 참조 주소를 비교한다.
출력 형태 참(True), 거짓(False) 형태로 반환한다.
fun main() {

    val a : Int = 128
    val b = a

    println(a===b) // 삼중 등호 비교

    val c : Int? = a
    val d : Int? = a
    val e : Int? = c

    println(c == d) // 이중 등호 비교
    println(c === d)
    println(c === e)
}

위의 코드는 눈으로 보았을 때는 어렵지 않은 코드이다. 하지만 위의 코드를 해석하기 위해서는 스택, 힙 영역에 메모리가 할당되는 방식을 알아야지 해석이 될 것이다. 간략한 해석을 하면 a, b 변수는 Int 참조 자료형이 컴파일 과정을 통해 기본 자료형으로 바뀌어 스택에 128 값이 저장이 된다. 하지만 c, d, e 변수는 자료형 선언 부분에 세이프 콜을 붙여 참조 자료형이다 보니 스택에 힙에 저장된 값의 참조 주소가 저장이 된다. 이 정도 해석이 가능하다면 실행 결과에 대해서는 스스로 추측해보는 것을 추천드립니다.

🔎 자료형을 검사하는 방법은?

어떠한 변수의 자료형을 검사하는 방법은 무엇이 있을까? 한 개의 예를 들어보도록 하겠습니다. Java 언어에서 어떠한 객체가 특정한 인터페이스에 포함이 되어있는지 구별을 하기 위해 instanceof라는 키워드를 사용하였습니다. 그것과 비슷하게 코틀린에서는 특정한 자료형을 검사하기 위해 is라는 키워드를 사용하고 있습니다. 사용 방법은 Java 언어에서의 instanceof와 비슷합니다.

fun main() {
    val num = 256

    if(num is Int){ // is 키워드를 통해 자료형 검사
        print(num)
    }else if(num !is Int){
        print("Not an Int")
    }
}

🎨 스마트 캐스트에 대해서 알아보자!

스마트 캐스트라는 단어의 의미는 만약 당신이 프로그래밍을 할 때 어떤 변수의 자료형이 수시로 바뀐다면 그때 매번 자료형을 형 변환을 하는 것보다 스마트 캐스트 자료형을 이용하는 것이 좋을 것이다. 스마트 캐스트는 컴파일러가 자동으로 형 변환을 하는 것입니다. 다양한 스마트 캐스트의 종류 중에서 Number 자료형은 값에 따라 정수 자료형으로 컴파일러가 변환을 합니다.

fun main() {
    var test : Number = 12.2 // Number 스마트 캐스트 자료형 사용

    println("$test")

    test = 12
    println("$test")

    test = 120L
    println("$test")

    test += 12.0f
    println("$test")
}

이번에는 자주 사용하는 자료형 Any 자료형에 대해서 알아보도록 하겠습니다. Any 자료형은 스마트 캐스트 방식이 아니긴 하지만 스마트 캐스트 방식과 거의 비슷한 성질을 가지고 있어 같이 설명을 진행하도록 하겠습니다. Any 자료형은 Kotlin 코틀린에서 모든 자료형의 슈퍼 클래스이며 코틀린의 모든 클래스는 이 Any 자료형을 슈퍼클래스로 가집니다. Any 자료형은 어떠한 자료형이라도 모두 될 수 있으며 필요한 자료형으로 언제나 변환이 가능합니다. 이것을 묵시적 변환이라고 부릅니다.

fun main() {
    var a : Any = 1 // Any 자료형으로 선언을 하였다.
    a = 20L

    println("a : $a, type : ${a.javaClass}")
}
fun main() {
    checkArg("Hello")
    checkArg(5)
}

fun checkArg(x : Any){ // 함수의 인자 x의 자료형 Any 선언
    if(x is String){
        println("x is String : $x")
    } else if(x is Int){
        println("x is Int : $x")
    }
}

👍 글을 마치며

오늘은 Kotlin 코틀린 언어의 변수와 자료형에 대해서 한 번 알아보는 시간을 가졌습니다. 변수와 자료형에 대한 내용은 다른 언어들과 비슷한 점이 많아 처음에는 설명을 해야 하나?라는 생각이 많이 들었습니다. 하지만 Kotlin 언어만의 독창적이고 특이한 내용들이 의외로 많이 존재하여 정리를 해야겠다는 생각이 들어 글을 적기 시작하였습니다. 저는 기초가 중요하다고 생각하는 사람 중 한 명입니다. 프로그래밍 실력을 사람으로 비유를 한다면 저는 하체는 약하고 상체는 강한 사람보다는 하체가 강하고 상체가 약한 사람이 더 발전 가능성이 있으며 앞으로 치고 나갈 수 있다고 생각을 합니다. 그럼 오늘의 글은 여기서 모두 마치도록 하겠습니다. 감사합니다.

 

참고 : Do it 코틀린 프로그래밍

Comments