10
10

data class

๋ฐ์ดํ„ฐ ํด๋ž˜์Šค๋Š” ํ•„์ˆ˜ ๋ฉ”์„œ๋“œ๋ฅผ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋งŒ๋“ค์–ด์ค€๋‹ค. ์ฝ”ํ‹€๋ฆฐ ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ์ด๋ฏธ ํ”„๋กœํผํ‹ฐ ์ ‘๊ทผ์ž, ์ƒ์„ฑ์ž๊ฐ™์€๊ฑธ ์ž๋ฐ”์™€๋Š” ๋‹ค๋ฅด๊ฒŒ ์•Œ์•„์„œ ๋งŒ๋“ค์–ด ์ฃผ๋Š”๊ฑธ ์ด์ „ ๋‚ด์šฉ์—์„œ ๋ณผ ์ˆ˜ ์žˆ์—ˆ๋Š”๋ฐ, ๊ฑฐ๊ธฐ์— ํ•œ๋ฐœ ๋” ๋‚˜๊ฐ€์„œ toString(), equals(), hashCode()์™€ ๊ฐ™์€ ๋ฉ”์„œ๋“œ๋ฅผ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋งŒ๋“ค์–ด์ค€๋‹ค.

์ฝ”ํ‹€๋ฆฐ์˜ == ๋Š” equals()๋กœ ๋‚ด๋ถ€์ ์œผ๋กœ ํ˜ธ์ถœ๋˜์–ด ๊ฐ์ฒด๋ฅผ ๋น„๊ตํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ์ž๋ฐ”์—์„œ ๋ฌธ์ œ๊ฐ€ ๋˜๋˜ ์ฐธ์กฐ๋น„๊ต๊ฐ€ ์ฝ”ํ‹€๋ฆฐ์˜ ==์—์„œ๋Š” ๋ฐœ์ƒํ•˜์ง€์•Š๋Š”๋‹ค. ์ฐธ์กฐ ๋น„๊ต๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋ฉด ===๋กœ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

val client1 = Client("Alice", 342562)
val client2 = Client("Alice", 342562)
println(client1 == client2) println(client1 === client2)
 

๋‘˜๋‹ค false์ด๋‹ค. ๊ฐ์ฒด๋ฅผ ๋น„๊ตํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ธ๋ฐ, ๊ฐ’์ด ๊ฐ™์€ ๊ฑธ ๋น„๊ตํ•˜๋ ค๋ฉด equals๋ฉ”์„œ๋“œ๋ฅผ Clientํด๋ž˜์Šค ๋‚ด๋ถ€์—์„œ ์˜ค๋ฒ„๋ผ์ด๋“œ ํ•˜์—ฌ ๊ตฌํ˜„ํ•ด์•ผ๋œ๋‹ค.

hashCode()๋Š” ์ž˜ ๋ชจ๋ฅด๋˜ ๊ฐœ๋…์ธ๋ฐ, equals()๊ฐ€ true๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋‘ ๋น„๊ต๊ฐ์ฒด๋Š” ๊ฐ™์€ hashCode๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผํ•œ๋‹ค๋Š” ํŠน์ง•์ด ์žˆ๋‹ค. ํ•ด์‹œ ์ปจํ…Œ์ด๋„ˆ๋ผ๊ณ ๋„ ํ•˜๋Š”๋ฐ, ๊ตฌ๋ณ„ID๋ผ๊ณ  ๋ณผ ์ˆ˜๋„ ์žˆ๋‹ค.

์›๋ž˜๋ผ๋ฉด ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” ์—ญํ• ๋งŒ ํ•˜๋Š” ํด๋ž˜์Šค์— ์ด๋Ÿฐ ๋ฉ”์„œ๋“œ๋ฅผ ์ƒํ™ฉ์— ๋งž๊ฒŒ ์˜ค๋ฒ„๋ผ์ด๋“œ ํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ๋˜์ง€๋งŒ, data class๋กœ ์ƒ์„ฑํ•œ ํด๋ž˜์Šค๋Š” ์ด ๋ฉ”์„œ๋“œ๋“ค์„ ์ž๋™ ์ƒ์„ฑํ•œ๋‹ค.

