when 문 : https://kotlinlang.org/docs/control-flow.html#when-expression
최신 언어들은 switch-case 문을 안쓰는 추세다. 대신 더 유연하고 진보적인 when문이 생김. when문은 switch-case와 유사하게 조건 표현식에 따라 여러 경우의 수에 대해 처리를 달리 해주는 표현식이다.
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> {
print("x is neither 1 nor 2")
}
}
swich 문과 다르게 break나 continue가 없다. 대신 순차적으로 조건을 체크하면서 만족하는 브랜치를 찾아 실행하게 된다. when은 그냥 statement로도 쓰일 수 있지만, expression으로 사용될 수도 있다. 이 경우, 조건식의 마지막 expression값이 리턴값이라고 생각하면 된다. 또한, 나열되는 조건이 모든 가능한 조건을 포함하는 경우, else는 생략될 수 있다.
enum class Bit {
ZERO, ONE
}
val numericValue = when (getRandomBit()) {
Bit.ZERO -> 0
Bit.ONE -> 1
// 'else' is not required because all cases are covered
}
여러 케이스를 합치는 경우엔 콤마(,)로 컨디션을 한 라인에 나열 할 수 있다.
when (x) {
0, 1 -> print("x == 0 or x == 1")
else -> print("otherwise")
}
조건부분에 임의의 표현식 사용도 가능하다.
when (x) {
s.toInt() -> print("s encodes x")
else -> print("s does not encode x")
}
in, !in 을 이용해 range 체크도 가능.
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("x is outside the range")
else -> print("none of the above")
}
is, !is를 이용해 타입 체크도 가능.
fun hasPrefix(x: Any) = when(x) {
is String -> x.startsWith("prefix")
else -> false
}
인자 없이 if-else 체인을 대신해서 사용도 가능하다.
when {
x.isOdd() -> print("x is odd")
y.isEven() -> print("y is even")
else -> print("x+y is odd")
}

for 문 : https://kotlinlang.org/docs/control-flow.html#for-loops
for 루프도 C나 자바처럼 초기치; 조건; 증가분 형태가 아니라 파이썬같은 방식으로 사용된다. 제공되는 iterator에 대해서 순회하는 방식이다. 이는 C#의 foreach문과 동일하다.
for (item in collection) print(item)
for (item: Int in ints) {
// ...
}
주어진 iterator를 이용한다는 얘기는 리턴값이 Iterator<>인 iterator() 도 멤버로 갖고 있거나 확장함수로 가져야 한다. 또한, next()와 hasNext()를 멤버로 갖거나 확장함수로 갖고 있어야 한다.
range 표현식을 사용하는 경우의 예는 다음과 같다.
for (i in 1..3) {
println(i)
}
for (i in 6 downTo 0 step 2) {
println(i)
}
array나 list의 인덱스로 순회하는 경우의 예는 다음과 같다.
for (i in array.indices) {
println(array[i])
}
이걸 withIndex를 사용하여 다음과 같이 사용하면 index와 value를 보다 가독성이 좋게 사용 가능하다.
for ((index, value) in array.withIndex()) {
println("the element at $index is $value")
}

extention function : https://kotlinlang.org/docs/extensions.html
기존 클래스에 새 기능을 넣으려면 상속(inheritance)을 받아 구현하는게 보통이다. 아니면 기존 클래스를 포함하는 새 클래스를 만들고 그 멤버로 기존 클래스를 생성하는 composition, 또는 새 클래스의 생성자의 인자로 기존 클래스를 넘겨서 멤버에 저장하는 Aggregation을 이용할 수도 있다. Decorator 패턴은 Aggregation을 이용하게 되는데, 코틀린에서는 Extension function을 이용해 이를 손쉽게 구현할 수 있다.
Extension function은 리시버 타입 클래스를 지정하고 함수를 정의한다.
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // 'this' corresponds to the list
this[index1] = this[index2]
this[index2] = tmp
}
val list = mutableListOf(1, 2, 3)
list.swap(0, 2) // 'this' inside 'swap()' will hold the value of 'list'
만약, 기존에 갖고있는 멤버함수와 동일하게 확장함수를 정의하면 항상 멤버함수가 불리게 된다. 이와 동일하게 property도 extension property 사용이 가능하다.
val <T> List<T>.lastIndex: Int
get() = size - 1

Class Constructors : https://kotlinlang.org/docs/classes.html#constructors
기본적으로 primary constructor는 클래스 선언에 ‘constructor’ 키워드와 함께 사용한다. annotation이나 visibility modifier 가 없다면, 키워드는 생략이 가능하다.
class Person constructor(firstName: String) { /*...*/ }
class Person(firstName: String) { /*...*/ }
이렇게 쓰면, 생성자 바디가 없는 상태인데, 클래스 안에서 init { } 블럭을 이용해 생성자 코드를 사용할 수 있다.
class InitOrderDemo(name: String) {
val firstProperty = "First property: $name".also(::println)
init {
println("First initializer block that prints $name")
}
val secondProperty = "Second property: ${name.length}".also(::println)
init {
println("Second initializer block that prints ${name.length}")
}
}
이 init { } 블럭은 인스턴스 생성시, 기술된 순서대로 실행된다. 또한, property 초기화도 생성자로 사용된다.
class Customer(name: String) {
val customerKey = name.uppercase()
}
좀 더 간편하게, primary 생성자에 property들을 직접 기술 할 수 있다. 마지막에 콤마가 있어도 상관없다.
class Person(val firstName: String, val lastName: String, var age: Int,)
primary constructor 외에 추가로 생성자가 필요하다면, 클래스 바디안에 constructor 키워드를 이용해 선언한다. primary constructor가 존재한다면, secondary constructor는 primary constructor에 delegate해야한다.
class Person(val name: String) {
val children: MutableList<Person> = mutableListOf()
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
this를 사용하면 되며, 위 코드에서 this(name)이 사용되었다.