본문 바로가기

Study/Kotlin Study

[Kotlin] 단순한 변수 이상인 프로퍼티

+ 항공대학교 김철기 교수님의 객체 지향 프로그래밍 과목 내용를 정리한 글입니다.

최상위 프로퍼티

- 클래스에 소속되지 않은 프로퍼티는 전역 변수/상수의 역할을 한다.

- public/internal/private 등의 가시성 지정이 가능하다.

private val prefix = "Hello, " // 전역 변수
// private 선언이기 때문에 다른 파일에서는 호출이 불가능하다.

fun main() {
    val name = readLine()?: return // null이면 return
    println("$prefix $name")
}

늦은 초기화 (lateinit)

- 생성자에서 초기화되지는 않지만, 프로그램 흐름 상 실사용 시 초기화되는 것이 명백한 변수에는 lateinit이라는 예약어로 표기하여 null값이 불가함을 표기할 수 있다. (var의 늦은 초기화)

- 실제 초기화가 안될 경우 UninitializedPropertyAccessException이 발생하므로 !!를 사용한 것과 비슷한 특성을 가진다. (위험성을 갖는다.)

=> 가능하면 by lazy를 권장한다. (val의 늦은 초기화)

import java.io.File

class Content {
    lateinit var text: String
    // 실 사용되기 전 loadFile이 반드시 사전 호출되어 초기화 됨을 프로그래머가 확인해야한다.

    fun loadFile(file: File) {
        text = file.readText()
    }
}

fun main() {
    val c = Content()

    println(c.text) // kotlin.UninitializedPropertyAccessException 발생
}

커스텀 접근자

- 프로퍼티에 대한 접근을 함수로 수행한다.

class Person (val firstName: String, val familyName: String) {
    val fullName: String
        get() { // fullName이 호출될 때마다 get함수 호출
            println("get() has been called.")
            return "$firstName $familyName"
        }
}

fun main() {
    val p = Person("Charlie", "Kim")

    println(p.fullName) // get 함수 호출
}

 

- 식이 본문인 형태의 사용도 가능하다.

val fullName: String
	get() = "$firstName $familyName"

뒷받침하는 필드

- 커스텀 접근자 게터를 사용하는 경우도 메모리에 뒷받침하는 필드를 두어 값을 저장할 수 있다.

- 이 경우 field라는 예약어로 해당 필드에 접근한다.

class Person (val firstName: String, val familyName: String, age: Int) {
    val age = age // 뒷받침하는 필드
        get() {
            println("Getter was called.")
            return field
        }
}

fun main() {
    val p = Person("Charlie", "Kim", 40)

    println(p.age) // get 함수 호출
}

가변 프로퍼티를 위한 세터

- var 프로퍼티를 위해서는 set(value)를 통하여 setter를 둘 수 있다.

class Person (val firstName: String, val familyName: String) {
    var age: Int? = null
        set(value) {
            if (value != null && value <= 0) { // 널이 아니고 음수라면
                throw IllegalArgumentException("Invalid age: $value") // 예외 발생
            }
            field = value // age = value
        }
}

fun main() {
    val p = Person("Charlie", "Kim")
    p.age = 20 // 커스텀 setter 호출
    println(p.age)
}

뒷받침하는 필드가 없는 가변 프로퍼티

- getter와 setter을 모두 생성해야한다.

class Person (var firstName: String, var familyName: String) {
    var fullName // 뒷받침하는 필드가 없는 프로퍼티
        get() = "$firstName $familyName"
        set(value) {
            val names = value.split(" ")
            if (names.size != 2) throw IllegalArgumentException("Invalid full name: $value")
            firstName = names[0]
            familyName = names[1]
        }
}

fun main() {
    val p = Person("Charlie", "Kim")
    p.fullName = "Gildong Hong"
    println(p.firstName)
    println(p.familyName)
    println(p.fullName)
}

프로퍼티 접근자를 통한 접근 통제

- 프로퍼티의 get / set 각각에 대하여 다른 접근자를 두어 접근을 통제할 수 있다.

=> 보통 클래스 외부에서는 값을 변경할 수 없지만, 읽을 수는 있도록 할 때 사용한다.

import java.util.*

class Person (name: String) {
    var lastChanged: Date? = Date() // 가장 최근에 변경된 날짜 저장
        private set // 클래스 외부에서는 값을 변경할 수 없다.
    var name: String = name
        set(value) {
            lastChanged = Date() // 현재 날짜로 변경
            field = value
        }
}

fun main() {
    val p = Person("Gildong")
    p.name = "Charlie"
    println(p.lastChanged)
}

지연 계산 프로퍼티 (by lazy)

- 프로퍼티 접근 때까지 프로퍼티의 값에 대한 연산을 지연한다. (val만 가능하다.)

- lateinit과는 다르게 널 안전하므로 권장된다.

import java.io.File

class Content(var file: File) {
    val text by lazy {
        println("reading text from file")
        file.readText()
    }
}

fun main() {
    val c = Content(File("Quiz.txt"))
    println("Before access text")
    println(c.text) // text를 읽을 때만 파일을 읽는다.
}

'Study > Kotlin Study' 카테고리의 다른 글

[Kotlin] 함수형 프로그래밍  (0) 2023.09.22
[Kotlin] 객체 (Object)  (0) 2023.09.17
[Kotlin] 널 가능성  (0) 2023.09.12
[Kotlin] 함수  (4) 2023.09.03
[Kotlin] 클래스 정의하기  (1) 2023.08.28