data class Client(val name: String, val postalCode: Int)
 

๋ฐ์ดํ„ฐ ํด๋ž˜์Šค์˜ ์ฃผ์ƒ์„ฑ์ž๋กœ ์„ ์–ธ๋œ ํ”„๋กœํผํ‹ฐ๋ฅผ ๊ณ ๋ คํ•ด equals, hashCode๊ฐ€ ๋งŒ๋“ค์–ด์ง„๋‹ค.

๋ถˆ๋ณ€์„ฑ

๋ฐ์ดํ„ฐ ํด๋ž˜์Šค์˜ ํ”„๋กœํผํ‹ฐ๋Š” var๋กœ๋„ ์„ ์–ธํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, val๋กœ ๊ถŒ์žฅ๋œ๋‹ค. ๋ถˆ๋ณ€์„ฑ์ด ํ•„์ˆ˜์ ์ธ ํƒ€์ž… ์ปจํ…Œ์ด๋„ˆ์— ๋ฐ์ดํ„ฐ ํด๋ž˜์Šค๋ฅผ ๋‹ด๋Š” ๊ฒฝ์šฐ, var๋กœ ์„ ์–ธํ•œ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋ฉด ์ปจํ…Œ์ด๋„ˆ ์ƒํƒœ๊ฐ€ ๋ณ€ํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋ถˆ๋ณ€ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋‹ค์ค‘์Šค๋ ˆ๋“œ์ผ ๋•Œ ๋” ์•ˆ์ „ํ•˜๋‹ค. ์“ฐ๊ธฐ๋กœ ์ ‘๊ทผํ•  ์ˆ˜๊ฐ€ ์—†์œผ๋‹ˆ ๋™๊ธฐํ™”ํ•  ํ•„์š”๊ฐ€ ์ค„์–ด๋“ค๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ๋Ÿผ var๋กœ ์„ ์–ธํ•ด์•ผํ•˜๋งŒ ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ ํ•ด๊ฒฐํ•ด์•ผ๋ ๊นŒ?

copy()๊ฐ€ ์žˆ๋‹ค. val ํ”„๋กœํผํ‹ฐ๋ฅผ copyํ•˜์—ฌ ๋ณต์‚ฌ๋ณธ์˜ ๊ฐ’์„ ์žฌ์ •์˜ ํ•œ๋‹ค. ์ด๋Ÿฌ๋ฉด ์›๋ณธ์€ ์‚ด์•„์žˆ๊ณ , ๋ณต์‚ฌ๋ณธ์„ ์ œ๊ฑฐํ•ด๋„ ์›๋ณธ์— ์˜ํ–ฅ์ด ์—†๋‹ค.

val bob = Client("Bob", 973293)
println(bob.copy(postalCode = 382555))
// Client๋Š” data class ๊ฐ์ฒด.
 

์œ„์ž„ delegation

๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด์ด ์˜ˆ์‹œ๋กœ ๋‚˜์™€์žˆ๋‹ค. ํ—ค๋“œํผ์ŠคํŠธ GoF ๋””์ž์ธํŒจํ„ด ์ฑ…์„ ์ฝ์œผ๋ฉด์„œ ๋‚˜์™”๋˜ ๊ฑด๋ฐ, ํ•„์š”ํ•œ ๊ธฐ๋Šฅ์ด ์žˆ์œผ๋ฉด ์ƒ์†๋ฐ›์€ ๊ฒƒ ์œ„์— ๋ง์”Œ์›Œ์„œ ์ƒˆ๋กœ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ์‹์ด๋‹ค. ์ฝ”ํ‹€๋ฆฐ์—์„œ๋Š” ์ด๊ฑธ by ํ‚ค์›Œ๋“œ๋กœ ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

class CountingSet<T>(
        val innerSet: MutableCollection<T> = HashSet<T>()
) : MutableCollection<T> by innerSet {

    var objectsAdded = 0

    override fun add(element: T): Boolean {
        objectsAdded++
        return innerSet.add(element)
    }

    override fun addAll(c: Collection<T>): Boolean {
        objectsAdded += c.size
        return innerSet.addAll(c)
    }
}
 

