Kotlin 제어 구문(Control statement)

조건문(Conditional statement)

if 문

if(expr) stmt1 else stmt2는 만약 expr이 참이면, stmt1을 그렇지 않으면 stmt2을 실행한다.

if (a > b) {
    println("Big")
} else {
    println("Small")
}

main 함수에 args가 비어 있지 않으면 받은 인수에 따라 문자열을 변경하도록 해보자. 조건에 따라 처리를 분기하는 구문은 Java에서도 인숙한 if-else 이다.

예제: args가 비어 있지 않은지 확인

fun main(args: Array<String>) {
  if (args.isNotEmpty()) {
    println("Hello, ${args[0]}!")
  } else {
    println("Hello, world!")
  }
}

isNotEmpty 메서드는 배열이 비어 있지 않은지 확인한다. 배열이 비어 않다면, isNotEmpty에서 true를 반환하고 if 다음에 오는 블록을 실행한다. 그렇지 않으면 else 다음에 오는 블록을 실행한다. Java의 if-else와 마찬가지로 분기 후에 블록 내의 문장이 하나인 경우에만 중괄호({ }) 를 생략하여 작성할 수 있다.

Kotlin에서 if-else는 식이다. if-else는 값을 반환한다.

var c = if (a > b) a else b

이는 자바의 삼항 연산자와 같은 동작을 한다. 위에 예제를 다시 작성하면 아래와 같이 된다.

예제: if-else는 수식이다. fun main(args: Array) { val name = if (args.isNotEmpty()) args[0] else “名無しさん” println(“Hello, ${name}!”) }

코틀린에서는 삼항식 연산식이 없고, 위와 같이 if문으로 표현해야 한다.

Java에서는 삼항 연산자를 (condition) ? a : b와 같이 작성하는데, Kotlin에서는 삼항 연산자를 지원하지 않는다.

반복문(Loop Control)

for 루프

for 루프문 기본 형태는 for (... in ...) ...이고, 범위나 배열, 컬렉션을 반복할때 사용된다.

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

for (i in 10 downTo 0 step 2) {
    println(i)
}

var colors = arrayOf("Red", "Green", "Blue")
for (color in colors) {
    println(color)
}

main 함수에 여러 개의 커멘드 라인 인수에 대해 모두 Hello를 출력하고 싶다면, for 루프를 사용한다.

예제: args의 각 요소에 Hello를 반복한다.

fun main(args: Array<String>) {
  for (name in args) {
    println("Hello, ${name}!")
  }
}

Kotlin for은 Java의 “확장 for 문” 과 유사하다. 루프 카운터를 늘려가는 스타일의 루프를 Kotlin for은 지원하지 않는다.

forEach 루프

forEachforEachIndexed는 배열이나 컬렉션에 대해서 처리를 반복한다.

var a = arrayOf("Red", "Green", "Blue")

a.forEach {
    value -> println(value)
}

a.forEachIndexed {
    index, value -> println("${index}: ${value}")
}

아래 예제는 목록 여러 형태로 반복하여 출력하는 프로그램이다.

ForTutorial.kt

fun main() {
    val langList = listOf("javascript", "python", "kotlin", "java", "go")

    println("========= for loop =============");
    for (item in langList) {
        println(item)
    }

    println("========= for loop index =============");
    for (index in langList.indices) {
        println("${index} = ${langList[index]}")
    }

    println("========= for each loop =============");
    langList.forEachIndexed { index, value ->
        println("$index = $value")
    }
}

Output:

========= for loop =============
javascript
python
kotlin
java
go
========= for loop index =============
0 = javascript
1 = python
2 = kotlin
3 = java
4 = go
========= for each loop =============
0 = javascript
1 = python
2 = kotlin
3 = java
4 = go

for문은 iteration한 값을 반복할 수 있다. for (item in list)라고 하면 list에 값을 하나하나 꺼내와 item 변수에 받아 올 수 있다.

for (index in list.indices)라고 작성하면 item이 몇번째에 위치하는 인덱스 값은 idx 변수에 받아 올 수 있다. 인덱스를 받아오기 때문 list[index]에 접근하여 해당하는 값을 가져올 수도 있다.

