[Kotlin] 코틀린의 기초 2

코틀린의 기초 1에 이어 코틀린의 기초 2를 시작한다.


선택 표현과 처리: enum과 when

enum 클래스 정의

코틀린에서 enum은 소프트 키워드라 부르는 존재이다. enum은 class앞에 있을 때는 특별한 의미를 지니지만 다른 곳에서는 이름에 사용할 수 있다. 반면 class는 키워드이므로 이름으로 사용할 수 없다.

자바와 마찬가지로 enum은 단순히 값만 열거하는 존재가 아니다. enum 클래스 안에도 프로퍼티나 메소드를 정의할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
enum class Color (
val r: Int, val g: Int, val b: Int
) {
RED(255, 0, 0), ORANGE(255, 165, 0),
YELLOW(255, 255, 0), GREEN(0, 255, 0), BLUE(0, 0, 255),
INDIGO(75, 0, 130), VILOET(238, 130, 238);

fun rgb() = (r * 256 + g) * 256 + b
}

>>> println(Color.BLUE.rgb())
255

when

자바의 switch에 해당하는 코틀린 구성 요소는 when이다. if와 마찬가지로 when도 값을 만들어내는 식이므로 따라서 식이 본문인 함수에 when을 바로 사용할 수 있다. 아래 예는 when과 enum을 사용하여 빨주노초파남보를 외우는 식이다.

1
2
3
4
5
6
7
8
9
10
11
fun getMnemonic(color: Color) = 
when(color) {
Color.RED -> "Richard"
Color.ORANGE -> "Of"
Color.YELLOW -> "York"
Color.GREEN -> "Gave"
Color.BLUE -> "Battle"
Color.INDIGO -> "In"
Color.VIOLET -> "Vain"
else -> "Other"
}

자바와 다른 점은 break 를 넣지 않아도 되며 결과를 바로 리턴할 수 있는 식의 형태이다. 만약 여러개를 매치하기 위해서는 ,를 사용하여 분리한다.

1
2
3
4
fun GetMnemoic(color: Color) =
when(color) {
Color.RED, Color.YELLOW -> "blah"
}

코트린의 when은 임의의 개체를 사용하여 분기 처리도 가능하다.

1
2
3
4
5
6
7
8
9
10
fun mix(c1: Color, c2: Color) =
when(setOf(c1, c2)) {
setOf(RED, YELLOW) -> ORANGE
setOf(YELLOW, BLUE) -> GREEN
setOf(BLUE, VIOLET) -> INDIGO
else -> throw Exception("Dirty Color")
}

>>> println(mix(BLUE, YELLOW))
GREEN

또한 아예 인자가 없이도 사용이 가능하다.

1
2
3
4
5
6
7
fun mixOptimized(c1: Color, c2: Color) =
when {
(c1 == RED && c2 == YELLOW) ||
(c1 == YELLOW && c2 == RED) ->
ORANGE
...
}

when에 아무 인자도 없으려면 각 분기의 조건이 Boolean 결과를 계산하는 식이어야 한다.

스마트 캐스트: 타입 검사와 타입 캐스트를 조합

코틀린에서는 자바와는 다르게 타입 검사와 타입 캐스트가 동시에 이뤄진다. 자바의 경우 instanceof로 타입 검사 이후 캐스팅하여 사용을 해야하지만 코틀린에서는 검사와 동시에 캐스팅이 되어 그대로 변수를 사용이 가능하다.

1
2
3
4
5
6
7
8
9
10
// java
if (obj instanceof Person) {
Person person = (Person) obj;
System.out.println(person.getName());
}

// kotlin
if (obj is Person) {
println(obj.name)
}

대상을 이터레이션

while 루프

코틀린에서는 whiledo-while 루프가 있다. 두 루프의 문법은 자바와 다르지 않다.

1
2
3
4
5
6
7
while (조건) {
/*...*/
}

do {
/*...*/
} while (조건)

for 루프

코틀린에서 for 루프는 변수의 초기화, 조건, 증분에 대한 요소가 없다. 자바와 동일한 반복문을 작성하기 위해서는 range라는 범위 키위둬를 사용한다. range는 기본적으로 두 값으로 이뤄진 구간이며 폐구간(양 끝을 포함하는) 이다. 사용법은 아래와 같다.

