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(): AnimalhasNext(): 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
이 저작물은 크리에이티브 커먼즈 저작자표시-비영리-동일조건변경허락 4.0 국제 라이선스에 따라 이용할 수 있습니다. 



Comments