본문 바로가기
Kotlin/Kotlin 프로그래밍

[Kotlin] 프로그램의 흐름 제어 : 조건문, 반복문, 예외처리

by 주 녕 2021. 4. 14.
728x90

모든 내용은 Do it! 코틀린 프로그래밍을 바탕으로 정리한 것입니다. 

조건문

if 문과 if~else 문

    if (조건식) {
        수행할 문장 // 조건식이 true인 경우에만 실행
        ...
    }
    
    if (조건식) {
        수행할 문장 // 조건식이 true인 경우
    } else {
        수행할 문장 // 조건식이 false인 경우
    }
  • 수행할 문장이 하나인 경우, 블록 구문인 중괄호 생략 가능
  • 조건문을 한 줄에 구성할 때, 조건식에 따라 값을 할당하도록 변수 이름 단독으로 쓸 수 있음
  • 블록의 표현식이 길어질 때, 람다식처럼 블록의 마지막 표현식이 변수에 반환되어 할당됨

else if 문 ; 조건문 중첩

    val number = 0
    val result = if (number > 0)
            "양수 값"
        else if (number < 0)
            "음수 값"
        else
            "0"
  • 여러 조건을 적용하기 위해 else if를 사용하여 조건문을 중첩할 수 있음
  • 필요한 만큼 조건을 조합할 수 있지만, 너무 많으면 코드 읽기가 어려워짐

  • 포함 여부 확인 : in 연산자2개의 점(..)으로 구성된 범위 연산자 제공
    • 변수 이름 in 시작값..마지막값
    • 시작값과 마지막값까지 포함하는 범위를 계산함

 

when 문 ; 다양한 조건 처리

> 인자를 사용하는 when 문

    when (인자) {
        인자에 일치하는 값 혹은 표현식 -> 수행할 문장
        인자에 일치하는 범위 -> 수행할 문장
        ...
        else -> 수행할 문장
    }
  • 화살표(->) 왼쪽 : 일치하는 값, 표현식, 범위로 조건을 나타냄
    • 일치되는 조건을 한 번에 여러 개 표현하려면 쉼표(,) 사용 : 0, 1 -> print("x == 0 or x == 1")
    • 함수의 반환값 사용 가능 : parseInt(s) -> print("일치함!")
    • in 연산자와 범위 지정자 사용 가능 : !in 1..10 -> print("x는 1 이상 10 이하의 범위에 포함되지 않습니다.")
    • is 키워드 사용 가능(특정 자료형 검사) : is String -> "문자열입니다."
  • 화살표(->) 오른쪽 : 수행할 문장을 사용
    • 문장이 여러 줄이라면 중괄호를 사용해 블록으로 구성할 수 있음
  • 조건과 일치하는 값이 없으면 else문 다음에 작성한 문장을 실행
  • switch~case 문과 비슷하지만 각 수행 문장을 멈추는 break와 같은 문장이 필요하지 않음

> 인자가 없는 when 문

when문에 인자가 주어지지 않으면 else if 문처럼 각각의 조건을 실행할 수 있음 (조건이나 표현식을 직접 만들 수 있기 때문)

fun main() {
    print("Enter the score: ")
    val score = readLine()!!.toDouble()
    var grade: Char = 'F'

    when {
        score >= 90.0 -> grade = 'A'
        score in 80.0..89.9 -> grade = 'B'
        score in 70.0..79.9 -> grade = 'C'
        score < 70.0 -> grade = 'F'
    }
    println("Score: $score, Grade: $grade")
}

변수와 조건식을 when 문에 직접 사용함

 

> 다양한 자료형의 인자를 받는 when 문

fun main() {
    cases("Hello")  // 2
    cases(1)  // 1
    cases(System.currentTimeMillis())  // 3
    cases(MyClass())  // 4
}

fun cases(obj: Any) {
    when(obj) {
        1 -> println("Int: $obj")  // 1
        "Hello" -> println("String: $obj")  // 2
        is Long -> println("Long: $obj")  // 3
        !is String -> println("Not a String")  // 4
        else -> println("Unknown")  // 5
    }
}
  • when 문의 인자로 Any를 사용하면 다양한 자료형의 인자를 받을 수 있음
    • 숫자, 문자, 클래스의 객체

 


반복문

for 문

    for (요소 변수 in 컬렉션 또는 범위) { 반복할 본문 }
  • 자바의 for문 : 초기화식, 조건식, 증감식을 세미콜론(;)으로 구분
  • 코틀린의 for문 : in 연산자를 함께 사용 (세미콜론 사용 불가)
    • 역순 : downTo 키워드 사용  - for (i in 5 downTo 1) print(i)
    • 증감식 : step 키워드 사용  - for (i in 1..5 step 2) print(i)

while 문

    while (조건식) {
        본문
        ...
    }
  • 조건식이 true를 만족하는 경우 while 문의 블록을 무한히 반복
  • 조건식이 false가 되면 실행문이 중단되어 while 루프를 빠져 나감
  • 조건식을 true로 해 무제한으로 반복할 수 있음

do-while 문

    do {
        본문
    } while (조건식)
  • do 블록에 작성한 본문을 한 번은 실행한 다음 마지막에 조건식을 검사해서 true가 나오면 작업을 반복

 


흐름의 중단과 반환

흐름 제어문
  • return : 함수에서 결과값을 반환하거나 지정된 라벨로 이동
  • break : for문이나 while문의 조건식에 상관없이 반복문을 끝냄
  • continue : for문이나 while문의 본문을 모두 수행하지 않고 다시 조건식으로 넘어감
