Kotlin - Sizuha/devdog GitHub Wiki

Google์—์„œ Android ๊ฐœ๋ฐœ์–ธ์–ด๋กœ ๊ณต์‹ ์ง€์› ๊ฒฐ์ •.

Android Studio 3.0 ๋ถ€ํ„ฐ ์ง€์›.

๋ณ€์ˆ˜/์ƒ์ˆ˜ ์„ ์–ธ

// Java
String s = "abc";
final String t = "xyz";
// Kotlin
var s: String = "abc"
val t: String = "xyz"

var s = "abc"
val t = "xyz"

Basic Types

Numbers

Type Bit width
Double 64
Float 32
Long 64
Int 32
Short 16
Byte 8

Literal Constants

Decimals: 123
Long: 123L
Hexadecimals: 0x0F
Binaries: 0b00001011
Doubles: 123.5, 123.5e10
Floats: 123.5f
val oneMillion = 1_000_000
val creditCardNumber = 1234_5678_9012_3456L
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010

Characters

Char

Character literals go in single quotes: '1'.

Special characters can be escaped using a backslash.

The following escape sequences are supported: \t, \b, \n, \r, ', ", \ and $.

To encode any other character, use the Unicode escape sequence syntax: '\uFF00'.

Booleans

var b: Boolean = true
b = false

Arrays

// Creates an Array<String> with values ["0", "1", "4", "9", "16"]
val asc = Array(5, { i -> (i * i).toString() })

val x: IntArray = intArrayOf(1, 2, 3)
x[0] = x[1] + x[2]

String

val s: String = "Hello, world!\n"