innerSet์„ ์œ„์ž„๋ฐ›์•„์„œ ํ•„์š”ํ•œ add, addAll ๋ฉ”์„œ๋“œ๋งŒ ์žฌ์ •์˜ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ์Šต์ด๋‹ค.

object

object๋Š” ํด๋ž˜์Šค ์ •์˜์™€ ๋™์‹œ์— ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๋Š” ํ‚ค์›Œ๋“œ๋‹ค. ํ‚ค์›Œ๋“œํ•˜๋‚˜๋กœ ์‹ฑ๊ธ€ํ„ด์„ ๋งŒ๋“ค์ˆ˜ ์žˆ๊ฒŒ๋๋‹ค.

object CaseInsensitiveFileComparator : Comparator<File> {
    override fun compare(file1: File, file2: File): Int {
        return file1.path.compareTo(file2.path,
                ignoreCase = true)
    }
}
 

์ƒ์„ฑ์ž๋ฅผ ๊ฐ์ฒด์„ ์–ธ์— ์“ธ์ˆ˜์—†๋‹ค. ์ •ํ™•ํžˆ๋Š” ์„ ์–ธ๊ณผ ๋™์‹œ์— ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒ์„ฑ๋˜๋‹ˆ ์“ธ ํ•„์š”๊ฐ€ ์—†๋‹ค. ๋ถ€์ƒ์„ฑ์ž๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋‹ค. ์˜์กด๊ด€๊ณ„๊ฐ€ ๋งŽ์•„์ง€๋Š” ๋Œ€๊ทœ๋ชจ ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” ์ด๋Ÿฐ ํŠน์ง•์ด ์ƒ์„ฑ์„ ์ œ์–ดํ•  ์ˆ˜ ์—†๊ณ  ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์กฐ์ ˆํ•  ์ˆ˜ ์—†์–ด ๋‹จ์ ์ด ๋˜๋Š”๋ฐ, ์ด๋•Œ๋Š” ์˜์กด๊ด€๊ณ„ ์ฃผ์ž… ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค.

object๋Š” ํด๋ž˜์Šค ์•ˆ์—์„œ ์„ ์–ธํ•  ์ˆ˜๋„ ์žˆ๊ณ , ์ด๋•Œ ์—ญ์‹œ ์ด ๊ฐ์ฒด์˜ ์ธ์Šคํ„ด์Šค๋Š” ํ•˜๋‚˜๋ฟ์ด๋‹ค.

data class Person(val name: String) {
    object NameComparator : Comparator<Person> {
        override fun compare(p1: Person, p2: Person): Int =
            p1.name.compareTo(p2.name)
    }
}
 

์ฝ”ํ‹€๋ฆฐ ํด๋ž˜์Šค ์•ˆ์—๋Š” static ํ•œ ๋ฉค๋ฒ„๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์—†๋‹ค. ๋”ฐ๋ผ์„œ ํด๋ž˜์Šค ๋ฐ–์— ์ตœ์ƒ์œ„ ํ•จ์ˆ˜/ ํ”„๋กœํผํ‹ฐ๋กœ ์„ ์–ธํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š”๋ฐ, ์ตœ์ƒ์œ„ ๋ฉค๋ฒ„๋Š” private๋กœ ๋œ ๋ฉค๋ฒ„์— ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๋‹จ์ ์ด์žˆ๋‹ค. ์ด๋•Œ ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ์ด ๋™๋ฐ˜๊ฐ์ฒด companion object์ด๋‹ค.

class A {
    companion object customName {
        fun bar() {
            println("Companion object called")
        }
    }
}
 

