Table of Contents

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

https://kotlinlang.org/docs/reference/control-flow.html

if, while/do while은 동일하므로 생략.

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

기본적으로 …Of 형태로 생성 가능.

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

변경자 클래스 멤버 최상위 선언
public (default) 모두 모두 접근 가능
internal 같은 모듈 같은 모듈
protected 하위 클래스 -
private 같은 클래스 같은 파일

Module : https://kotlinlang.org/docs/reference/visibility-modifiers.html#modules

기타 유의 사항

Class

Enum

Functions

JVM Interop