Kotlin에서 propery 정의

field를 통해 내부 변수에 접근해야 한다는 점에 유의해야 함.

var state: State? = null
set(value) {
    field?.leave()
    field = value
    field?.enter()
}
get() = field

[Kotlin] Inner Class

Inner Class는 어떤 클래스(A라고 하자)의 내부에 정의되는데.. 이 Inner Class는 바로 객체화 될수 없고, 먼저 A 클래스를 객체화한 뒤 객체화된 것을 통해 생성된다.

아래는 코틀린의 문법 중 Inner Class에 대한 예제이다.

open class Base {
    open val c: String = "Dip2K"
    open fun f() = println("Programmer, ${c}")
}

class Derived: Base() {
    override  val c: String = "Super ${super.c}"
    override fun f() = println("Developer, ${c}")

    inner class InnerClass {
        constructor() {
            println("InnerClass's constructor")
        }

        fun f() = println("InnerClass's fun: f")
        fun t() {
            f()
            Derived().f()
            super@Derived.f()
        }
    }
}

fun main() {
    val c = Derived()
    val i = c.InnerClass()

    i.t()
}

Inner Class를 기준으로 바깓 클래스 및 그 바깓 클래스의 부모 클래스에 대한 프로퍼티와 함수에 대한 접근에 대한 문법을 나타내고 있다. 실행결과는 다음과 같다.

InnerClass's constructor
InnerClass's fun: f
Developer, Super Dip2K
Programmer, Super Dip2K

Kotlin 고유의 표준함수 (let, also, apply, with, run, …)

다시 고등학생이 된 기분입니다. let, also, with 등 매우 기초적인 영어 단어를 외우는 기분인데요. 원래는 표준함수로 불리지만 Kotlin에서 고유하게 제공하므로 고유의 표준함수라는 제목을 붙였습니다. 이 함수들을 사용하여 코드를 좀더 최적화 시켜줄 수 있으며 불필요한 코딩량을 줄일 수 있습니다.

let

예제 코드는 다음과 같습니다.

data class WHAT(val name: String, val age: Int)

fun main() {
    println("Case 1")

    var w: WHAT? = null

    val v = w?.let {
        println(it)
    } ?: "Known"

    println(v)

    println("Case 2")

    var W: WHAT? = WHAT("Dip2K", 43)

    val V = W?.let {
        println(it)
    } ?: "Known"

    println(V)
}

결과는 다음과 같다.

Case 1
Known
Case 2
WHAT(name=Dip2K, age=43)
kotlin.Unit

let은 받은 객체(w)를 람다 함수 내부에서 it으로 받아 사용할 수 있습니다. 반환값은 람다 함수의 가장 마지막 코드의 반환값이 됩니다.

also

예제 코드는 다음과 같습니다.

data class WHAT(val name: String, val age: Int)

fun main() {
    println("Case 1")

    var w: WHAT? = null

    val v = w?.also {
        println(it)
    } ?: "Known"

    println(v)

    println("Case 2")

    var W: WHAT? = WHAT("Dip2K", 43)

    val V = W?.also {
        println(it)
    } ?: "Known"

    println(V)
}

결과는 다음과 같다.

Case 1
Known
Case 2
WHAT(name=Dip2K, age=43)
WHAT(name=Dip2K, age=43)

also 함수는 받은 객체를 it으로 받아 사용할 수 있으며, 받은 객체를 그대로 그대로 반환합니다.

apply

예제 코드는 다음과 같습니다.

data class WHAT(var name: String, var age: Int)

fun main() {
    val w = WHAT("Jackass", 16)

    val r = w.apply {
        name = "Dip2K"
        age = 43
    }

    println(w)
    println(r)
}

결과는 다음과 같다.

WHAT(name=Dip2K, age=43)
WHAT(name=Dip2K, age=43)

apply는 받은 객체를 람다 함수 내부에서 it이 아닌 this로 처리합니다. 받은 객체를 그대로 반환합니다.

with

예제 코드는 다음과 같습니다.

package with

data class WHAT(var name: String, var age: Int)

fun main() {
    val w = WHAT("Jackass", 16)

    val r = w?.let {
        with(it) {
            name = "Dip2K"
            age = 43
            "Good day!"
        }
    }

    println(w)
    println(r)
}

결과는 다음과 같다.

WHAT(name=Dip2K, age=43)
Good day!