์ด๋ ‡๊ฒŒ ์„ ์–ธ๋œ ๋™๋ฐ˜ ๊ฐ์ฒด์˜ ๋ฉค๋ฒ„์— ์ ‘๊ทผํ•˜๋ ค๋ฉด A.bar() ์ฒ˜๋Ÿผ ํ•˜๋ฉด ๋œ๋‹ค. private ๋ฉค๋ฒ„์— ์ ‘๊ทผํ•  ์ˆ˜์žˆ๋‹ค๊ณ  ํ–ˆ๋Š”๋ฐ, ์ด ๋ง์€ ๊ณง private ์ƒ์„ฑ์ž๋ฅผ ์—ฌ๊ธฐ์„œ ํ˜ธ์ถœํ•˜๋ฉด ๋œ๋‹ค๋Š” ๋œป์ด๋‹ค. ๋™๋ฐ˜๊ฐ์ฒด๋Š” ํด๋ž˜์Šค ์•ˆ/๋ฐ–์˜ private ๋ฉค๋ฒ„์— ์ ‘๊ทผ ๊ฐ€๋Šฅํ•ด์„œ, private ์ฒ˜๋ฆฌ๋œ ์ฃผ ์ƒ์„ฑ์ž๋ฅผ ์—ฌ๊ธฐ์„œ ํ˜ธ์ถœํ•˜๋Š” ์ผ์ข…์˜ ํŒฉํ† ๋ฆฌ ํŒจํ„ดํ˜•ํƒœ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ฝ”๋“œ๋กœ ๋ฐ”๋กœ ๋ณด๋ฉด ์ดํ•ด๊ฐ€ ๋น ๋ฅด๋‹ค.

class User private constructor(val nickname: String) {
    companion object {
        fun newSubscribingUser(email: String) =
            User(email.substringBefore('@'))

        fun newFacebookUser(accountId: Int) =
            User(getFacebookName(accountId))
    }
}
 

๋™๋ฐ˜๊ฐ์ฒด์˜ ๋ฉ”์„œ๋“œ๋Š” private ์ƒ์„ฑ์ž๋ฅผ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋Š” ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๋‹ค.

 

User ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ, ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๋˜ํ•œ object์ด๊ธฐ ๋•Œ๋ฌธ์— ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๋กœ ์ƒ์„ฑ๋œ ์ธ์Šคํ„ด์Šค๋Š” ์ฃผ์†Œ๋ณ„๋กœ ์œ ์ผํ•˜๊ณ , ์ด๋ฏธ ์ƒ์„ฑ๋œ ์ธ์Šคํ„ด์Šค์— ์ ‘๊ทผ ํ•  ๋•Œ๋Š” ์บ์‹œ์— ์žˆ๋Š” ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋™๋ฐ˜ ๊ฐ์ฒด๋Š” ํด๋ž˜์Šค ์•ˆ์— ์ •์˜๋œ ์ผ๋ฐ˜ ๊ฐ์ฒด์ด๊ณ  ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์†, ํ™•์žฅํ•จ์ˆ˜, ํ”„๋กœํผํ‹ฐ ์ •์˜ ,๋‹ค ๊ฐ€๋Šฅํ•˜๋‹ค. ๋™๋ฐ˜ ๊ฐ์ฒด์—์„œ ๊ตฌํ˜„๋œ ์ธํ„ฐํŽ˜์ด์Šค๋Š” ๋™๋ฐ˜๊ฐ์ฒด๋ฅผ ๋‘˜๋Ÿฌ์‹ผ ํด๋ž˜์Šค ์ด๋ฆ„์œผ๋กœ ์ ‘๊ทผ ํ•  ์ˆ˜์žˆ๋‹ค.

 

์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ๋™๋ฐ˜๊ฐ์ฒด๋ฅผ ๊ฐ–๋Š” ํด๋ž˜์Šค์˜ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•ด ํ•„์š”ํ•œ ๋ฉ”์„œ๋“œ๋กœ ์ธ์Šคํ„ด์Šค๋ฅผ ๋„˜๊ธธ ์ˆ˜ ์žˆ๋‹ค.

fun loadJson<T>(factory: C<T>): T{

}
class P(val name: String){
    companion object : C<P>{
        // ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„
        override fun fromJson(jsonText: String):P = ...
    }
}
loadJson(P)
 

์ด๋Ÿฐ์‹์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด๋ฆ„์—†์ด ์ •์˜๋œ ๋™๋ฐ˜๊ฐ์ฒด์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” Companion์ด๋ผ๋Š” ์ด๋ฆ„์„ ์จ์•ผ๋˜๋Š”๋ฐ, ๋™๋ฐ˜๊ฐ์ฒด ํ™•์žฅ ์˜ˆ์‹œ์™€ ๊ฐ™์ด ๋ณด์ž.

class A {
    companion object {
        fun bar() {
            println("Companion object called")
        }
    }
}

