5일차 시작하기 - Control Flow
지난 4일차까지 Introduction
의 내용으로 코틀린의 기본적인 Functions(함수), Classes(클래스), Variables(변수) 와 Generics(제네릭), Null Safety, Inheritance(상속)에 대하여 학습하였다.
오늘은 모든 언어의 가장 기본적인고 빠질 수 없는 Control Flow(제어흐름, 제어문, 제어식)에 대해 학습을 시작한다. 코틀린도 다른 언어와 별반 다르지 않을 것이라 생각된다. 학습을 하면서 무엇이 다른지 확인해보자.
When
처음부터 신기한 녀석이 나왔다. 일반적인 언어에서 사용하는 switch
와 같은 녀석이다. 코틀린에서는 더 유연하고 명확한 구성을 위해 when
이라는 이름을 붙인 것 같다.
문(statement) 또는 식(expression)으로 사용 가능하다.
When Statement
fun main() {
cases("Hello")
cases(1)
cases(0L)
cases(MyClass())
cases("hello")
}
fun cases(obj: Any) {
when (obj) { // 1
1 -> println("One") // 2
"Hello" -> println("Greeting") // 3
is Long -> println("Long") // 4
!is String -> println("Not a string") // 5
else -> println("Unknown") // 6
}
}
class MyClass
/* 실행 결과
Greeting
One
Long
Not a string
Unknown
*/
when
을statement
로 사용하여 정의한다.obj
가 숫자(Number)1
일 경우One
이라는 값을 출력한다.obj
가 문자(String)Hello
일 경우Greeting
이라는 값을 출력한다.obj
가Long
타입일 경우Long
이라는 값을 출력한다.obj
가String
타입이 아닌 경우Not a string
이라는 값을 출력한다.obj
가 위 모든 조건이 아닌 경우Unknown
이라는 값을 출력한다.- 일반적인
switch
문의default
와 같다. 일발적인 언어의
switch
구조와 비교하면case
라는 키워드를 사용하지 않고, 기본값을 표현하는default
대신else
를 사용한다.// Javascript의 경우 switch(obj) { case [일치하는 값]: // 일치할 경우 실행할 내용 break; default: // 일치하는 것이 없을 경우 실행할 내용 }
// 코틀린의 경우 when(obj) { [조건] -> [리턴할 값 or 실행할 내용] [조건] -> { // 여러줄의 실행할 내용 } else -> [리턴할 값 or 실행할 내용] // 위 조건에 일치하는 내용이 없을 경우 실행할 내용 } // 한 줄로 표현할 때는 `{}`를 생략하고, 여러 줄을 표현할 때는 `{}`로 감싸준다.
다른 언어의
switch
는 일치하는 값이면, 해당case
의 시작에서break
가 나올 때까지의 내용을 수행했지만, 코틀린의when
은{}
로 감싸주거나 한 줄로 표현한다.break
라는 것이 없고, 분기의 조건에 일치하면 해당 분기를 바로 실행한다.
- 일반적인
When Expression
fun main() {
println(whenAssign("Hello"))
println(whenAssign(3.4))
println(whenAssign(1))
println(whenAssign(MyClass()))
}
fun whenAssign(obj: Any): Any {
val result = when (obj) { // 1
1 -> "one" // 2
"Hello" -> 1 // 3
is Long -> false // 4
else -> 42 // 5
}
return result
}
class MyClass
/* 실행 결과
1
42
one
42
*/
when
을expression
으로 사용하여 정의한다.obj
의 값에 따라result
에 담기는 값이 달라지게 된다.
obj
가 숫자1
인 경우result
에one
을 설정한다.obj
가Hello
인 경우result
에 숫자1
을 설정한다.obj
가Long
타입인 경우result
에 Booleanfalse
을 설정한다.obj
가 위 조건에 모두 맞지 않을 경우result
에42
를 설정한다.
이렇게 when
을 expression
으로 사용하는 경우에는 모든 조건이 일치하지 않는 경우를 대비하여 마지막 else
를 넣어주는 것이 좋다.
추가로, switch
의 경우 값과 일치하는 case
가 나오고 break
가 없으면 break
가 나올 때까지 하위 case
의 내용도 모두 수행하였다. 이 특성을 이용해서 case
가 다르더라도 동일한 결과를 주도록 코드를 작성할 수 있었다. 코틀린에서는 조건이 일치하는 경우 해당 분기의 내용만 실행하기 때문에 이전의 switch
할 수 없다. 대신, when
의 경우 각 분기의 조건을 입력하기 때문에 조건으로 처리하거나, 값을 콤마(,)로 구분하여 나열하면 된다.
위 이미지에서처럼 in
을 이용하여 범위를 지정하여 해당 범위의 값이 일치하는 경우 또는 !in
을 이용하여 해당 범위가 아닌 경우를 조건으로 사용할 수 있고, is
와 !is
를 이용하여 타입을 검사할 수 있다.when
의 특이점으로 인자(argument)를 전달하지 않으면, 분기 조건이 단순 Boolean 표현이 되어 if-else if
와 같은 표현을 대체해서 사용할 수 있다.
when {
x.isOdd() -> print("x is odd")
y.isEven() -> print("y is even")
else -> print("x+y is odd.")
}
코틀린 1.3버전부터는 아래와 같은 표현도 가능하다.
fun Request.getBody() = when (val response = executeRequest()) { is Success -> response.body is HttpError -> throw HttpException(response.status) }
https://kotlinlang.org/docs/reference/control-flow.html#when-expression 참고
Loops
코틀린은 다른 언어에서 일반적으로 사용되는 for
, while
, do-while
을 지원한다.loop
안에서 사용 가능한 break
와 continue
도 지원한다.
참고: https://kotlinlang.org/docs/reference/returns.html
for
코틀린에서 for
의 동작은 방식은 대부분의 다른언어와 동일하다. 코틀린 레퍼런스에서는 C#과 같은 언어의 foreach
와 비슷하다고 한다.
그리고 iterator
를 제공하는 모든 것을 반복한다. 참고: https://kotlinlang.org/docs/reference/control-flow.html#for-loops
val cakes = listOf("carrot", "cheese", "chocolate")
for (cake in cakes) { // 1
println("Yummy, it's a $cake cake!")
}
cakes
목록의 각 항목을 반복한다.
while and do-while
코틀린에서의 while
, do-while
의 동작 방식도 대부분의 다른언어와 동일하다. 코틀린의 레퍼런스에서도 다른 내용은 없었다.
참고: https://kotlinlang.org/docs/reference/control-flow.html#for-loops
fun eatACake() = println("Eat a Cake")
fun bakeACake() = println("Bake a Cake")
fun main(args: Array<String>) {
var cakesEaten = 0
var cakesBaked = 0
while (cakesEaten < 5) { // 1
eatACake()
cakesEaten ++
}
do { // 2
bakeACake()
cakesBaked++
} while (cakesBaked < cakesEaten)
}
cakesEaten < 5
조건이 성립하는 동안 블럭안의 내용을 반복해서 수행한다.cakesBaked < cakesEaten
조건의 성립과 관계 없이 1회 반복 후 조건이 성립하면 반복 수행한다.
Iterators
iterator
연산자를 클래스에 추가하여 클래스에 iterator
를 정의한다.
class Animal(val name: String)
class Zoo(val animals: List<Animal>) {
operator fun iterator(): Iterator<Animal> { // 1
return animals.iterator() // 2
}
}
fun main() {
val zoo = Zoo(listOf(Animal("zebra"), Animal("lion")))
for (animal in zoo) { // 3
println("Watch out, it's a ${animal.name}")
}
}
Zoo
클래스에operator
키워드를 이용하여iterator
를 정의한다. 이름은 반드시iterator
야 한다.operator
키워드는 2일차에 학습했던 Operator Functions를 참고하자.다음 메서드 요구사항을 충복하는
iterator
를 반환합니다.next(): Animal
hasNext(): Boolean
1.
번에서iterator()
를 정의할때 리턴타입으로 사용된Iterator<Animal>
의interface Iterator
는 기본으로hasNext()
,next()
를 가지고 있습니다. 자세한 내용은 https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-iterator/ 참고1.
번에서 정의한iterator()
를 이용하여,Zoo
클래스를 반복하여animal
을 반복 출력합니다.
iterator
는 타입이나 확장 함수(extension function)로 정의 가능하다 하는데, 좀 더 예제를 따라 하거나 레퍼런스를 참고해봐야 이해가 될 것 같다.
참고: https://kotlinlang.org/docs/reference/iterators.html
Ranges
범위를 정의하기 위해 코틀린에서 제공하는 도구(tools)이다. 다른 언어에서도 이런 방식으로 사용했던 것 같은데, 기억이 안 난다.
for(i in 0..3) { // 1
print(i)
}
print(" ")
for(i in 0 until 3) { // 2
print(i)
}
print(" ")
for(i in 2..8 step 2) { // 3
print(i)
}
print(" ")
for (i in 3 downTo 0) { // 4
print(i)
}
print(" ")
0에서 3까지 반복하는
for
를 정의합니다.i in 0..3
은i
의 값이0
부터3
까지 증가하며3
을 포함한다.C/C++/Java
의 경우for(i=0; i<=3; ++i)
와 같이 사용한 것이다.
0에서 3이전까지 반복하는
for
를 정의합니다.i in 0 until 3
은i
의 값이0
부터2
까지 증가하며3
을 포함하지 않는다.Python
의for loop
와 같고,C/C++/Java
의 경우for(i=0; i<3; ++i)
와 같이 사용한 것이다.
2에서 8까지 반복하는
for
를 정의합니다.i in 2..8 step 2
는i
의 값이2
부터2
씩 증가하여8
을 포함한다. 출력결과는2468
이다.C/C++/Java
의 경우for(i=2; i<=8; i=i+2)
와 같이 사용한 것이다.
3에서 0까지 반복하는
for
를 정의합니다.i in 3 downTo 0
는i
의 값이3
부터0
까지 감소하며0
을 포함한다.C/C++/Java
의 경우for(i=3; i>=0; --i)
와 같이 사용한 것이다.
범위로 문자(Char
)도 지원한다.
for (c in 'a'..'d') { // 1
print(c)
}
print(" ")
for (c in 'z' downTo 's' step 2) { // 2
print(c)
}
print(" ")
a
부터d
까지 알파벳 순서대로 반복된다.- 출력결과는
abcd
이다.
- 출력결과는
z
부터s
까지 알파벳의 역순으로 반복된다.- 문자(
Char
)에서도downTo
,step
사용이 가능하다. step 2
가 있어 2단계식 낮아져, 출력결과는zxvt
이다.
- 문자(
if
에서도 이용 가능하다.
val x = 2
if (x in 1..5) { // 1
print("x is in range from 1 to 5")
}
println()
if (x !in 6..10) { // 2
print("x is not in range from 6 to 10")
}
x
가 범위안에 있으면x is in range from 1 to 5
를 출력한다.x
가 범위안에 없으면x is not in range from 6 to 10
를 출력한다.!
를 사용하여 부정으로 표현한다.
Range
에 대한 더 자세한 내용은 https://kotlinlang.org/docs/reference/ranges.html 참고
Equality Checks
코틀린에서의 ==
과 ===
의 차이점이다.==
는 구조 비교(structural comparison
)를 위해 사용한다.===
는 레퍼런스 비교(referential comparison
)를 위해 사용한다.
예제에서는
a == b
는if (a == null) b == null else a.equals(b)
로 컴파일 된다고 하는데, 이게 더 무슨 말인지 모르겠다. 결국a.equals(b)
라는 것인가? 앞에if (a == null) b == null
의미는 무엇일까?
val authors = setOf("Shakespeare", "Hemingway", "Twain")
val writers = setOf("Twain", "Shakespeare", "Hemingway")
println(authors == writers) // 1
println(authors === writers) // 2
- 결과는
true
이다. 이유는authors.equals(writers)
를 호출하게 되고, 안에 단계전 요소(element
)의 순서를 비교하지 않는다. (순서는 사실상 무시된다.) 결과는
false
이다. 이유는authors
와writers
가 따로 정의하였기 때문에 별도의 레퍼런스를 가지고 있어서 이다.- 자칫
authors
와writers
의setOf
한 데이터의 순서가 달라서false
일꺼라 생각할 수 있는데, 이는 전혀 상관이 없다. - 레퍼런스의 비교이기 때문에 애초
setOf
로 정의할때 서로다른 레퍼런스를 가지게 되었으므로===
로 비교해서는 둘은 절대true
가 나올 수 없다.
- 자칫
Conditional Expression
코틀린(Kotlin
)은 삼항연산자(ternary operator
)가 없다. 코틀린의 if
포현식에서 값을 반환하기 때문이다. 그래서 if
를 사용하면 된다.
fun max(a: Int, b: Int) = if (a > b) a else b // 1
println(max(99, -42))
(a > b)
조건이true
일 경우a
를 리턴하고,false
일 경우b
를 리턴한다.
우리가 흔히 생각하는 if
와는 조금 다른듯 하다. if
는 {}
을 사용할 수 있는데, 이경우 {}
의 가장 마지막줄의 내용이 리턴된다. when
과 마찬가지로 조건에 따라 바로 어디가에 값을 할당하거나 리턴해야 하는 경우에는 반드시 else
를 추가 해야 한다. if
의 표현에 대한 좀더 자세한 내용은 https://kotlinlang.org/docs/reference/control-flow.html#if-expression 참고한다.
5일차 마무리 & Next
오늘은 코틀린(Kotlin
)의 Control Flow
에 대해서 학습하였다.
적어도 1주일에 하나씩은 해야지 하였는데, 생각대로 되지 않고 있다. 시작은 작년 11월 말이었는데, 벌써 새해가 밝아 1월 중순이 지나고 있다. 하지만 나의 진도는 아직도 기초에서 벗어나지 못하고 있다.
처음 계획이 예제를 하나씩 따라 하다 보면 금방 하겠지였는데, 예제 하나만으론 부족하여 자연스럽게 레퍼런스를 참고하고, 서치를 조금씩 하다 보니 내용이 늘어나고 시간이 늘어나는 것 같다. 그래도 이렇게 하나씩 하나씩 하다 보면 언젠간 다 보는 날이 올 것이라 생각하고, 학습 로그를 꾸준히 남기겠다.
다음시간에는 코틀린의 Special Classes
에 대해 학습하도록 하겠다.
- Kotlin 무작정 따라해보기 관련 Post
Comments