A raw string is delimited by a triple quote ("""), contains no escaping and can contain newlines and any other characters:

val text = """
    |Tell me and I forget.
    |Teach me and I remember.
    |Involve me and I learn.
    |(Benjamin Franklin)
    """.trimMargin()

trimMargin

fun main(args: Array<String>) {
    println("---")
    println("""
    |for (i = 0; i < 100; ++i)
      |console.log(i)
    """.trimMargin())
    println("---")
    println("""
    > a
    > b
    > c
    > d
    """.trimMargin("> "))
    println("---")
}

๊ฒฐ๊ณผ

---
for (i = 0; i < 100; ++i)
console.log(i)
---
a
b
c
d
---

trimIndent

fun main(args: Array<String>) {
    println("---")
    println("""
    for (i = 0; i < 100; ++i)
      console.log(i)
    """.trimIndent())
    println("---")
}

๊ฒฐ๊ณผ

---
for (i = 0; i < 100; ++i)
  console.log(i)
---

String Templates

val i = 10
val s = "i = $i" // evaluates to "i = 10"

val s = "abc"
val str = "$s.length is ${s.length}" // evaluates to "abc.length is 3"

Templates are supported both inside raw strings and inside escaped strings. If you need to represent a literal $ character in a raw string (which doesn't support backslash escaping), you can use the following syntax:

val price = """
${'$'}9.99
"""

Packages

Imports

import foo.Bar // Bar is now accessible without qualification
import foo.* // everything in 'foo' becomes accessible
import bar.Bar as bBar // bBar stands for 'bar.Bar'

Control Flow

If

// Traditional usage 
var max = a 
if (a < b) max = b

// With else 
var max: Int
if (a > b) {
    max = a
} 
else {
    max = b
}
 
// As expression 
val max = if (a > b) a else b

val max = if (a > b) {
    print("Choose a")
    a
} else {
    print("Choose b")
    b
}

When

when replaces the switch operator of C-like languages. In the simplest form it looks like this

when (x) {
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> { // Note the block
        print("x is neither 1 nor 2")
    }
}

when (x) {
    0, 1 -> print("x == 0 or x == 1")
    else -> print("otherwise")
}

when (x) {
    parseInt(s) -> print("s encodes x")
    else -> print("s does not encode x")
}

when (x) {
    in 1..10 -> print("x is in the range")
    in validNumbers -> print("x is valid")
    !in 10..20 -> print("x is outside the range")
    else -> print("none of the above")
}

// As expression (1)
fun hasPrefix(x: Any) = when(x) {
    is String -> x.startsWith("prefix")
    else -> false
}

// As expression (2) 
val groupCode  = when(number) {
    0 -> 0
    in 1..10 -> 1
    in 11..100 -> 2
    else -> 3
}

when {
    x.isOdd() -> print("x is odd")
    x.isEven() -> print("x is even")
    else -> print("x is funny")
}

For Loops

for (item: Type in Collection) {
    // ...
}
for ((index, value) in array.withIndex()) {
    println("the element at $index is $value")
}

ไพ‹๏ผ‰ 1 ~ 100 ๊นŒ์ง€ ๋ฃจํ”„

// ๊ธฐ๋ณธ์ ์œผ๋กœ range ํ‘œํ˜„์„ ์ด์šฉํ•œ๋‹ค.

// 1.
for (i in 1..100) { ... }

// 2.
(1..100).forEach { i -> }

While Loops

while (x > 0) {
    x--
}

do {
    val y = retrieveData()
} while (y != null) // y is visible here!

Returns and Jumps

return, break, continue

Break and Continue Labels

loop@ for (i in 1..100) {
    for (j in 1..100) {
        if (...) break@loop
    }
}

Return at Labels

fun foo() {
    ints.forEach lit@ {
        if (it == 0) return@lit
        print(it)
    }
}

fun foo() {
    ints.forEach {
        if (it == 0) return@forEach
        print(it)
    }
}

Operator

Unary operations

Expression Translated to
+a a.unaryPlus()
-a a.unaryMinus()
!a a.not()
a++ a.inc()
a-- a.dec()

Binary operations

Expression Translated to
a + b a.plus(b)
a - b a.minus(b)
a * b a.times(b)
a / b a.div(b)
a % b a.rem(b)
a..b a.rangeTo(b)
a in b b.contains(a)
a !in b !b.contains(a)

Indexed access operator

Expression Translated to
a[i] a.get(i)
a[i, j] a.get(i, j)
a[i] = b a.set(i, b)
a[i, j] = b a.set(i, j, b)

Invoke operator

Expression Translated to
a() a.invoke()
a(i) a.invoke(i)

Augmented assignments

Expression Translated to
a += b a.plusAssign(b)
a -= b a.minusAssign(b)
a *= b a.timesAssign(b)
a /= b a.divAssign(b)

Equality and inequality operators

Expression Translated to
a == b a?.equals(b) ?: (b === null)
a != b !(a?.equals(b) ?: (b === null))

Comparison operators

Expression Translated to
a > b a.compareTo(b) > 0
a < b a.compareTo(b) < 0
a >= b a.compareTo(b) >= 0
a <= b a.compareTo(b) <= 0

Range

https://kotlinlang.org/docs/reference/ranges.html

1์—์„œ 100๊นŒ์ง€ (100 ํฌํ•จ)

for (i in 1..100) {
    print(i)
}

1์—์„œ 99๊นŒ์ง€(100 ๋ฏธํฌํ•จ)

for (i in 1 until 100) {
    print(i)
}

100์—์„œ 1๋กœ (1 ํฌํ•จ)

for (i in 100 downTo 1) {
    print(i)
}

0๋ถ€ํ„ฐ 100๊นŒ์ง€, ๋‹จ 2์”ฉ ์ฆ๊ฐ€

for (i in 0..100 step 2) {
    print(i)
}

100์—์„œ 0๊นŒ์ง€, ๋‹จ 2์”ฉ ๊ฐ์†Œ

for (i in 100 downTo 0 step 2) {
    print(i)
}

Class

class Some {

}

Constructors

class Person constructor(firstName: String) {
}
//--- or ---//
class Person(firstName: String) {
}
class AClass(some: String) {	
	lateinit var field: String

	init {
		field = some
	}
}
//--- or ---//
class AClass(some: String) {	
	val field: String = some
}
//--- or ---//
class AClass(val field: String) {	
}
class Person {
    constructor(parent: Person) {
        parent.children.add(this)
    }
}

class Person(val name: String) {
    constructor(name: String, parent: Person) : this(name) {
        parent.children.add(this)
    }
}

initializer blocks

class Constructors {
    init {
        println("Init block")
    }

    constructor(i: Int) {
        println("Constructor")
    }
}

init ๋ธ”๋ก์€ constructor ๋ธ”๋ก ์ •์˜ ๋ณด๋‹ค๋„ ๋จผ์ € ์ˆ˜ํ–‰๋จ.

Creating instances of classes

class Customer(val customerName: String = "") {}

val customer = Customer("Joe Smith")

Properties

class AClass {
    var name: String
        get() = this.toString()
        set(value) { name = value }
}

์ฝ๊ธฐ์ „์šฉ์œผ๋กœ ๋งŒ๋“ค๊ธฐ

class AClass {
    var name: String
        get() = this.toString()
        private set
}

Nested and Inner Classes

class Outer {
    private val bar: Int = 1
    class Nested {
        fun foo() = 2
    }
}

val demo = Outer.Nested().foo() // == 2
class Outer {
    private val bar: Int = 1
    inner class Inner {
        fun foo() = bar
    }
}

val demo = Outer().Inner().foo() // == 1

Inner Class๋Š” ์ž์‹ ์ด ์„ ์–ธ๋œ ํด๋ž˜์Šค ์˜์—ญ์˜ ๋งด๋ฒ„์— ๋ฐ”๋กœ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

Anonymous inner classes

window.addMouseListener(object: MouseAdapter() {
    override fun mouseClicked(e: MouseEvent) {
        // ...
    }
                                                                                                            
    override fun mouseEntered(e: MouseEvent) {
        // ...
    }
})

Inheritance

open class Base {
    open fun v() {}
    fun nv() {}
}
class Derived() : Base() {
    override fun v() {}
}

Kotlin์€ ๊ธฐ๋ณธ์ ์œผ๋กœ final class์ด๋‹ค. open class๋กœ ์„ ์–ธํ•ด ์ค˜์•ผ ์ƒ์† ๊ฐ€๋Šฅ. ๋งค์„œ๋“œ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€.

interface

Java์ฒ˜๋Ÿผ interface ํ‚ค์›Œ๋“œ๋กœ ์ •์˜.

// ์ •์˜
interface ISome {
	var fieldA: String // ํ”„๋กœํผํ‹ฐ
	val fieldB: String // ํ”„๋กœํผํ‹ฐ

	fun testA()
	fun testB(): Boolean { return true } // ๊ธฐ๋ณธ ๋™์ž‘์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค
}

// ์‚ฌ์šฉ
class SomeClass : ISome {
	override var fieldA
		get() = "TEST"
		private set

	override val fieldB = "TEST2"
	override fun testA { /* ... */ }
}

Generics

์ œ๋„ค๋ฆญ ํ•จ์ˆ˜

fun <T> someFunc() { ... }

// T์— ๋Œ€ํ•œ ํƒ€์ž… ์กฐ๊ฑด์„ ์ถ”๊ฐ€
fun <T: SomeType> someFunc() { ... }

// T์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๊ฐœ์˜ ํƒ€์ž… ์กฐ๊ฑด์„ ์ถ”๊ฐ€
fun <T> someFunc() 
where T: SomeTypeA, T: SomeTypeB
{ ... }

// ๋ฌผ๋ก  ์ œ๋„ค๋ฆญ ํƒ€์ž…์„ ์—ฌ๋Ÿฌ๊ฐœ ์ง€์ •ํ•  ์ˆ˜๋„ ์žˆ๋‹ค
fun <E, R> someFunc() { ... }

์ œ๋„ค๋ฆญ ํด๋ž˜์Šค

class ํ˜น์€ interface ๋“ฑ์— ์ œ๋„ค๋ฆญ์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

class SomeClass<T> {
// . . .
}

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ํƒ€์ž… ์ œํ•œ์„ ๊ฑธ ์ˆ˜๋„ ์žˆ๋‹ค.

class SomeClass<T: SomeType> {
// . . .
}

class SomeClass<T> 
where T: SomeTypeA, T: SomeTypeB {
// . . .
}

ๅฎฃ่จ€ๅดๅค‰ๆ€ง(Declaration-site variant)

์ œ๋„ค๋ฆญ ํƒ€์ž…์„ out ํ‚ค์›Œ๋“œ๋กœ ์„ ์–ธํ•˜๋ฉด ๋ฆฌํ„ด ํƒ€์ž…์œผ๋กœ๋งŒ ์‚ฌ์šฉ์ด ์ œํ•œ๋œ๋‹ค.

interface ISome<out T> {
	fun someFunc(): T
}

๋ฐ˜๋Œ€๋กœ in ํ‚ค์›Œ๋“œ๋กœ ์„ ์–ธ๋œ ํƒ€์ž…์€ ์ธ์ˆ˜๋กœ์„œ๋งŒ ์‚ฌ์šฉ๋œ๋‹ค.

interface ISome<in T> {
	fun someFunc(a: T)
}

Annotation ์ •์˜

@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY)
annotation class Column(
        val name: String,
        val notNull: Boolean = false,
        val unique: Boolean = false,
        val exclude: Boolean = false
)

annotation class ํ‚ค์›Œ๋“œ๋กœ ์–ด๋…ธํ…Œ์ด์…˜ ํด๋ž˜์Šค๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.
์–ด๋…ธํ…Œ์ด์…˜์—์„œ ์ทจ๊ธ‰ํ•˜๋Š” ์ธ์ˆ˜์˜ ํƒ€์ž…์€ ๊ธฐ๋ณธ ํƒ€์ž…๋งŒ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

@Target() ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ํ•ด๋‹น ์–ด๋…ธํ…Œ์ด์…˜์ด ์–ด๋””์— ๋ถ™์„ ์ˆ˜ ์žˆ๋Š”์ง€ ์ œํ•œ์„ ๋‘”๋‹ค.

Reflection

Android ํ”„๋กœ์ ํŠธ์—์„œ, build.gradle (Module) ์„ค์ •

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
}

ํด๋ž˜์Šค ๋งด๋ฒ„ ํƒ์ƒ‰

ํƒ€์ž…๋ช…::class.memberProperties๋Š” ๋งด๋ฒ„ ๋ณ€์ˆ˜, ํ”„๋กœํผํ‹ฐ๋ฅผ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

for (member in some::class.memberProperties) {
	// ์–ด๋…ธํ…Œ์ด์…˜ ํƒ์ƒ‰(1)
	member.annotations.forEach { a: Annotation -> 
		// ...
	}

	// ์–ด๋…ธํ…Œ์ด์…˜ ํƒ์ƒ‰(2)
	member.findAnnotation<AnnotationType>()?.let { /* ... */ }
}

ํƒ€์ž… ํ™•์ธ

ํด๋ž˜์Šค ๋งด๋ฒ„์˜ ๋ฆฌํ„ด ํƒ€์ž…์„ ํ™•์ธ.

for (member in some::class.memberProperties) {
	when (member.returnType) {
		Int::class.createType() -> { /* ... */ }
		else -> { /* ... */ }
	}
}

**ํƒ€์ž…๋ช…::class.createType()**์œผ๋กœ ๋น„๊ตํ•˜๋Š” ๊ฒƒ์— ์ฃผ์˜.

๋งด๋ฒ„์— ์ฝ๊ณ  ์“ฐ๊ธฐ

// member: KMutableProperty<*>

// ์ฝ๊ธฐ
val r = member.getter.call(...)

// ์“ฐ๊ธฐ
member.setter.call(...)

private ๋งด๋ฒ„์— ๋Œ€ํ•œ ์ ‘๊ทผ

val accessible = member.isAccessible
member.isAccessible = true
member.setter.call(tableObj, value)
member.isAccessible = accessible

apply, also, run, let

๋‹ค์Œ๊ณผ ๊ฐ™์ด class๋ฅผ ์ •์˜ํ–ˆ๋‹ค๊ณ  ํ•  ๋•Œ,

class NameCard {
    var name = ""
    var tel = ""
    var email = ""
}

apply์˜ ๊ฒฝ์šฐ

val newCard: NameCard = NameCard().apply { // this = NameCard()
    name = "xxxxxx"
    tel = "000-0000-0000"
    email = "[email protected]"
    // ์ž๋™์œผ๋กœ this๋ฅผ ๋„˜๊ฒจ์คŒ
}

also์˜ ๊ฒฝ์šฐ

val newCard: NameCard = NameCard().also { // it = NameCard()
    it.name = "xxxxxx"
    it.tel = "000-0000-0000"
    it.email = "[email protected]"
    // ์ž๋™์œผ๋กœ it์„ ๋„˜๊ฒจ์คŒ
}

run์˜ ๊ฒฝ์šฐ

val newCard: NameCard()
newCard.run { // this = newCard
    name = "xxxxxx"
    tel = "000-0000-0000"
    email = "[email protected]"
}

let์˜ ๊ฒฝ์šฐ

val newCard: NameCard()
newCard.let { // it = newCard
    it.name = "xxxxxx"
    it.tel = "000-0000-0000"
    it.email = "[email protected]"
}
โš ๏ธ **GitHub.com Fallback** โš ๏ธ