κ³„λž€μ†Œλ…„ 2023. 9. 9. 15:33

질문

객체와 ꡬ쑰체의 차이점은?

ν΄λž˜μŠ€λŠ” 상속지원&μ°Έμ‘° ν˜•μ‹, κ΅¬μ‘°μ²΄λŠ” κ°’ ν˜•μ‹, 상속 및 λ©”μ†Œλ“œ 지원 ν•˜μ§€ μ•ŠμŒ.

 

ν•¨μˆ˜κ°€ 자료ꡬ쑰 μ•ˆμœΌλ‘œ λ“€μ–΄μ˜€λ©΄ μ–΄λ–€ μž₯점이 μƒκΈ°λŠ”κ°€?

 

μ™œ 정보 은닉은 객체 κΈ°λ°˜μ—μ„œλ§Œ κ°€λŠ₯ν•œκ°€?

 

λ°–μ—μ„œ μ‚¬μš© ν•  수 μ—†κ²Œ μ„ μ–Έ

var time:Double
private Set

 

 

4ν΄λž˜μŠ€λž€?

λ³€μˆ˜μ™€ λ©”μ†Œλ“œλ₯Ό κ°€μ§€λŠ” νƒ€μž…μ„ μ •μ˜ν•˜λŠ” λ‹¨μœ„

클래슀 μ„ μ–Έ
class Person{
//ν”„λ‘œνΌν‹°
var firstName: String = "" 
var familyName: String = "" 
var age: Int = 0

//λ©”μ†Œλ“œ
fun fullName() = "$firstName $familyName"

fun showMe() {
	println("${fullName()}: $age")
    }
}


객체의 생성

val person = Person()// personμ΄λΌλŠ” 객체의 μ°Έμ‘°μžλ³€μˆ˜κ°€ μƒˆλ‘­κ²Œ μƒμ„±ν•œ Person객체λ₯Ό 가리킴

person.firstName = "John" 
person.familyName = "Doe" 
person.age = 25
person.showMe() // John Doe: 25

 

μƒμ„±μž

class Person(firstName: String, familyName: String) { 
	val fullName = "$firstName $familyName"
}
fun main() {
    val person = Person("John", "Doe")
    println(person.fullName)
}

 

init블둝

ν•˜λ‚˜μ˜ μ‹μœΌλ‘œ ν‘œν˜„ν•˜κΈ° νž˜λ“  μ΄ˆκΈ°ν™” λ‘œμ§μ„ μ‹€ν–‰ν•΄μ•Ό ν•  λ•ŒλŠ” init 블둝을 μ‚¬μš©ν•œλ‹€.

class Person(fullName: String) {
    val firstName: String
    val familyName: String
    init {
        val names = fullName.split(" ")
        if (names.size != 2) {
            throw java.lang.IllegalArgumentException ("Invalid name: $fullName")
        }
        firstName = names[0]
        familyName = names[1]
    }
}

fun main() {
    val person = Person("John Doe")
    println(person.firstName)
}

ν”„λ‘œνΌν‹°

ν”„λ‘œνΌν‹°λŠ”  λ°˜λ“œμ‹œ μ΄ˆκΈ°ν™”λ˜μ–΄μ•Ό ν•œλ‹€.

class Person(fullName: String) { 
    val firstName: String
    val familyName: String
    init {
        val names = fullName.split(" ")
        if (names.size == 2) { // names의 크기가 2κ°€ μ•„λ‹κ²½μš°
            firstName = names[0] // firstName, familyName이 μ΄ˆκΈ°ν™” λ˜μ§€μ•ŠμœΌλ―€λ‘œ error λ°œμƒ
            familyName = names[1]
        }
    }
}

μ£Όμƒμ„±μžμ™€ λΆ€μƒμ„±μž

μ£Όμƒμ„±μž

μ£Όμƒμ„±μž νŒŒλΌλ―Έν„°λŠ” ν”„λ‘œνΌν‹° μ΄ˆκΈ°ν™”λ‚˜ init 블둝 μ•ˆμ—μ„œλ§Œ μ‚¬μš©ν•  수 있음.

class Person(firstName: String, familyName: String) {
    val fullName = "$firstName $familyName"
    fun printFirstName() {
        println(firstName) // Error: firstName is not available here
        }
    }

 μƒμ„±μž νŒŒλΌλ―Έν„° μ •μ˜ μ•žμ— valμ΄λ‚˜ varλ₯Ό 뢙이면 νŒŒλΌλ―Έν„°λ₯Ό ν”„λ‘œνΌν‹°λ‘œ λ§Œλ“€ 수 μžˆλ‹€.