1
2
3
for (i in 1..100) // 1부터 100(포함) 범위의 정수에 대해 이터레이션
for (i in 100 downTo 1 step 2) // 100부터 1(포함) 범위를 2씩 감소하며 이터레이션
for (i in 0 until size) // 0부터 size(비포함)까지 이터레이션

맵에 대한 이터레이션

for 루프는 범위 뿐만 아니라 컬렉션에 대한 이터레이션도 가능하다. 이번에 보일 것은 키와 값으로 이뤄진 맵에 대한 이터레이션이다.

1
2
3
4
5
6
7
8
9
val binaryRpes = TreeMap<Char, String>()
for (c in 'A'..'F') {
val binary = Integer.toBinaryString(c.toInt())
binaryReps[c] = binary
}

for ((letter, binary) in binaryReps) {
println("${letter} = ${binary}")
}
  • 숫자 뿐만 아니라 문자 타입에도 범위를 적용
  • 자바에서의 맵과 사용법이 조금 다름
    1
    2
    3
    4
    5
    6
    7
    // java
    binaryReps.put(c, binary)
    binary = binaryReps.get(c)

    // kotlin
    binaryReps[c] = binary
    binary = binaryReps[c]

위와 같이 맵에 대해 for 루프를 적용할 수 있으며 컬렉션에서도 활용이 가능하다. 컬렉션의 원소와 인덱스를 유지하면서 컬렉션을 이터레이션할 수 있다.

1
2
3
4
val list = arrayListOf("10", "11", "1001")
for ((index, element) in list) {
println("${index} : ${element}")
}

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

in 연산자를 사용해 어떤 값이 범위에 속하는지 검사할 수 있다. 반대로 !in을 사용하면 어떤 값이 범위에 속하지 않는지 검사할 수 있다.

1
2
3
4
5
6
7
fun isLetter(c: Char) = c in 'a'..'z' || c in 'A'..'Z'
fun isNotDigit(c: Char) = c !in '0'..'9'

>>> println(isLetter('q'))
true
>>> println(isNotDigit('x'))
false

위의 함수식을 when과 함께 사용하면 더 좋은 코드로 변경된다.

1
2
3
4
5
6
7
8
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"
}

>>> println(recognize('8'))
It's a digit!

코틀린의 예외 처리

try, catch, finally

자바와 마찬가지로 예외를 처리하려면 try, catch, finally 절을 함께 사용한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun readNumber(reader: BufferedReader): Int? {
try {
val line = reader.readLine()
return Integer.parseInt(line)
}
catch (e: NumberFormatException) {
return null
}
finally {
reader.close
}
}

>>> val reader = BufferedReader(StringReader("239"))
>>> println(readNumber(reader))
239

자바와 다른 점은 catch에서 지정하지 않은 다른 예외의 대한 처리이다. 자바에서는 명시적으로 throws를 통해 IOException, Exception 등 지정하지 않은 예외를 처리해야 했으나 코틀린에서는 최신의 JVM 언어와 마찬가지로 체크 예외와 언체크 예외를 두었다. 만약 언체크 예외가 발생했을 때 잡아내도 되고 잡아내지 않아도 되게끔 하였다. 그 이유는 프로그래머들이 의미 없는 예외를 다시 던지거나 잡는 불편함을 없애기 위함이다.

try를 식으로 사용

앞서 설명했듯이 코틀린에서는 대부분이 식이다. 그러므로 try또한 식으로 사용할 수 있다. 앞에서 예들 들었던 코드를 식으로 처리할 수도 있다.

1
2
3
4
5
6
7
8
9
fun readNumber(reader: BufferedReader) {
val number = try {
Integer.parseInt(reader.readLine())
} catch (e: NumberFormatException) {
return // <- return 절 밑으로 실행하지 않으려면 return을 사용
null // <- 예외 발생시 계속 진행하고 싶다면 값을 반환
}
println(number)
}

출처 : Kotlin in Acation

#

댓글

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×