= Kotlin = * 기본적으로 Java를 다뤄봤다는 전제하에 실용적인 측면으로만 기술. {{tag>Language JVM Object_Oriented_Programming Functional_Programming}} = 기본 = // 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) = persons.maxBy { it.age ?: 0} // main 함수를 작성하는데 class가 필요하지 않다. fun main(args: Array) { 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) { // 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) { 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) { // 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) { val l = arrayListOf() 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.filterByOdd() = this.filter { it % 2 != 0 } import myextension.filterByOdd fun main(args: Array) { (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) { val test: MouseEvent = RightButtonEvent() // 정적으로 지정한 MouseEvent의 onKeyUp이 호출된다. test.onKeyUp() } MouseEvent = Collections = 기본적으로 ...Of 형태로 생성 가능. == Map == fun main(args: Array) { // 중위 표현식. 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 == |<100%>| ^ 변경자 ^ 클래스 멤버 ^ 최상위 선언 ^ | **public (default)** | 모두 | 모두 접근 가능 | | internal | 같은 모듈 | 같은 모듈 | | protected | 하위 클래스 | - | | private | 같은 클래스 | 같은 파일 | Module : https://kotlinlang.org/docs/reference/visibility-modifiers.html#modules = 기타 유의 사항 = == Class == * 모든 Class와 Method는 final이 포함되어 있다. 상속 및 Override를 허용 하려면 "open"을 접두사로 붙인다. * Nested Class는 static이 포함되어 있다. Inner Class를 사용하려면 "inner" 변경자를 붙인다. Inner Class 사용시 바깥쪽 Class인 Outer를 참조하려면 "this@Outer"를 쓴다. == Enum == * 선언은 "enum class CLASSNAME". * 함수를 내장하는 경우 마지막 enum 값에 ";" 필요. (유일하게 Kotlin에서 ";"가 필요한 경우) == Functions == * 이름 있는 인자는 Java로 작성된 Code에서 사용할 수 없다. 즉, Android의 API에서 사용할 수 없다. * Java에서 Default Parameter 값이 적용된 함수를 이용하려면 "@JvmOverloads"를 붙인다. 이를 사용하면 Kotlin Compiler가 각 Method 자동 생성한다. == JVM Interop == * 다른 JVM 언어에서 Kotlin 파일의 함수를 호출하려면 접미사가 "Kt"인 class를 호출하면 된다. 예를 들어 XXX.kt의 함수를 호출하려면 XXXKt.method()