class Person(val firstName: String, val familyName: String) { ...

 

λΆ€μƒμ„±μž

ν•˜λ‚˜ μ΄μƒμ˜ μƒμ„±μžλ₯Ό μ •μ˜ν•˜κ³  싢을 λ•ŒλŠ” fun ν•¨μˆ˜ λŒ€μ‹  constructorλΌλŠ” μ˜ˆμ•½μ–΄λ‘œ μΆ”κ°€ μƒμ„±μžλ₯Ό μ •μ˜ν•œλ‹€.

class Person {
val fullName: String
constructor(firstName: String, familyName: String):
    this("$firstName $familyName") 
    constructor(fullName: String) {
        this.fullName = fullName
    }
}

constructorμ˜ˆμ•½μ–΄λ‘œ μƒμ„±ν•œκ±΄ λΆ€μƒμ„±μž!

this():κΈ°λ³Έμƒμ„±μžμ—κ²Œ μœ„μž„. 첫번째 보쑰 μƒμ„±μžμ—μ„œ μ‚¬μš©

this.fullName: 직접 ν• λ‹Ή. λ‘λ²ˆμ§Έ 보쑰 μƒμ„±μžμ— μ‚¬μš©

 

멀버 κ°€μ‹œμ„±

클래슀 λ‚΄λΆ€ ν”„λ‘œνΌν‹°, λ©”μ†Œλ“œλ§ˆλ‹€ κ°€μ‹œμ„± μ„€μ • κ°€λŠ₯ν•˜λ‹€.

public: 멀버λ₯Ό μ–΄λ””μ„œλ‚˜ λ³Ό 수 μžˆλ‹€. (λ””ν΄νŠΈ)

internal: 컴파일 λͺ¨λ“ˆ λ‚΄λΆ€μ—μ„œλ§Œ 멀버λ₯Ό λ³Ό 수 μžˆλ‹€.

protected: 멀버가 μ†ν•œ ν΄λž˜μŠ€μ™€ κ·Έ ν•˜μœ„ ν΄λž˜μŠ€μ—μ„œ λ³Ό 수 μžˆλ‹€.

private: 멀버가 μ†ν•œ ν΄λž˜μ„œ λ‚΄λΆ€μ—μ„œλ§Œ λ³Ό 수 μžˆλ‹€

 

예제

class Person(private val firstName: String, private val familyName: String) {
    fun fullName() = "$firstName $familyName"
}
• firstNameκ³Ό familyName은 클래슀 λ°–μ—μ„œλŠ” λ³Ό 수 μ—†λ‹€. 
• fullName()은 μ–΄λ””μ„œλ‚˜ λ³Ό 수 μžˆλ‹€

 

μ£Όμƒμ„±μž κ°€μ‹œμ„±


μ£Όμƒμ„±μžμ— publicμ™Έμ˜ κ°€μ‹œμ„±μ„ μ£Όκ³  싢을 경우

class Empty private constructor() { 
    fun showMe() = println("Empty")
}

Empty의 μœ μΌν•œ μƒμ„±μžκ°€ privateμ΄λ―€λ‘œ class μ™ΈλΆ€μ—μ„œ μΈμŠ€ν„΄μŠ€ν™” ν•  수 μ—†μŒ

 

nedsted class: 클래슀 μ•ˆμ— 클래슀λ₯Ό μ„ μ–Έν•  수 μžˆλ‹€.

class Person (val id: Id, val age: Int) {
    class Id(val firstName: String, val familyName: String)
    fun showMe() = println("${id.firstName} ${id.familyName}, $age")
}

fun main() {
    val id = Person.Id("John", "Doe") 
    val person = Person(id, 25)
    person.showMe()
}

 

inner class

λ‚΄ν¬λœ ν΄λž˜μŠ€μ— innerλ₯Ό μ„ μ–Έν•˜λ©΄ λ‚΄λΆ€ 클래슀 (inner class)κ°€ λ˜μ–΄ λ‚΄λΆ€ μΈμŠ€ν„΄μŠ€κ°€ μ™ΈλΆ€ μΈμŠ€ν„΄μŠ€ 1κ°œμ— μ†Œμ†λ˜κ²Œ λœλ‹€.

λ‚΄λΆ€ μΈμŠ€ν„΄μŠ€λŠ” μžμ‹ μ΄ μ†Œμ†λœ μ™ΈλΆ€ μΈμŠ€ν„΄μŠ€μ˜ 멀버에 μ ‘κ·Όν•  수 μžˆλ‹€.

λ‚΄λΆ€ μΈμŠ€ν„΄μŠ€ μƒμ„±μ‹œ μžμ‹ μ΄ μ†Œμ†λœ μ™ΈλΆ€ μΈμŠ€ν„΄μŠ€λ₯Ό λͺ…μ‹œν•΄μ•Ό ν•œλ‹€.

class Person(val firstName: String, val familyName: String) { 
    inner class Possession(val description: String) {
        fun showOwner() = println(fullName()) 
    }
    private fun fullName() = "$firstName $familyName" 
}

fun main() {
    val person = Person("John", Doe")
    val wallet = person.Possession("Wallet") 
    wallet.showOwner() // John Doe
}
class Person(val firstName: String, val familyName: String) { 
    inner class Possession(val description: String) {
        fun showOwner() = println(fullName())
        fun getOwner() = this@Person // μ™ΈλΆ€ Person μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜
    }
    val myWallet = Possession("Wallet") // this.Possession("Wallet")κ³Ό κ°™μŒ 
    fun fullName() = "$firstName $familyName"
}

 

local class

ν•¨μˆ˜ 내에 classλ₯Ό μ •μ˜ν•  경우 

μ§€μ—­ ν΄λž˜μŠ€λŠ” μžμ‹ μ„ λ‘˜λŸ¬μ‹Ό μ½”λ“œμ˜ 멀버에 μ ‘κ·Ό κ°€λŠ₯ν•˜λ‹€.

fun main() { 
    var x = 1
    class Counter {
        fun increment() {
            x++ //μžμ‹ μ„ λ‘˜λŸ¬μ‹Ό μ½”λ“œμ˜ 멀버
        }
    }
    Counter().increment()
    println(x)    // 2
}

 


null

μ •μ˜: 아무것도 μ°Έμ‘°ν•˜μ§€ μ•ŠλŠ” μ°Έμ‘°κ°’μ˜ νŠΉλ³„ν•œ μƒνƒœ

null에 λŒ€ν•΄μ„œ 멀버 접근을 μ‹œλ„ν•˜λ©΄ NullPointerException이 λ°œμƒ

μ»΄νŒŒμΌμ‹œκ°„μ— νŒŒμ•…μ΄ λ˜μ§€ μ•Šμ•„ μ΅œμ•…μ˜μ—λŸ¬ 쀑 ν•˜λ‚˜λ‘œ κ°„μ£Ό

 Kotlinμ—μ„œλŠ” 일반 μ°Έμ‘°ν˜•μ—λŠ” null을 담을 수 μ—†λ‹€.

fun isLetterString(s: String): Boolean { 
    if (s.isEmpty()) return false
    for (ch in s) {
        if (!ch.isLetter()) return false 
    }
    return true 
}
fun main() { 
    println(isLetterString("abc"))    // Ok
    println(isLetterString(null))    // 컴파일 μ—λŸ¬
}

 

널이 될 수 μžˆλŠ” νƒ€μž… (nullable)

참쑰자의 νƒ€μž… 뒀에 ?(λ¬ΌμŒν‘œ)λ₯Ό 뢙인닀.

널이 될 수 μžˆλŠ” νƒ€μž…μ˜ λ³€μˆ˜λ₯Ό 일반 νƒ€μž…λ³€μˆ˜μ— λŒ€μž…ν•˜λ©΄ nullκ°€λŠ₯μ„± λ•Œλ¬Έμ— 컴파일 μ—λŸ¬κ°€ λ°œμƒν•œλ‹€.

fun isBooleanString(s: String?) = s == "false" || s == "true"

fun main() { 
    println(isBooleanString(null))     // Ok
    val s: String? = "abc"             // Ok
    val ss: String = s                 //Error: type mismatch
}

 

널이 될 수 μžˆλŠ” νƒ€μž…μ— λŒ€ν•œ 멀버접근

널이 될 수 μžˆλŠ” νƒ€μž…μ€ κΈ°λ³Ένƒ€μž…μ˜ 멀버접근 λ“±μ˜ 연산을 λ°”λ‘œ μˆ˜ν–‰ν•˜λ € ν•  경우 컴파일 μ—λŸ¬κ°€ λ°œμƒν•œλ‹€.

fun isLetterString(s: String?): Boolean {
    if (s.isEmpty()) return false       // Error
    for (ch in s) {                     // Error 
        if (!ch.isLetter()) return false
    }
    return true
}

 

널가λŠ₯μ„± 제거λ₯Ό ν†΅ν•œ 슀마트캐슀트

ν”„λ‘œκ·Έλž¨ νλ¦„μ—μ„œ null인 κ²½μš°κ°€ λ…Όλ¦¬μ μœΌλ‘œ 배제되면 μ»΄νŒŒμΌλŸ¬λŠ” 이λ₯Ό μΈμ§€ν•˜κ³  널 κ°€λŠ₯성을 μ œκ±°ν•˜κ³  μ»΄νŒŒμΌν•œλ‹€.

fun isLetterString(s: String?): Boolean {
    if (s == null) return false
    //이 μ‹œμ λΆ€ν„° sκ°€ null이 μ•„λ‹˜μ΄ λͺ…λ°±
    if (s.isEmpty()) return false
    for (ch in s) {
        if (!ch.isLetter()) return false
    }
    return true
}

 

쑰건문 λ‚΄μ—μ„œμ˜ 슀마트 캐슀트

when, λ£¨ν”„μ—μ„œμ˜ 쑰건 검사

fun describeNumber(n: Int?) = when {
    null -> "null"
    n >= 0 && n <= 10 -> "small"
    n >= 11 && n <= 100 -> "large"
    else -> "out of range"
}

||λ‚˜ &&μ—°μ‚°μ˜ 슀마트 캐슀트

fun isSingleChar(s: String?) = s != null && s.length == 1

 

객체의 κ°€λ³€ ν”„λ‘œνΌν‹°μ™€ 슀마트 캐슀트

객체의 κ°€λ³€ ν”„λ‘œνΌν‹°λŠ” μŠ€λ§ˆνŠΈμΊμŠ€νŠΈκ°€ λΆˆκ°€λŠ₯ ν•˜λ‹€.

ν”„λ‘œκ·Έλž¨ λ‹€λ₯Έ μ–΄λ–€κ³³μ—μ„œ 값을 λ°”κΏ€ 수 있기 λ•Œλ¬Έ

 
class MyString { 
    var str: String?
    fun isStrEmpty(): Boolean { 
        if (str == null) return true
        if (str.isEmpty()) return true
        else return false         // Error
        }

 

널 μ•„λ‹˜ λ‹¨μ–Έμ—°μ‚°μž(!!)

널이 될 수 μžˆλŠ” νƒ€μž…μ— λŒ€ν•˜μ—¬ ν”„λ‘œκ·Έλž˜λ¨Έκ°€ 널이 μ•„λ‹ˆλΌκ³  ν™•μ–Έν•΄ μ£ΌλŠ” μ—°μ‚°μž

κ·ΈλŸΌμ—λ„ λΆˆκ΅¬ν•˜κ³  λ„μ΄μ—ˆμ„ 경우 KotlinNullPointerException이 λ°œμƒν•œλ‹€.

널 μ•„λ‹˜ λ‹¨μ–Έμ—°μ‚°μžλŠ” λ°˜λ“œμ‹œ ν•„μš”ν•œ 경우λ₯Ό μ œμ™Έν•˜κ³ λŠ” μ‚¬μš©ν•˜μ§€λ§ 것

예) fun readInt() = readLine()!!.toInt()

readLine()은nullμ„λ°˜ν™˜ν• μˆ˜μžˆλ‹€.

μœ„λ¬Έμž₯λ„ν”Όν• μˆ˜μžˆμœΌλ©΄ν”Όν•˜λŠ”κ²ƒμ΄μ’‹λ‹€.

μœ„μ˜ˆμ—μ„œreadInt()μ˜λ°˜ν™˜ν˜•μ€Int이닀.(λ„μ΄λ μˆ˜μ—†μŒ)

 

 

μ•ˆμ „ν•œ 호좜 μ—°μ‚°μž

• 예) fun readInt() = readLine()?.toInt()

• μˆ˜μ‹ κ°μ²΄κ°€ 널이 μ•„λ‹Œκ²½μš°μ—λŠ” μ˜λ―ΈμžˆλŠ” 일을 ν•˜κ³ , 널인 κ²½μš°μ—λŠ” 널을 λ°˜ν™˜ν•œλ‹€.

• μœ„μ˜ μ˜ˆμ œμ—μ„œ readLine()이 널을 λ°˜ν™˜ν•˜λ©΄ toInt() μž‘μ—…μ„ ν•˜μ§€ μ•Šκ³  전체 λ¬Έμž₯이 null을 λ°˜ν™˜ν•¨

• 결과적으둜 readInt()λŠ” 널을 λ°˜ν™˜ν•  μˆ˜λ„ 있게 λ˜μ–΄ λ°˜ν™˜ νƒ€μž…μ΄ Int?κ°€ 됨

 

μ—˜λΉ„μŠ€ μ—°μ‚°μž

μ—°μ‚°μž μ™Όμͺ½μ˜ 값이 null인 경우 μ—°μ‚°μž 였λ₯Έμͺ½ κ°’μœΌλ‘œ μΉ˜ν™˜ν•œλ‹€.

fun readInt() = readLine()?.toInt() ?: 0

μœ„μ—μ„œ readLine()이 null인 경우 readLine()?.toInt() 값이 nullμ΄λœλ‹€. 이 λ•Œ 이 null을 ?: 0이 0으둜 μΉ˜ν™˜ν•œλ‹€.

결과적으둜 readInt()λŠ” null을 λ°˜ν™˜ν•  κ°€λŠ₯성이 μ—†μ–΄μ§€λ―€λ‘œ λ°˜ν™˜ν˜•μ΄ Intκ°€ λœλ‹€.

fun sayHello(name: String?) {
	println("Hello, " + (name ?: "Unknown"))
}
fun main() {
	sayHello("John") // Hello, John
	sayHello(null) // Hello, Unknown
}

 

μ‹€μŠ΅μ½”λ“œ

class Car(
    val name:String,
    var speed: Double=0.0) {
    var distance: Double? = 0.0
}


class Track{
    private val cars = Array<Car?>(8){_->null}
    private var numCars:Int = 0
    private var time:Double =0.0

