πŸ“‹ Kotlin을 μ •λ³΅ν•΄λ΄…μ‹œλ‹€ 2


μ½”ν‹€λ¦° DSL

DLSλž€?

  • 도메인 νŠΉν™” μ–Έμ–΄ (Domain-specific language) ↔️ λ²”μš© ν”„λ‘œκ·Έλž˜λ° μ–Έμ–΄
  • 선언적 μ–Έμ–΄
  • μ„ΈλΆ€ 싀행은 μ–Έμ–΄λ₯Ό ν•΄μ„ν•˜λŠ” 엔진에 맑김
  • 컴파일 μ‹œμ μ— μ œλŒ€λ‘œ κ²€μ¦ν•˜λŠ” 것이 어렀움

μ½”ν‹€λ¦° DSLμ΄λž€?

  • λ²”μš© μ–Έμ–΄(= μ½”ν‹€λ¦°)둜 μž‘μ„±λœ ν”„λ‘œκ·Έλž¨μ˜ 일뢀
  • λ²”μš© 언어와 λ™μΌν•œ 문법 μ‚¬μš©
  • 호좜 κ²°κ³Όλ₯Ό 객체둜 λ³€ν™˜ν•˜κΈ° μœ„ν•΄ λ…Έλ ₯ν•  ν•„μš” μ—†μŒ
  • νƒ€μž… μ•ˆμ •μ„± 보μž₯

코틀린은 κ°„κ²°ν•œ ꡬ문을 μ–΄λ–»κ²Œ μ§€μ›ν•˜λŠ”κ°€?

  • ν™•μž₯ ν•¨μˆ˜
  • μ€‘μœ„ 호좜
  • μ—°μ‚°μž μ˜€λ²„λ‘œλ”©
  • get λ©”μ„œλ“œμ— λŒ€ν•œ κ΄€λ‘€
  • λžŒλ‹€λ₯Ό κ΄„ν˜Έ λ°–μœΌλ‘œ λΉΌλŠ” κ΄€λ‘€
  • μˆ˜μ‹  객체 지정 λžŒλ‹€

ν™•μž₯ ν•¨μˆ˜ Extension functions

  • 코틀린은 클래슀λ₯Ό ν™•μž₯ν•΄μ„œ μƒˆλ‘œμš΄ κΈ°λŠ₯을 κ°œλ°œν•  수 μžˆλ„λ‘ 지원
  • 상속 κ³ΌλŠ” 쑰금 λ‹€λ₯Έ κ°œλ…
  • ex) μ™ΈλΆ€ 라이브러리λ₯Ό μ‚¬μš©ν•  λ•Œ 이 자체 ν΄λž˜μŠ€λŠ” λ³€κ²½ν•  수 μ—†μ§€λ§Œ 이λ₯Ό ν™•μž₯ν•΄ μ›ν•˜λŠ” μƒˆλ‘œμš΄ ν•¨μˆ˜λ₯Ό λ§Œλ“€ 수 있음
"Kotlin".lastChar()

fun String.lastChar(): Char {
    return this.get(this.length - 1)
}

μ€‘μœ„ ν‘œκΈ° Infix notation

μ€‘μœ„ν‘œκΈ°λ²•?

infix(μ€‘μœ„ν‘œκΈ°λ²•) : μΌμƒμƒν™œμ—μ„œμ˜ μˆ˜μ‹ ν‘œκΈ°λ²•μœΌλ‘œ 두 개의 ν”Όμ—°μ‚°μž 사이에 μ—°μ‚°μžκ°€ μ‘΄μž¬ν•˜λŠ” ν‘œν˜„λ°©μ‹μ΄λ‹€. ex) X + Y

Kotlinμ—μ„œ infix ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ μ€‘μœ„ν‘œκΈ°λ²•μœΌλ‘œ ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•  수 μžˆλ‹€. 단, μ•„λž˜ μš”κ±΄μ„ μΆ©μ‘±ν•΄μ•Ό ν•œλ‹€.

  • They must be member functions or extension functions. (멀버 ν•¨μˆ˜ ν˜Ήμ€ ν™•μž₯ ν•¨μˆ˜μΌ λ•Œ)
  • They must have a single parameter. (단일 맀개 λ³€μˆ˜μΌ λ•Œ)
  • The parameter must not accept a variable number of arguments and must have no default value. (κ°€λ³€μΈμžλ₯Ό λ°›μœΌλ©΄ μ•ˆλ˜κ³  κΈ°λ³Έ 값을 가지면 μ•ˆλœλ‹€.)
1 to "one"

infix fun Any.to(other: Any) = Pair(this, other)

μ—°μ‚°μž μ˜€λ²„λ‘œλ”© Operator overloading

Point(0, 1) + Point(1, 2)