forEachIndexed() 메소드를 사용하면 indexvalue를 같이 받아올 수 있다. 자바에서는 따로 변수를 만들어 증가 연산자를 넣어주여 하지만, 코틀린에서는 이 메소드로 해결이 가능하다.

while 루프

while은 조건이 성립되는 동안 작업을 반복한다.

var i = 10
while (i > 0) {
    println(i--)
}

while을 활용한 코드는 아래와 같다.

WhileTutorial.kt

fun main(args:Array<String>) {
    val mostLang = listOf("javascript", "python", "kotlin", "java", "go")

    println("== while case 1 ==============")
    var i = 0
    while (true) {
        if (i == mostLang.size) {
            break
        }
        println(mostLang[i])
        i++
    }

    println("== while case 2 ==============")
    var j = 0
    while (j < mostLang.size) {
        println(mostLang[j])
        j++
    }
}
== while case 1 ==============
javascript
python
kotlin
java
go
== while case 2 ==============
javascript
python
kotlin
java
go

do-while 루프

do ... while은 조건이 성립되지 않을 때까지 루프를 반복한다.

var i = 10
do {
    println(i--)
} while (i > 0)

분기문(Branching statement)

break 문

break는 가장 안쪽의 루프를 빠져 나온다.

for (i in 1..5) {
    if (i == 2) {
        break
    }
    println(i)
}

continue 문

continue 가장 안쪽의 루프에 다음 반복을 시작한다.

for (i in 1..5) {
    if (i == 2) {
        continue
    }
    println(i)
}

when 식

when은 조건에 따라 다른 값을 반환한다. is문으로 데이터 타입에 따라서 처리를 배분할 수 있다.

fun describe(obj: Any): String =
    when (obj) {
        1          -> "One"
        in 2..3    -> "Two or Three"
        "Hello"    -> "Greeting"
        is Long    -> "Long"
        !is String -> "Not a string"
        else       -> "Unknown"
    }

switch case문과 비슷하게 보이지만 약간 다른다. 보통 switch 문에서 case는 한가지 타입만 들어가야 하는데, when에서는 모든 타입이 다 들어갈 수 있다. 로직이 완료되고 break문을 넣어서 빠져나올 필요도 없다. 해당 조건에 맞으면 바로 빠져 나오게 된다. default 대신에 else를 사용한다.

WhenTutorial.kt

fun whenFunc(value: Any) {
    when (value) {
        0 -> println("0 입니다.")
        "two" -> println("two 입니다.")
        3.14 -> println("3.4 입니다.")
        else -> println("아무것도 해당되지 않아요")
    }
}

fun main(args: Array<String>) {
    whenFunc(0)
    whenFunc("two")
    whenFunc(3.14)
    whenFunc(1234)
}
0 입니다.
two 입니다.
3.4 입니다.
아무것도 해당되지 않아요

그밖에

레이블(@)

@으로 레이블을 지정함으로써, breakcontinue의 대상 루프를 지정할 수 있다.

foo@ for (i in 1..5) {
    for (j in 1..5) {
        if (i == 3 && j == 3) break@foo
        println("$i $j")
    }
}

함수의 람다 식으로 return하면 일반적으로 함수에서 반환되지만 @ 레이블 지정하여 람다 식을 return하는 것이 가능하다.

fun foo() {
    listOf(1, 2, 3, 4, 5).forEach baa@ {
        if (it == 3) return@baa
        println(it)		// 1, 2, 4, 5
    }
}

클래스나 함수는 암시적으로 클래스명, 함수명의 레이블을 가지고 있다. 아래와 같이 내부 클래스에서 외부 클래스의 인스턴스를 참조하는 것도 가능하다.

class A {
    val foo = "AAA"
    inner class B {
        val foo = "BBB"
        fun foo() = println("${this.foo} ${this@A.foo}")	// BBB AAA
    }
}

fun main() {
    A().B().foo()
}

예외 처리 (try, throw, catch, finally)

예외 처리는 다음과 같은 예이다.

try {
    if (...) {
        throw IllegalStateException()
    } catch (e: Exception) {
        println(e)
    } finally {
        println("Finally")
    }
}

try는 예외 처리를 시작한다. throw 예외를 던진다. catch는 예외를 잡는다. finally는 예외 발생과 상관 없이 기술된 처리를 무조건 실행한다.