kotlin basic (1)

💻 Programming

[Kotlin/JVM] 코틀린 기본문법 (Basic Syntax)

예전에 취미로 안드로이드 공부해서 자바로 어플하나 만들어 올린 적이 있었다.

너무 업데이트를 안해서 구글정책에 위배되어 해당 어플이 내려가 있었는데 그것도 모르고 놔두고 있었다.

최근에 수정해서 다시 올리는 작업을 했는데, 추가로 개발하는 1인 프로젝트들은 코틀린으로 해보고 싶은 마음이 생겼다.

아직 안드로이드 개발의 메인언어는 자바인 것 같긴 한데, 코틀린은 IntelliJ IDE를 개발한 JetBrain사에서 자바의 단점을 보완하기 위해서 개발한 언어로 자바처럼 JVM위에서 돌아갈 수 있고, 안드로이드, 자바스크립트와도 쉽게 연동할 수 있고 네이티브 바이너리를 만들 수도 있도록 개발되었다고 한다. (더 자세한 정보는 코틀린 공식사이트 FAQ에서 확인할 수 있다.)

 

나는 우선 자바 개발자이기 때문에 Kotlin/JVM 으로 코틀린을 연습해보려 한다. (Kotlin/JS를 이용하면 자바스크립트로 개발도 가능하다)

코틀린 프로젝트를 만들어서 Hello World 를 출력하는 예제는 코틀린 공식사이트에서 확인하기 바란다.

 

그 첫 번째 시간으로 코틀린 파일의 기본 구조가 어떻게 생겼는지, 그리고 변수 정의는 어떻게 하는지 자바와 어떻게 다른지 살펴보았다.

 

1. 코틀린 파일 구조

코틀린 헬로월드

- 코틀린 파일은 .kt 확장자를 갖는다.

- 자바는 class 블럭 내에 main메서드가 존재하지만 코틀린의 메인함수는 클래스 블럭내에 존재하는 것이 아니다.

- package 선언부 및 import 문은 자바와 동일하다.

- 코틀린에서 함수는 function의 축약어인 fun 으로 시작하고 리턴타입은 함수명() 뒤쪽에 위치한다.

- 코틀린에서는 statement를 세미콜론으로 종료하지 않는다.

2. 변수의 타입, 정의 및 초기화

코틀린에서 변수의 타입은 자바의 그것과 동일하지만 정의하는 방법이 다르다.

데이터 타입에 관계없이 상수는 val, 변수는 var로 선언한다.

데이터 타입을 명시해주지 않아도 할당해주는 값에 따라 자동으로 결정된다.

코틀린에서는 val을 read-only variable로 설명을 한다.

일반적인 변수는 var를 이용하고 read-only 변수는 val을 사용한다

val은 자바의 final과 같기 때문에 다른 값을 재할당 하려할 경우 컴파일 오류가 발생한다.

val 키워드로 선언된 변수에 다른 값을 재할당 하려할 경우 컴파일 오류 발생

var 키워드로 선언한 변수의 경우 타입이 한번 정해지면 다른 타입을 할당할 수 없다.

타입이 정해진(Int) var 키워드로 정의한 변수에 다른 타입(String)의 값 재할당 시도 시 컴파일 오류 발생

 

2-1. 변수 정의

코틀린에서 변수의 정의(define)은 아래와 같이 한다. 초기화를 하지 않고 정의만 할 경우 타입을 필수로 명시해줘야 한다.

    fun main() {
        var i:Int
        var s:String
        var l:Long
        var f:Float
        var d:Double
    }

 

2-2. 변수 초기화

변수의 초기화는 각 변수의 타입에 맞는 값을 할당해주면 된다.

    fun main() {
        var i:Int
        var s:String
        var l:Long
        var f:Float
        var d:Double

        i = 10
        s = "string"
        l = 10L
        f = 10.2F
        d = 10.5

        var v = "string variable"	// String 타입 변수
        var n = 123		// Int 타입 변수
    }

 

 

3. 함수 정의

두 개의 Int타입 파라미터를 전달받아 Int 타입을 반환하는 함수

    fun sum(a: Int, b: Int): Int { return a + b}

 

직접 값을 할당해주는 함수 (위 함수와 동일한 함수이다)

    fun sum(a: Int, b: Int) = a + b

 

리턴값이 없는 함수는 Unit (자바의 void) 으로 정의

    fun printSum(a: Int, b: Int): Unit { println("sum of $a and $b is ${a + b}")}

 

Unit 리턴타입은 안써줘도 된다.

    fun printSum(a: Int, b: Int) { println("sum of $a and $b is ${a + b}")}

 

4. 스트링 템플릿

    var a = 1
    // simple name in template:
    val s1 = "a is $a" 

    a = 2
    // arbitrary expression in template:
    val s2 = "${s1.replace("is", "was")}, but now is $a"

자바에서 String 클래스가 갖고있는 format 메서드와 유사한 기능을 하는 것으로 String의 내용이 가변적일 때 사용할 수 있다.

 

5. 조건문

조건문 사용법은 자바와 다를게 없다.

    fun maxOf(a: Int, b: Int): Int {
        if (a > b) {
            return a
        } else {
            return b
        }
    }

하지만 코틀린에서는 if문을 하나의 표현식으로 사용이 가능하다는 점이 다르다.

    fun maxOf(a: Int, b: Int) = if (a > b) a else b

