관리 메뉴

너와 나의 스토리

[Kotlin] 코틀린 기초2 - 스마트 캐스트, 이터레이션(for 문, 컬렉션이나 원소 검사), 예외 처리 본문

Programming Language/Kotlin

[Kotlin] 코틀린 기초2 - 스마트 캐스트, 이터레이션(for 문, 컬렉션이나 원소 검사), 예외 처리

노는게제일좋아! 2021. 6. 11. 00:09
반응형

스마트 캐스트

interface Expr
class Num(val value: Int) : Expr // Expr 인터페이스를 구현한다
class Sum(val left: Expr, val right: Expr) : Expr
  • 위의 예제를 보자.
  • Expr 타입의 객체라면 어떤 것이나 Sum 연산의 인자가 될 수 있다. 즉, Num이나 다른 Sum이 인자로 올 수 있다.
  • (1+2)+4를 계산한다고 하자. 
fun eval(e: Expr): Int {
    if(e is Num){
        val n = e as Num
        return n.value
    }
    if(e is Sum){
        return eval(e.right)+eval(e.left)
    }
    throw IllegalArgumentException("Unknown expression")
}

fun main(args: Array<String>) {
    println(eval(Sum(Sum(Num(1), Num(2)),Num(4)))) // output: 7
}
  • 변수 타입을 검사하기 위해 is 명령어를 사용한다. -> 자바에서의 instanceof와 비슷
    • 일단 is 명령어로 검사하고 나면 마치 처음부터 그 변수가 원하는 타입으로 선언된 것처럼 사용할 수 있다.
    • if문을 보면 (e is Num)부분을 거친 후에 변수 e를 따로 Num으로 캐스팅하지 않아도 자연스럽게 Num으로 인식하고 있다.
    • 이렇게 컴파일러가 자동으로 캐스팅을 수행해주는 것을 스마트 캐스트(smart cast)라고 부른다.
  • 스마트 캐스트는 is로 변수에 든 값의 타입을 검사한 다음에 그 값이 바뀔 수 없는 경우에만 작동한다.
    • 즉, 스마트 캐스트를 사용하려면 그 프로퍼티는 반드시 val이어야 하며 커스텀 접근자를 사용한 것이어도 안 된다.
      • 커스텀 접근자를 사용하는 경우에는 val이 아니거나 val이지만 해당 프로퍼티에 대한 접근이 항상 같은 값을 리턴한다고 확신할 수 없기 때문이다.
  • 원하는 타입으로 명시적으로 타입 캐스팅하려면 as 키워드를 사용할 수 있다.
    • val n = e as Num
    • 객체가 타입 변환이 가능한 올바른 타입이 아니라면 ClassCastException이 발생한다.
    • 이런 경우를 예방하기 위해 as? 연산자를 사용할 수 있다.
    • val n = e as? Num
    • e를 Num 타입으로 변경할 수 없다면 null이 반환된다.

 

if와 when

  • 코틀린의 [ if ( a>b) a else b ]는 자바의 a>b ? a:b처럼 작동한다.
  • 즉, 코틀린은 if가 값을 만들어 내기 때문에 자바와 달리 3항 연산자가 따로 없다. 
  • 이런 특성을 이용해 위해서 작성한 eval() 메서드를 리팩토링할 수 있다.
fun eval(e: Expr): Int {
    when (e) {
        is Num ->
            e.value
        is Sum ->
            eval(e.right) + eval(e.left)
        else ->
            throw IllegalArgumentException("Unknown expression")
    }
}
  • is Num -> { ... } 형식으로 분기를 블록으로 만들 수 있다.

 

이터레이션: while 문과 for문

while 문

  • while문과 do-while 문이 있으며 이 두 루프의 문법은 자바와 동일하다. 작성 방식도 동일

for 문

  • 코틀린에서는 범위(range)를 사용해 for 문을 돌린다.
  • '..' 연산자로 시작 값과 끝 값을 연결해 범위를 표현한다.
    • a..b => [a, b]
      • 예: for ( i in 1..100 )
      • 1부터 100까지의 정수에 대해 이터레이션(끝 값도 포함)
    • 'A'..'F' => A, B, C, ..., F
  • until
    • a until b => [a, b)
    • 예: for ( i in 1 until 5)
    • 위 예제에서 i는 1, 2, 3, 4가 된다.
  • 증가 값을 가지고 범위 이터레이션할 수 있다.
    • 예: for( i in 100 downTo 1 step 2)
    • '100 downTo 1': 100에서 시작해서 1까지 내려가라는 소리 
    • 'step 2'는 2씩 값이 내려간다는 소리이다.
    • 위 예제에서 i는 100, 98, ..., 2가 된다.
  • 맵에 대한 이터레이션
val list = arrayListOf(10,11,12)

for((index, element) in list.withIndex()){
	println("$index: $element")
}
/*
output: 
0: 10
1: 11
2: 12
*/

 

 

in으로 컬렉션이나 범위의 원소 검사

fun isLetter(c: Char) = c in 'a'..'z' || c in 'A'..'Z'
fun isNotDigit(c: Char) = c !in '0'..'9'
  • when 식에서도 사용 가능하다.
fun recognize(c: Char) = when(c) {
    in '0'..'9' -> "It's a digit!"
    in 'a'..'z', in 'A'..'Z' -> "It's a letter!"
    else -> "I don't know"
}

 

 

코틀린의 예외 처리

예외 던지는 방법

  • 예외를 던지는 방법은 자바와 비슷하다.
fun isValid(percentage: Int) {
    if (percentage !in 0..100) {
        throw IllegalArgumentException(
            "A percentage value must be between 0 and 100: $percentage"
        )
    } 
}

 

try, catch, finally

  • 자바와 마찬가지로 try, catch, finally 절로 예외를 처리한다.
  • 다음은 파일에서 각 줄을 읽어 수로 변환하되 그 줄이 올바른 수 형태가 아니면 null을 반환하는 예제이다.
fun readNumber(reader: BufferedReader): Int? {
    try {
        val line = reader.readLine()
        return Integer.parseInt(line)
    } catch (e: NumberFormatException) {
        return null
    } finally {
        reader.close()
    }
}
  • 자바와 throws 절이 없다. 
    • 자바에서는 함수를 작성할 때 함수 선언 뒤에 throws IOException을 붙여줘야 한다. 
    • 이는 IOException이 checked exception이고, 자바에서는 checked exception을 명시적으로 처리해야 하기 때문이다.
    • 코틀린은 checked exception과 unchecked exception을 구별하지 않는다. 
    • 코틀린에서는 함수가 던지는 예외를 지정하지 않고, 발생한 예외를 잡아내도 되고 잡아내지 않아도 된다.
  • try를 식으로 사용할 수 있다.
    • 코틀린의 try 키워드는 if나 when과 마찬가지로 식이다.
    • 따라서 try의 값을 변수에 대입할 수 있다.
fun readNumber(reader: BufferedReader)  {
    val number = try {
        Integer.parseInt(reader.readLine())
    } catch (e: NumberFormatException) {
        throw NumberFormatException()
    }
    println(number)
}

fun main(args: Array<String>) {
    val reader1 = BufferedReader(StringReader("785"))
    readNumber(reader1)
    // output: 785
    
    val reader2 = BufferedReader(StringReader("not a number"))
    readNumber(reader2)
    // output: Exception in thread "main" java.lang.NumberFormatException
}

 

 

 

 

출처:

- [Kotlin IN ACTION]

- [코틀린을 다루는 기술]

 

반응형
Comments