예외 처리문
  • try{...} catch{...} : try 블록의 본문을 수행하는 도중 예외가 발생하면 catch 블록의 본문을 실행
  • try{...} catch{...} finally{...} : 예외가 발생해도 finally 블록 본문은 항상 실행

 

return 문

  • return 이후의 코드는 실행되지 않음
  • Unit 반환 (Unit이라는 반환 값은 Unit이라는 자료형 자체를 반환하는 것 ≠ void)
    • 명시적으로 Unit을 반환하는 경우
    • Unit 이름을 생략한 반환 ( = 값 없이 return만 사용한 경우)
    • return 문 자체를 생략한 경우

> 람다식에서 return 사용하기

inline 으로 선언되지 않은 람다식에서는 return을 사용할 수 없음 (이전 포스팅 참고)

return@label과 같이 라벨 표기와 함께 사용해야 함

 * 라벨 : 코드에서 특정한 위치를 임의로 표시한 것, @ 기호와 이름을 붙여서 사용

 

  • 인라인 함수에서의 return은 람다식 바깥의 retFunc()까지 빠져나가게 됨 : 비지역 반환
  • 람다식에서 라벨을 정의하여 return을 사용 → 비지역 반환 방지
  • 라벨 지정 - 라벨 이름@ / 사용 시 - return@라벨 이름
  • 암묵적 라벨 : 람다식 명칭을 그대로 라벨처럼 사용
    • 따로 정의하지 않고 사용 시 return@람다식명칭

> 익명 함수에서 return

라벨을 사용하지 않고도 가까운 익명 함수 자체가 반환되므로 라벨을 사용한 람다식과 동일한 결과를 가질 수 있음

    fun retFunc() {
        println("start of retFunc")
        inlineLambda(13, 3) fun(a, b) {
            val result = a+b
            if (result > 10) return
            println("result: $result")
        }
        println("end of retFunc")
    }

보통의 경우에는 람다식을 사용하고 return과 같이 명시적으로 반환해야 할 것이 여러 개라면 익명 함수를 사용하는 것이 좋음

 

break 문과 continue 문

  • break : 더 이상 반복을 수행하지 않고 해당 블록을 빠져나감
  • continue : 조건식이 false가 되는 부분만 처리하지 않고 계속 반복문을 수행함

> 라벨 사용하기

라벨을 사용해서 반복문이 중단되는 위치를 바꿀 수 있음

→ continue도 마찬가지

  • 일반적인 break 였을 경우 : j가 3인 경우만 제외한 모든 i의 경우의 수가 출력되었을 것
  • first@ 라벨을 사용한 break : 라벨이 선언된 블록 전체를 break 하여 탈출함

 


예외 처리

예외(Exception) : 해당 코드가 제대로 작동하지 못하고 중단되는 현상

→ 실행 도중의 잠재적인 오류까지 검사할 수 없기 때문에 정상적으로 실행되다가 비정상적으로 프로그램이 종료될 수 있음

  • 운영체제의 문제 (잘못된 시스템 호출의 문제)
  • 입력값의 문제 (존재하지 않는 파일 또는 숫자 입력란에 문자 입력 등)
  • 받아들일 수 없는 연산 (0으로 나누기 등)
  • 메모리의 할당 실패 및 부족
  • 컴퓨터 기계 자체의 문제 (전원 문제, 망가진 기억 장치 등)

이러한 예외를 대비하는 것 = 예외 처리

    try {
        예외 발생 가능성 있는 문장
    } catch (e: 예외 처리 클래스 이름) { 
        예외를 처리하기 위한 문장
    } finally {
        반드시 실행되어야 하는 문장
    }

  • catch의 인자에 Exception 클래스는 일반적인 모든 예외를 가리킴
  • 위의 코드처럼 산술 연산에 대한 예외를 따로 특정해서 잡으려면 ArithmeticException을 사용할 수 있음
  • e.message : 객체 e의 멤버 변수 또는 프로퍼티로 불리는 message를 읽으면 예외 원인을 간단히 출력해 줌
  • e.printStackTrace() : 스택의 추적
    • 어떤 특정 예외가 발생했는지 알 수 있고, 오류가 발생한 코드의 줄을 확인할 수 있음
    • 추적 가능한 이유 : 프로그램이 디버깅 정보를 유지하고 있기 때문

예외 발생 시키기

throw 키워드 : 의도적으로 예외를 발생할 수 있음

fun main() {
    var amount = 600

    try {
        amount -= 100
        checkAmount(amount)
    } catch (e: Exception) {
        println(e.message)
    }
    println("amount: $amount")
}

fun checkAmount(amount: Int) {
    if (amount < 1000)
        throw Exception("잔고가 $amount 으로 1000 이하입니다.")
}

→ amount가 1000 이하일 때, throw로 예외를 발생시키고 이것은 main()의 catch가 잡아서 처리

 * 공을 던지고 받는 캐치볼 놀이를 생각하면 이해하기 쉬움

 

사용자 정의 예외

기본 Exception 클래스로부터 새롭게 사용자가 정의한 예외 클래스를 만들 수 있음

    class <사용자 예외 클래스 이름>(message: String) : Exception(message)

콜론(:)을 사용해 하위 클래스인 사용자 예외 클래스 이름을 지정해 예외로 만들 수 있음

class InvalidNameException(message: String) : Exception(message)

fun main() {
    var name = "Kildong123"
    try{
        validateName(name)
    } catch (e: InvalidNameException) {
        println(e.message)
    } catch (e: Exception) {
        println(e.message)
    }
}

fun validateName(name: String) {
    if(name.matches(Regex(".*\\d+.*"))) // 이름에 숫자가 포함되어 있으면 예외발생
        throw InvalidNameException("Your name: $name : contains numerals.")
}

 

728x90

댓글