    fun getTime():Double{
        return time
    }

    fun addCar(c:Car){
        cars[numCars]=c
        numCars++
    }

    fun timePassed(t:Double){
        for(i:Int in 0 until numCars) {
            val c: Car? = cars[i]
            c?.distance = c?.distance?.plus(t * c.speed)
        }

        time +=t
    }
    fun removeCar(num:Int){
        cars[num] =null
    }

}

fun main() {
    val track = Track()
    val a = Car("Alpha")
    track.addCar(a)
    val b = Car("Bravo",30.0)
    track.addCar(b)

    while (true){
        track.timePassed(1.0)
        a.speed=a.speed+1.0
        if ((a.distance?:0.0)> (b.distance?:0.0)){
            println("${a.name} passed ${b.name} at ${track.getTime()}")
            break
        }
    }

}
2λ²ˆμ€„μ— ?λ₯Ό μ¨μ•Όν•˜λŠ” 이유
λ‚˜μ€‘μ— distance에 null ν• λ‹Ήν• μˆ˜ 있음

8,9,10λ²ˆμ€„μ— private인 이유
cars, numCars, time λ³€μˆ˜λŠ” Track 클래슀 λ‚΄λΆ€μ—μ„œλ§Œ μ‚¬μš©λ˜μ–΄μ•Ό ν•˜κΈ° λ•Œλ¬Έμ— private둜 μ„ μ–Έ

42λ²ˆμ€„ if(a.distance > b.distance)μ•„λ‹Œ 이유
이 λΆ€λΆ„μ—μ„œ a.distance와 b.distanceλŠ” λ‘˜ λ‹€ nullableν•œ Double? νƒ€μž…μœΌλ‘œ μ„ μ–Έλ˜μ—ˆμŠ΅λ‹ˆλ‹€.
nullable λ³€μˆ˜λ₯Ό 직접 비ꡐ할 λ•Œ ?: μ—°μ‚°μžλ₯Ό μ‚¬μš©ν•˜λ©΄ null 값이 μžˆλŠ” 경우 기본값을 μ§€μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
즉, a.distance ?: 0.0와 b.distance ?: 0.0λŠ” 각각 a.distance와
b.distance 값이 null인 경우 0.0을 μ‚¬μš©ν•©λ‹ˆλ‹€.
λ”°λΌμ„œ 이 뢀뢄은 a.distance와 b.distance 쀑 ν•˜λ‚˜λΌλ„ null이면 0.0으둜 κ°„μ£Όν•˜κ³  λΉ„κ΅ν•©λ‹ˆλ‹€.
이것이 μ›ν•˜λŠ” λ™μž‘μ΄ μ•„λ‹ˆλΌλ©΄, null인 경우λ₯Ό λ”°λ‘œ μ²˜λ¦¬ν•΄μ•Ό ν•©λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄,
a.distance와 b.distanceκ°€ null이면 비ꡐλ₯Ό μŠ€ν‚΅ν•˜λ„λ‘ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.