data class Point(val x: Int, val y: Int) {
    operator fun plus(other: Point): Point = Point(x + other.x, y + other.y)
}
  • plus ν•¨μˆ˜ μ•žμ— operator ν‚€μ›Œλ“œλ₯Ό λΆ™μ—¬ μ—°μ‚°μž μ˜€λ²„λ‘œλ”©μ„ ν•˜λŠ” ν•¨μˆ˜μž„μ„ λͺ…μ‹œ
  • ν™•μž₯ ν•¨μˆ˜λ‘œ μ •μ˜ν•  μˆ˜λ„ 있음

이항 μ‚°μˆ  μ—°μ‚° μ˜€λ²„λ‘œλ”©

μ—°μ‚° μš°μ„ μˆœμœ„ 식 ν•¨μˆ˜ 이름
1 a * b times
1 a / b div
1 a % b mod(1.1λΆ€ν„° rem)
2 a + b plus
2 a - b minus
  • 더 λ§Žμ€ μ—°μ‚°μžμ— λŒ€ν•œ λ©”μ„œλ“œλŠ” κ³΅μ‹λ¬Έμ„œ μ°Έκ³ 

get λ©”μ„œλ“œμ— λŒ€ν•œ κ΄€λ‘€ Indexed access operator

val names = listOf("am", "mazzi")
names.get(0)
names[0]
  • get이 μ•„λ‹Œ 인덱슀둜 μ ‘κ·Όν•œλ‹€.

λžŒλ‹€λ₯Ό κ΄„ν˜Έ λ°–μœΌλ‘œ λΉΌλ‚΄λŠ” κ΄€λ‘€ Passing a lambda to the last parameter

check(false) { "Check failed." }

μˆ˜μ‹  객체 지정 λžŒλ‹€ Lambda with receiver

  • λžŒλ‹€ ν•¨μˆ˜λ₯Ό μ“Έ λ•Œ λ‚΄κ°€ 자주 쓰고싢은 객체λ₯Ό 미리 μ§€μ •ν•΄μ„œ μ‚¬μš©ν•˜λŠ” λžŒλ‹€

μˆ˜μ‹  객체?

  • ν™•μž₯ ν•¨μˆ˜μ—μ„œμ˜ thisλŠ” ν™•μž₯된 클래슀의 객체
  • 즉 ν™•μž₯ ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜λŠ” κ·Έ 객체λ₯Ό μ˜λ―Έν•˜λŠ”λ° 이 객체가 λ°”λ‘œ μˆ˜μ‹  객체

with

  • 첫 번째 인자둜 받은 객체λ₯Ό 두 번째 인자둜 받은 λžŒλ‹€μ˜ μˆ˜μ‹  객체둜 λ§Œλ“¦

withλ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šμ„ 경우

fun alphabet(): String {
    val result = StringBuilder()
    for (letter in 'A'..'Z') {
    	result.append(letter)
    }
    result.append("\nNow I know this alphabet!")
    return result.toString()
}
  • result 의 쀑볡이 λ°œμƒ

withλ₯Ό μ‚¬μš©ν•œ 경우

fun alphabet(): String {
    val stringBuilder = StringBuilder()
    return with(stringBuilder) {
    	for (letter in 'A'..'Z') {
    		this.append(letter)
    	}
        append("\n amazzi~~~!")
        this.toString()
    }
}

// λΆˆν•„μš”ν•œ stringBuilder λ³€μˆ˜λ₯Ό μ—†μ• λ©΄ alpabet ν•¨μˆ˜κ°€ μ‹μ˜ κ²°κ³Όλ₯Ό λ°”λ‘œ λ°˜ν™˜ν•˜κ²Œ λœλ‹€.
// λžŒλ‹€ μ‹μ˜ 본문에 μžˆλŠ” λ§ˆμ§€λ§‰ μ‹μ˜ 값을 λ°˜ν™˜
fun alphabet(): String = with(StringBuilder()) {
    	for (letter in 'A'..'Z') {
    		append(letter)
    	}
        append("\nNow I know this alphabet!")
        toString()
    }
}

  • with(stringBuilder, { … }) 와 같은 λžŒλ‹€ ν•¨μˆ˜

apply

  • with와 μœ μ‚¬
  • μœ μΌν•œ μ°¨μ΄λŠ” 항상 μžμ‹ μ—κ²Œ μ „λ‹¬λœ 객체λ₯Ό λ°˜ν™˜
  • 객체의 μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“€λ©΄μ„œ μ¦‰μ‹œ ν”„λ‘œνΌν‹° 쀑 일뢀λ₯Ό μ΄ˆκΈ°ν™”ν•΄μ•Όλ˜λŠ” 경우 유용
fun alphabet(): String = StringBuilder().apply {
    	for (letter in 'A'..'Z') {
    		append(letter)
    	}
        append("\nNow I know this alphabet!")
    }.toString()