if문의 결과값이 maxOf함수의 리턴값이라고 정의한 것으로, 바로 위에 maxOf함수와 동일한 함수라고 볼 수 있다.

 

6. Null

코틀린에서 반환값이 nullable인 경우 아래와 같이 표현할 수 있다.

    fun parseInt(str: String): Int? {
        // 리턴타입이 기본적으로 Int이나 null일 수도 있는 함수
    }

그리고 위 함수를 사용하는 경우를 보면.

    fun printProduct(arg1: String, arg2: String) {
        val x = parseInt(arg1)
        val y = parseInt(arg2)

        // 여기서 `x * y` 연산을 바로 해버리면 x또는 y가 null일 경우 에러가 발생할 수 있다.
        // 따라서 null 체크를 먼저 해야한다.
        if (x != null && y != null) {
            // null 체크가 완료되면 x 와 y 는 자동으로 non-nullable로 캐스팅 된다 
            // (코틀린 공식 문서에 이렇게 나와있는데 non-nullable로 캐스팅 된다라는게 아직 잘 이해는 안된다. 그런 타입이 있다는건지..)
            println(x * y)
        }
        else {
            println("'$arg1' or '$arg2' is not a number")
        }    
    }

이렇게 null 체크를 하고나면 non-nullable로 캐스팅이 된다고 한다.

그리고 이는 일반 타입에서도 마찬가지이다.

 

7. Type Check and Automatic Casts

    fun getStringLength(obj: Any): Int? {
        if (obj is String) {
            // 이 블럭 내에서 `obj`는 자동으로 String 타입으로 변환되며 String에서 제공하는 기능사용이 가능
            return obj.length
        }

        // `obj`는 타입 체크 블럭 밖에서는 여전히 어떤 타입도 될 수 있는 상태이다
        return null
    }

타입 체크 시 반대로(not을 이용) 할 경우 

    fun getStringLength(obj: Any): Int? {
        if (obj !is String) return null

        // 위 if문에서 `obj`가 String이 아니면 null을 리턴하기 때문에, 여기로 내려왔다면 무조건 String일 수 밖에 없다.
        // 따라서 자동으로 String 타입으로 변환되며, String에서 제공하는 기능사용이 가능하다.
        return obj.length
    }

또는 이렇게도 사용가능하다.

    fun getStringLength(obj: Any): Int? {
        // `&&`연산의 오른쪽에서는 obj가 String이라는 것을 확인한 뒤이므로,
        // String 으로 자동 형변환되어 String 에서 제공하는 기능사용이 가능하다
        if (obj is String && obj.length > 0) {
            return obj.length
        }

        return null
    }

 

8. For-Loop, While-Loop

loop는 어느 프로그래밍 언어나 다 비슷하다. 코틀린에서도 자바의 foreach문 처럼 for-loop를 사용할 수 있다.

    val items = listOf("apple", "banana", "kiwifruit")
    for (item in items) {
        println(item)
    }

인덱스 정보를 이용해야 경우 아래처럼 사용가능하다.

    val items = listOf("apple", "banana", "kiwifruit")
    for (index in items.indices) {
        println("item at $index is ${items[index]}")
    }

while-loop는 아래처럼 사용할 수 있다.

    val items = listOf("apple", "banana", "kiwifruit")
    var index = 0
    while (index < items.size) {
        println("item at $index is ${items[index]}")
        index++
    }

 

9. when 표현식

when expression은 자바의 switch-case문과 유사한데, 함수형 프로그래밍에 맞게 변형시켜놓은 것 같다

    fun describe(obj: Any): String =
        when (obj) {
            1          -> "One"
            "Hello"    -> "Greeting"
            is Long    -> "Long"
            !is String -> "Not a string"
            else       -> "Unknown"
        }

 

10. Range (범위)

자바에서는 loop를 돌릴 때 인덱스를 이용해서 아래처럼 많이 사용한다

    for (int i = 0; i < x; i++) {
        // ...
    }

그리고 이를 코틀린으로 옮기면 아래와 같다

    for (z in 1..10) {
        println(z)
    }

그럼 만약 매 loop시마다 i값의 증분을 i++가 아니라 i=i+2 처럼 써야한다면??? 어떻게 쓸 수 있을까?

    for (x in 1..10 step 2) {
    	// 1부터 10까지 2씩 증가시키면서 출력
        // 결과: 1~10 중에서 홀수 출력 => 13579
        print(x)
    }
    println()
    for (x in 9 downTo 0 step 3) {
    	// 큰 숫자에서 작은 숫자로 내려가면서 loop를 도는 케이스에서는 downTo 키워드를 사용한다
        // 결과: 9부터 시작해서 3씩 내려가면서 x가 0보다 크거나 같을 때 까지 숫자 출력 => 9630
        print(x)
    }

이렇게 뒤에 step 키워드를 이용하여 얼마씩 증가시킬 것인지를 써주면 된다.

 

또한 인덱스가 특정 범위를 벗어났는지는 아래와 같이 ! 연산자를 in과 함께 사용하여 표현할 수 있다

    val list = listOf("a", "b", "c")

    if (-1 !in 0..list.lastIndex) {
        println("-1 is out of range")
    }
    if (list.size !in list.indices) {
        println("list size is out of valid list indices range, too")
    }

 

 

 

 

referene: 코틀린 공식문서 - 기본문법

 

Basic Syntax - Kotlin Programming Language

 

kotlinlang.org