Kotlin
기본
// data를 붙이면 toString(), equals(), hashCode(), copy() Method 생성
// 기본적으로 모든 변수는 null을 허용하지 않기 때문에, C#처럼 "Nullable" type이 존재.
// C#처럼 default value를 지정할 수 있음.
// val은 Scala처럼 Immutable, var는 Mutable.
// Custom Accessor는 C#처럼 작성. 아래는 getter의 예시.
data class Person(val name: String, val age: Int? = null, val job: String = "Salesman") {
val isDeveloper: Boolean
get() = job == "Developer"
}
// 함수 내용이 한 줄인 경우 "="를 이용하여 식(Expression) 표현이 가능하다.
// it은 Scala나 Clojure의 "_"와 같은 의미. 즉, 함수 안에서 자기 자신을 가리키며 "x -> x.age"에서 x를 it으로 축약 표현.
//"?:"은 Elvis Operator라고 하며, 뒤의 값은 null인 경우 0으로 처리하겠다는 의미.
fun getOldest(persons: List<Person>) = persons.maxBy { it.age ?: 0}
// main 함수를 작성하는데 class가 필요하지 않다.
fun main(args: Array<String>) {
val person1 = Person("john", 11)
// Nullable로 선언했기에 age는 생략 가능
val person2 = Person("doe")
// 이름을 붙인 argument
// 단, Java로 작성된 Code는 사용할 수 없음. 즉, Android에서 API 호출시 사용할 수 없음.
val person3 = Person(age = 22, name = "Jake")
//list 생성. 모든 Collection은 ...Of로 생성 가능.
val persons = listOf(person1, person2, person3)
val oldest = getOldest(persons)
// 문자열 Template 지원. 식(Expression)이 아닌 경우 {} 생략 가능.
println("oldest : ${oldest}")
println(persons.filter { it.isDeveloper })
}
oldest : Person(name=Jake, age=22)
[Person(name=Jake, age=22, job=Developer)]
High-Order Functions 시험
https://kotlinlang.org/docs/reference/lambdas.html
아래 예제처럼 Kotlin은 함수를 first-class로 취급하므로 Functional Programming의 기본 특성을 만족한다. (함수를 인자로 받고, 함수를 반환할 수 있는 특성)
// Type만을 지정하기 위한 빈 Interface 생성. 상속은 ":" 사용.
interface Event
data class KeyEvent(val value: String): Event
fun onKeyDown(f: (Event) -> Unit) = f(KeyEvent("keydown event"))
fun main(args: Array<String>) {
// onKeyDown { event -> println(event) } 와 동치.
onKeyDown { println(it) }
}
KeyEvent(value=Hello, World)
Control Flow
when
interface Event
data class KeyEvent(val value: String): Event
data class MouseEvent(val value: String): Event
fun main(args: Array<String>) {
val eventControlFlow = {
event: Event ->
when (event) {
is KeyEvent -> println("Key event! ${event.value}")
is MouseEvent -> println("Mouse event! ${event.value}")
else -> println("Unknown")
}
}
// 인자가 없는 형태로도 사용 가능. 이 경우 Matching 대상이 계속 Instance를 생성하는 경우 사용.
/* val eventControlFlow = {
event: Event ->
when {
event is KeyEvent -> println("Key event! ${event.value}")
event is MouseEvent -> println("Mouse event! ${event.value}")
else -> println("Unknown")
}
} */
eventControlFlow(KeyEvent("foo"))
eventControlFlow(MouseEvent("bar"))
}
Key event! foo
Mouse event! bar
for
foreach만 존재.
fun main(args: Array<String>) {
// in은 infix가 붙은 함수로 중위표현식 가능.
// in은 Collections 혹은 Range의 원소 검사.
val test = "Banana" in "Apple".."Cherry"
println(test)
for (i in 1..10) {
print("$i ")
}
println()
for (i in 10 downTo 1) {
print("$i ")
}
println()
for (i in 10 downTo 1 step 2) {
print("$i ")
}
println()
val size = 10
for (i in 0 until size) {
print("$i ")
}
}
true
1 2 3 4 5 6 7 8 9 10
10 9 8 7 6 5 4 3 2 1
10 8 6 4 2
0 1 2 3 4 5 6 7 8 9
Destructuring
data class Person(val name: String, val age: Int? = null)
fun main(args: Array<String>) {
val l = arrayListOf<Person>()
l.add(Person("Jake", 11))
l.add(Person("Luke", 23))
l.add(Person("Nina", 21))
// Destructuring
for((age, name) in l) {
println("$name $age")
}
println()
for((index, element) in l.withIndex()) {
// Kotlin은 element에 대해서 Nested Destructuring 을 지원하지 않기 때문에 변수를 이용하여 Destructuring.
val (name, age) = element
println("$index $name $age")
}
}
11 Jake
23 Luke
21 Nina
0 Jake 11
1 Luke 23
2 Nina 21
Functions
Extension Functions
이미 존재하는 Class에 함수 확장. Static Method 호출에 대한 Syntatic sugar. 이를 이용하더라도 private/protected가 선언된 Member를 호출할 수 없으므로 캡슐화가 깨지는 것은 아님.
// 다른 파일에서 사용하려면 package 선언. 임의 이름으로 선언 가능.
package myextension
// 기존에 존재하는 List에 filterByOdd() 함수 확장.
fun List<Int>.filterByOdd() = this.filter { it % 2 != 0 }
import myextension.filterByOdd
fun main(args: Array<String>) {
(1..10).toList().filterByOdd().forEach { print("$it ") }
}
1 3 5 7 9
그리고 Override할 수 없고, 정적으로 결정된다.
open class MouseEvent
class RightButtonEvent: MouseEvent()
fun MouseEvent.onKeyUp() = println("MouseEvent")
fun RightButtonEvent.onKeyUp() = println("RightButtonEvent")
fun main(args: Array<String>) {
val test: MouseEvent = RightButtonEvent()
// 정적으로 지정한 MouseEvent의 onKeyUp이 호출된다.
test.onKeyUp()
}
MouseEvent
Collections
Map
fun main(args: Array<String>) {
// 중위 표현식. to라는 Method는 접미사로 infix가 붙어 가능한 표현.
val map = hashMapOf(1 to "one", 2 to "two")
map[3] = "three"
// Destructuring
for ((key, value) in map) {
println("$key $value")
}
// Java에서 제공하지 않는 함수들은 모두 확장 함수(Extension Functions)
println(map.keys.max())
}
1 one
2 two
3 three
Class
Visibility Modifier
기타 유의 사항
Class
Enum
Functions
이름 있는 인자는 Java로 작성된 Code에서 사용할 수 없다. 즉, Android의 API에서 사용할 수 없다.
Java에서 Default Parameter 값이 적용된 함수를 이용하려면 “@JvmOverloads”를 붙인다. 이를 사용하면 Kotlin Compiler가 각 Method 자동 생성한다.
JVM Interop