Kotlin Anko gradle 세팅

|

build.gradle(프로젝트)

buildscript {
    ext.kotlin_version = '1.3.21'
    ext.anko_version='0.10.8'
    }
}


build.gradle(모듈)

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation "org.jetbrains.anko:anko:$anko_version"
}

Kotlin 인터페이스

|

interface

코틀린에서는 Java에서와 달리 interface 내부의 함수가 내용을 가질 수도 있고, abstract 멤버 변수를 가질 수도 있습니다.

또한 Java에서는 implements 키워드를 이용해서 인터페이스를 구현했는데, 코틀린에서는 상속과 마찬가지로 콜론(:)을 사용해서 인터페이스를 구현할 수 있습니다.

interface OnEventListener {
    open fun onEvent(type: Int)
}

open class EventHandler(var name: String) : OnEventListener {
    override fun onEvent(type: Int) {
        TODO("not implemented")
    }
}


다중 인터페이스 구현할 경우

아래 예제는 하나의 클래스가 여러 개의 인터페이스를 구현한 예제입니다. 인터페이스 내의 함수들이 구현체가 있고, 중복된 함수가 있을 경우 구현한 클래스에서는 필요한 클래스의 super를 호출할 수 있습니다. 둘 다 호출해도 상관없고, 필요한 super만 호출해도 상관없습니다.

interface onEventListener {
    fun onEvent() {}
    fun onMessageArrived() {}
}

interface onClientEventListener {
    fun onEvent() {}
    fun onConnected() {}
    fun onDisconnected() {}
}

class TcpServer : onEventListener, onClientEventListener {
    override fun onEvent() {
        super<onEventListener>.onEvent()
        super<onClientEventListener>.onEvent()
    }
}

Kotlin 상속

|

부모 클래스 구현하기

코틀린에서 상속 받을 때는 : 기호를 이용해서 상속 받습니다. 그리고 부모 클래스는 open 이나 abstract 키워드를 사용해야만 상속할 수 있습니다.

그리고 기본적으로 모든 클래스는 Java에서의 Object 처럼 Any라는 클래스를 상속받고 있습니다.

open class Shape(vertex: Int) {
    open fun onDraw(canvas: Canvas) {
    }
}

class Triangle() : Shape(3) {
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        TODO("Draw Lines")
    }
}

class Rectangle() : Shape(4) {
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        TODO("Draw Lines")
    }
}

open 키워드를 사용하지 않은 경우는 코틀린에서 기본적으로 final로 선언됩니다. 즉 오버라이드를 하고 싶으면 반드시 open을 붙여야 합니다.


상속 받는 방법

부모 클래스의 생성자가 여러 개인 경우 상속받는 자식 클래스는 다음과 같이 작성할 수 있습니다.

class CustomDialog(ctx: Context) : AlertDialog(ctx) {
}

또는

class CustomDialog : AlertDialog {
    constructor(ctx: Context) : super(ctx)
}

와 같이 작성할 수 있습니다. 이 경우 두 번째 방법이 더 좋습니다.

첫 번째 방법의 경우 부모 클래스의 디폴트 생성자가 정해져 있어서 다른 생성자를 만들기 어렵습니다.

만약 다음과 같이 코드를 작성하면 Primary constructor call expected 오류가 발생합니다.

class CustomDialog(ctx: Context) : AlertDialog(ctx) {
    constructor(ctx: Context, themeId: Int) : super(ctx, themeId)
}

두 번째 방법으로 사용하면 다음과 같이 해결할 수 있습니다.

class CustomDialog : AlertDialog {
    constructor(ctx: Context) : super(ctx, android.R.style.Theme_NoTitleBar)

    constructor(ctx: Context, themeId: Int) : super(ctx, themeId)
}

Kotlin Realim 사용하는 방법

|

build.gradle(프로젝트)

buildscript {
    ext.kotlin_version = '1.3.21'
    ext.anko_version = '0.10.8'
    repositories {
        google()
        jcenter()

    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.3.2'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "io.realm:realm-gradle-plugin:5.10.0"
    }
}


build.gradle(모듈)

apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

apply plugin: 'kotlin-kapt'

apply plugin: 'realm-android'


Realm 객체 생성하기

import io.realm.RealmObject
import io.realm.annotations.PrimaryKey

open class Todo(@PrimaryKey var id: Long = 0,
                var title: String = "",
                var date: Long = 0) : RealmObject() {

}


SnowApplication.kt

import android.app.Application
import io.realm.Realm

class SnowApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        Realm.init(this)
    }
}

그리고 AndroidManifest.xml에 위 Application을 등록합니다.


MainActivity.kt

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import io.realm.Realm
import io.realm.Sort
import io.realm.kotlin.createObject
import io.realm.kotlin.where
import kotlinx.android.synthetic.main.activity_main.*
import org.jetbrains.anko.alert
import org.jetbrains.anko.yesButton
import java.util.*

class MainActivity : AppCompatActivity() {

    private val realm = Realm.getDefaultInstance()
    private val calendar = Calendar.getInstance()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        insert_button.setOnClickListener { insertItem() }
        select_button.setOnClickListener { selectItemList() }
        update_button.setOnClickListener { updateItem(1) }
        delete_button.setOnClickListener { deleteItem(1) }
    }

    override fun onDestroy() {
        super.onDestroy()
        realm.close()
    }

    private fun nextId(): Long {
        val maxId = realm.where<Todo>().max("id")
        if (maxId != null) {
            return maxId.toLong() + 1
        }
        return 0
    }

    private fun insertItem() {
        realm.beginTransaction()

        val newItem = realm.createObject<Todo>(nextId())
        newItem.title = "Title"
        newItem.date = calendar.timeInMillis

        realm.commitTransaction()

        alert("An item is added.") {
            yesButton { }
        }.show()
    }

    private fun selectItemList() {
        val realmResult = realm.where<Todo>().findAll().sort("date", Sort.DESCENDING)

        for (Todo in realmResult) {
            Log.i("snowdeer", "[snowdeer] todo: $Todo")
        }
    }

    private fun updateItem(id: Long) {
        realm.beginTransaction()

        val newItem = realm.where<Todo>().equalTo("id", id).findFirst()!!
        newItem.title = "Title is changed !!!"
        newItem.date = calendar.timeInMillis

        realm.commitTransaction()

        alert("An item($id) is changed.") {
            yesButton { }
        }.show()
    }

    private fun deleteItem(id: Long) {
        realm.beginTransaction()

        val item = realm.where<Todo>().equalTo("id", id).findFirst()!!
        item.deleteFromRealm()

        realm.commitTransaction()

        alert("An item($id) is deleted.") {
            yesButton { }
        }.show()
    }
}

Kotlin 멤버 변수 get/set

|

코틀린에서는 변수 선언만 하면 컴파일러가 자동으로 get/set 함수를 생성해줍니다.

class Person() {
    var name: String = ""
    var age: Int = 0
}

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        var p = Person()
        p.name = "snowdeer"
        p.age = 30
    }
}


get/set 함수 오버라이딩

class Person() {
    var name: String = ""
        set(name) {
            field = "[-- $name --]"
        }
        get() = "Hello, " + field


    var age: Int = 0
}

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        var p = Person()
        p.name = "snowdeer"
        p.age = 30

        Log.i("snowdeer", "snowdeer] " + p.name)
    }
}

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

Hello, [-- snowdeer --]

만약 외부에서 set 함수에 접근하지 못하게 하려면 private 키워드를 사용하면 됩니다.

class Person() {
    var name: String = ""
        private set(name) {
            field = "[-- $name --]"
        }
        get() = "Hello, " + field
}