μ΄ˆκΈ°ν™”λ₯Ό μ§€μ—°ν•˜λŠ” 방법

  • μ½”ν‹€λ¦°μ—μ„œλŠ” λ³€μˆ˜ 선언을 λ¨Όμ €ν•˜κ³ , μ΄ˆκΈ°νšŒλŠ” λ’€λ‘œ λ―Έλ£¨λŠ” κΈ°λŠ₯듀을 제곡
  • μ‚¬μš©ν• μ§€ λͺ¨λ₯ΈλŠ” 데이터λ₯Ό 미리 μ΄ˆκΈ°ν™”ν•  ν•„μš”κ°€ μ—†μ–΄ μ„±λŠ₯ ν–₯상에 도움

lateInit

  • ν•„μš”ν•  λ•Œ μ΄ˆκΈ°ν™”ν•˜κ³  μ‚¬μš©
  • μ΄ˆκΈ°ν™” ν•˜μ§€ μ•Šκ³  μ‚¬μš©ν•˜λ©΄ μ˜ˆμ™Έ λ°œμƒ
  • var μ—λ§Œ μ‚¬μš© κ°€λŠ₯
  • μ›μ‹œ νƒ€μž…μ—λŠ” μ μš©ν•  수 μ—†μŒ
  • custom getter/setter μ‚¬μš© λΆˆκ°€
  • non-null ν”„λ‘œνΌν‹°λ§Œ μ‚¬μš© κ°€λŠ₯

lazy

  • λ³€μˆ˜λ₯Ό μ„ μ–Έν•  λ•Œ μ΄ˆκΈ°ν™” μ½”λ“œλ„ ν•¨κ»˜ μ •μ˜
  • λ³€μˆ˜κ°€ μ‚¬μš©λ  λ•Œ μ΄ˆκΈ°ν™” μ½”λ“œλ„ λ™μž‘ν•˜μ—¬ λ³€μˆ˜κ°€ μ΄ˆκΈ°ν™” 됨

0602 μ½”λ“œλ¦¬λ·°

enum class Symbol(val symbol: String) {
    DIAMOND("닀이아λͺ¬λ“œ"),
    SPADE("μŠ€νŽ˜μ΄λ“œ"),
    HEART("ν•˜νŠΈ"),
    CLOVER("ν΄λ‘œλ²„"),
  ;
}
  • 1.4λΆ€ν„° , 둜 λλ‚˜λ„ 컴파일 μ—λŸ¬κ°€ μ•ˆλ‚¨

Property와 Field

Field

  • λ‹¨μˆœνžˆ κ°’λ§Œ 가짐
  • 값을 κ°€μ Έμ˜€κ±°λ‚˜ λ³€κ²½ν•  λ•ŒλŠ” 직접 μ°Έμ‘°
  • ν•¨μˆ˜λ‚˜ 블둝 내뢀에 μ„ μ–Έλœ 지역 λ³€μˆ˜λŠ” λͺ¨λ‘ ν•„λ“œλ‘œ κ°„μ£Ό
var count = 100 // λ©”λͺ¨λ¦¬κ°€ ν• λ‹Ήλ˜κ³  값이 μ €μž₯됨
println(count) // count λ³€μˆ˜κ°’μ„ 직접 μ°Έμ‘°ν•˜μ—¬ κ°€μ Έμ˜΄
count += 200 // count λ³€μˆ˜κ°’μ„ 직접 λ³€κ²½

Property

  • μ΅œμƒμœ„ λ³€μˆ˜(ν•¨μˆ˜λ‚˜ 클래슀 외뢀에 μ •μ˜λ¨)λ‚˜ 클래슀의 멀버 λ³€μˆ˜λ‘œ 선언됨
  • μ„ μ–Έ μ‹œ ν•΄λ‹Ή μ†μ„±μ˜ getter/ setterκ°€ μžλ™μœΌλ‘œ 생성됨
  • val 둜 μ„ μ–Έμ‹œ getter 만 생성됨
  • 값을 κ°€μ§€μ§€λ§Œ μ†μ„±μ˜ 값을 κ°€μ Έμ˜€κ±°λ‚˜ λ³€κ²½ν•  λ•ŒλŠ” μžλ™μœΌλ‘œ κ΄€λ ¨ ν•¨μˆ˜κ°€ 호좜됨
    • 이λ₯Ό μ ‘κ·Όμž 라고 함
var count = 100 // λ©”λͺ¨λ¦¬κ°€ ν• λ‹Ήλ˜κ³  값이 μ €μž₯됨
println(count) // count μ†μ„±μ˜ μ ‘κ·Όμžκ°€ ν˜ΈμΆœλ˜μ–΄ 속성값을 λ°˜ν™˜
count += 200 // count μ†μ„±μ˜ μ ‘κ·Όμžκ°€ ν˜ΈμΆœλ˜μ–΄ 속성값을 λ³€ν™˜