fun A.Companion.foo(): String{
    return "foo"
}

fun main(args: Array<String>) {
    A.bar()
    A.foo()
}
 

๋™๋ฐ˜๊ฐ์ฒด์˜ ํ™•์žฅํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•˜๋ ค๋ฉด ๋นˆ ๋™๋ฐ˜ ๊ฐ์ฒด๋ผ๋„ ๊ฐ–๊ณ ์žˆ์–ด์•ผํ•œ๋‹ค.

๋ฌด๋ช… ๊ฐ์ฒด๋ฅผ ์ •์˜ํ•  ๋•Œ๋„ object๊ฐ€ ์‚ฌ์šฉ๋œ๋‹ค. ์•ˆ๋“œ๋กœ์ด๋“œ์˜ setOnClickListener์—์„œ๋„ ๋ณด์˜€๋˜ ๋ฐฉ๋ฒ•์ธ๋ฐ

etSignupPhone.setOnClickListener { object : View.OnClickListener {
                override fun onClick(v: View?) {
                    TODO("Not yet implemented")
                }
            } }
 

์ด๋Ÿฐ์‹์œผ๋กœ ์˜ˆ์ „์—๋Š” ์‚ฌ์šฉํ–ˆ์—ˆ๋‹ค. EditText์—์„œ TextWatcher๋ฅผ object๋กœ ๋„˜๊ธฐ๋Š” ๊ฒƒ๋„ ์ด ๊ฒฝ์šฐ๋‹ค. ๊ฐ์ฒด ์ด๋ฆ„์ด ์—†๋Š”๋ฐ, ๊ฐ์ฒด ์‹์˜ ํŠน์ง•์ด๋‹ค. ์ด๋ฆ„์„ ๋ถ™์—ฌ์•ผ ํ•œ๋‹ค๋ฉด

val clickListener = object : View.OnClickListener {
                override fun onClick(v: View?) {
                    TODO("Not yet implemented")
                }
            }
 

์ด๋Ÿฐ์‹์œผ๋กœ ๋ณ€์ˆ˜์— ๋Œ€์ž…ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

 

์ค‘์š”ํ•œ ์ ์€ ๋ฌด๋ช…๊ฐ์ฒด๋Š” ์‹ฑ๊ธ€ํ„ด์ด ์•„๋‹ˆ๋ผ, ์‚ฌ์šฉ ์‹œ ๋งˆ๋‹ค ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ๊ฐ์ฒด ์‹์€ ๋ฌด๋ช… ๊ฐ์ฒด ์•ˆ์—์„œ ์—ฌ๋Ÿฌ ๋ฉ”์„œ๋“œ๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋“œ ํ•ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ์— ์œ ์šฉํ•˜๋‹ค. ์ด ์˜ˆ์‹œ๊ฐ€ ๋ฐ”๋กœ EditText์˜ addTextChangedListener์ธ ๊ฒƒ์ด๊ณ , ๊ตฌํ˜„์ด ํ•„์š”ํ•œ ์ถ”์ƒ๋ฉ”์„œ๋“œ๊ฐ€ ํ•˜๋‚˜์ธ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋œปํ•˜๋Š” SAM ์ข…๋ฅ˜์ธ setOnClickListener๋Š” ๋žŒ๋‹ค๋ฅผ ์‚ฌ์šฉํ•ด ๋ฐ”๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

"๋Œ“๊ธ€, ๊ณต๊ฐ ๋ฒ„ํŠผ ํ•œ ๋ฒˆ์”ฉ ๋ˆ„๋ฅด๊ณ  ๊ฐ€์ฃผ์‹œ๋ฉด ํฐ ํž˜์ด ๋ฉ๋‹ˆ๋‹ค"
๋ฐ˜์‘ํ˜•

'๐Ÿ“– > ์ฝ”ํ‹€๋ฆฐ ์ธ ์•ก์…˜๐Ÿ“–' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

Kotlin in action(6)  (0) 2023.10.10
Kotlin in action(5)  (0) 2023.10.10
Kotlin in action(3)  (0) 2023.10.10
Kotlin in action(2)  (0) 2023.10.10
Kotlin in action(9)  (0) 2023.10.10
COMMENT