with 함수는 인자로 받은 객체를 람다 함수 안에서 this로 사용할 수 있습니다. 반환값은 람다 함수를 구성하는 가장 마지막 코드입니다.

run

예제 코드는 다음과 같습니다.

data class WHAT(var name: String, var age: Int)

fun main() {
    val v = run {
        val w = WHAT("Jackass", 16)
        w
    }

    println(v)

    val vv = v.run {
        name = "Dip2K"
        age = 43

        "Good day !"
    }

    println(v)
    println(vv)
}

결과는 다음과 같다.

WHAT(name=Jackass, age=16)
WHAT(name=Dip2K, age=43)
Good day !

run은 객체를 전달받지 않고 독립적으로 실행하는 경우와 개체를 전달받아 처리하는 경우로 구분됩니다. 객체를 전달받아 처리하는 경우 전달받은 객체를 람다 함수 내부에서 this로 받아 사용할 수 있습니다. 반환값은 람다 함수의 마지막 코드입니다.

use

예제 코드는 다음과 같습니다.

import java.io.File

fun main() {
    val f = File("d:/a.txt")
    f.bufferedWriter().use {
        it.appendln("Hello, GIS Developer.")
        it.append("Good Day !")
    }
}

use 함수는 전달 받은 객체를 it으로 람다 함수 내부에서 사용하며, 람다 함수 종료시 close 함수를 자동으로 호출해 줍니다.

takeIf, takeUnless

예제 코드는 다음과 같습니다.

data class WHAT(var name: String, var age: Int)

fun main() {
    val w = WHAT("Jackass", 16)

    val v = w?.takeIf { it.age < 20 }
    //val v = w?.takeUnless { it.age >= 20 } <- 위의 코드와 동일함
    println(v)

    var r = v?.apply {
        name = "Dip2K"
    }
    println(r)
}

결과는 다음과 같다.

WHAT(name=Jackass, age=16)
WHAT(name=Dip2K, age=16)

takeIf 함수는 받은 객체를 람다 함수 내부에서 it으로 처리하며, 반환값이 true일때 받은 객체를 반환하고, false일때 null을 반환합니다.

measureTimeMillis

예제 코드는 다음과 같습니다.

import kotlin.system.measureTimeMillis

fun main() {
    val time = measureTimeMillis {
        Thread.sleep(10000)
    }

    println(time)
}

결과는 다음과 같다.

10000

measureTimeMillis은 어떤 코드를 실행하는데 소요되는 시간을 측정하는데 사용됩니다.

코틀린의 observable, vetoable 위임자

코틀린은 2011년 중순에 공개되어 지속적으로 발전되어 오다가 2019년에 구글 안드로이드 개발 주요 개발언어로 채택되면서 현대적인 프로그래밍 언어중 하나입니다. 이 글은 코틀린의 데이터 변수에 obserable과 vetoable 위임자를 지정하여 변수의 값이 변경될 경우 원하는 로직을 실행하거나 변수의 값의 변경시 특정 조건과 맞지 않으면 변경을 취소하는 내용을 대해 설명합니다.

먼저 obserable 위임자를 통한 변수값 변경시 처리입니다.

import kotlin.properties.Delegates

class User {
    var name: String by Delegates.observable("초기값") {
        prop, old, new -> println("$old 값이 $new 값으로 변경됩니다.")
    }
}

fun main() {
    val user = User()
    user.name = "홍길동"
    user.name = "임꺽정"
}

실행 결과는 다음과 같습니다.

초기값 값이 홍길동 값으로 변경됩니다.
홍길동 값이 임꺽정 값으로 변경됩니다.

다음은 vetoable 위임자입니다. 값의 변경시 특정한 조건에 따라 변경을 취소할 수 있습니다.

import kotlin.properties.Delegates

class MoreBiggerInt(initValue: Int) {
    var value: Int by Delegates.vetoable(initValue) {
        property, oldValue, newValue -> {
        val result = newValue > oldValue
        if(result) {
            println("더 큰 값이므로 값을 변경합니다.")
        } else {
            println("작은 값이므로 변경을 취소합니다.")
        }
        result
    }()
    }
}

fun main() {
    val vv = MoreBiggerInt(10)

    vv.value = 20
    println("${vv.value}")

    vv.value = 5
    println("${vv.value}")
}

실행 결과는 다음과 같습니다.

더 큰 값이므로 값을 변경합니다.
20
작은 값이므로 변경을 취소합니다.
20