μ—₯ 근데 ν•„λ“œμ™€ λ™μΌν•˜κ²Œ μ½”λ“œλ₯Ό μž‘μ„±ν•˜λŠ”λ°? πŸ€”

  • ν”„λ‘œκ·Έλž˜λ¨Έκ°€ λ³΄λŠ” κ΄€μ μ—μ„œλŠ” κ°™μ§€λ§Œ, μ½”ν‹€λ¦° μ»΄νŒŒμΌλŸ¬λŠ” λ‹€λ₯΄κ²Œ λ™μž‘ν•¨
  • λ‹€μŒκ³Ό 같이 count μ†μ„±μ˜ μ ‘κ·Όμžλ₯Ό μžλ™μœΌλ‘œ 생성
  • count μ†μ„±μ˜ 값을 κ°€μ Έμ˜€κ±°λ‚˜ λ³€κ²½ν•  λ•Œ μžλ™μœΌλ‘œ 호좜 됨
var couhnt = 100
	get() = field
	set(value: Int) {
    field = value
  }
  • get() κ³Ό set() 이 μ ‘κ·Όμž
fun main(args: Array<String>) {
  pro1 += pro2
  println(pro1)
}

var pro1 = 100 // μ΅œμƒμœ„ μˆ˜μ€€μ˜ λ³€μˆ˜μ΄λ―€λ‘œ μ†μ„±μž„
var pro2 = 200 // μ΅œμƒμœ„ μˆ˜μ€€μ˜ λ³€μˆ˜μ΄λ―€λ‘œ μ†μ„±μž„
  • pro2의 κ²Œν„°κ°€ ν˜ΈμΆœλ˜μ–΄ 값을 κ°€μ Έμ˜΄
  • pro1의 κ²Œν„°μ—μ„œ λ°˜ν™˜λœ κ°’κ³Ό 더함
  • 이 값이 pro1의 μ„Έν„°μ˜ 인자둜 μ „λ‹¬λ˜μ–΄ pro1의 값이 변경됨
  • pro1 κ²Œν„°μ—μ„œ λ°˜ν™˜λœ 값을 좜λ ₯

ν”„λ‘œνΌν‹°μ— = 을 μ΄μš©ν•΄μ„œ ν• λ‹Ήν•˜λŠ”κ±°λž‘ get을 μ‚¬μš©ν•΄μ„œ

val shouldDraw  = cards.score()
val shouldDraw2 : Boolean
get() = cards.scroe()
  • get을 μ“°λŠ” 것은 맀번 λŒλ•Œλ§ˆλ‹€ 계산이 됨
  • ν”„λ‘œνΌν‹°μ— 접근은 κ³„μ‚°λ˜μ–΄ μžˆλŠ” 값을 씀

Backing fieldsο»Ώ

  • μ»€μŠ€ν…€ getter와 setterλ₯Ό μ œκ³΅ν•  경우 μ‚¬μš©
  • 속성이 ν•„λ“œμ˜ 값을 ν•„μš”λ‘œ ν•  λ•Œ 코틀린은 지원 ν•„λ“œ ν‚€μ›Œλ“œλ₯Ό 제곡
  • getter와 setter λ²”μœ„μ—μ„œλ§Œ μ‚¬μš© κ°€λŠ₯
  • field μ§€μ‹œμžλ₯Ό 톡해 μ†μ„±μ˜ κ²Œν„°λ‚˜ μ„Έν„°μ—μ„œ μ‚¬μš©
var counter = 0 // the initializer assigns the backing field directly
    set(value) {
        if (value >= 0)
            field = value
            // counter = value // ERROR StackOverflow: Using actual name 'counter' would make setter recursive
    }

μ•„λž˜ 예제의 thisλŠ” backing fieldκ°€ μ•„λ‹˜

val isEmpty: Boolean
    get() = this.size == 0

Backing propertiesο»Ώ

  • Backing fields의 체계에 λ§žμ§€ μ•ŠλŠ” μž‘μ—…μ„ μˆ˜ν–‰ν•  경우 μ΄λŠ” Backing propertiesο»Ώκ°€ 됨
class Skills(skills: List<String> = mutableListOf()) {
    private val _skills: MutableList<String> = skills.toMutableList()
    val skills: List<String>
        get() = _skills.toList()

    fun soft(soft: String) {
        this._skills.add(soft)
    }

    fun hard(hard: String) {
        this._skills.add(hard)
    }
}

as ν‚€μ›Œλ“œ μ‚¬μš©ν•΄λ„ λ˜λ‚˜μ—¬?

  • val results = resultBoard.values as List<GameResult>
  • μžλ°”μ˜ νƒ€μž… λ³€ν™˜κ³Ό 같은 것

참고 자료