λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°

Android & Kotlin

[λ‚΄λ³΄λ‚΄λ²ˆ] Properties | Kotin

Kotlin을 κ³΅λΆ€ν•˜λ©΄μ„œ 상속에 λŒ€ν•œ 뢀뢄을 κ³΅λΆ€ν•˜κ³ μž 곡식 λ¬Έμ„œλ₯Ό 직접 λ²ˆμ—­ν–ˆλ‹€.
ν•΄λ‹Ή λ¬Έμ„œλŠ” 2022λ…„ 3μ›” 3일자 곡식 λ¬Έμ„œμ΄λ‹€.
쀑간쀑간 πŸ‘€ ν‘œμ‹œκ°€ μžˆλŠ” λ¬Έμž₯μ΄λ‚˜ μ†Œμ œλͺ©μ€ λ²ˆμ—­μ΄ λΆ€μ •ν™•ν•  수 있으며 κ΅¬κΈ€μ΄λ‚˜ 파파고 λ²ˆμ—­κΈ°μ˜ 도움을 λ°›μ•˜λ‹€. 

Kotlin κ³΅μ‹λ¬Έμ„œ ν™•μΈν•˜κΈ° ➑️

 

Properties | Kotlin

 

kotlinlang.org

 

β€» μ˜μ–΄ μ „κ³΅μžλ„ ν•΄μ™Έ μœ ν•™νŒŒλ„ μ•„λ‹ˆκΈ°μ— λ²ˆμ—­μ—λŠ” μ˜μ—­, μ˜€μ—­, κ΅¬κΈ€ λ²ˆμ—­μ΄ λ¬΄μˆ˜νžˆ λ§Žμ„ 수 있으며, 혼자 κ³΅μ‹λ¬Έμ„œλ₯Ό μ°Έμ‘°ν•΄κ°€λ©° λ²ˆμ—­ν•˜λ‹€ λ³΄λ‹ˆ μ˜€νƒ€λ„ λ§Žμ„ 수 μžˆλ‹€. μ •ν™•ν•œ λ‚΄μš©μ€ κ³΅μ‹λ¬Έμ„œλ₯Ό 직접 μ‚΄νŽ΄λ³΄κ±°λ‚˜ λ‹€λ₯Έ 정보듀을 더 μ°Ύμ•„λ³΄λŠ” 것을 μΆ”μ²œν•œλ‹€.

(ν•˜μ§€λ§Œ λŒ“κΈ€ ν”Όλ“œλ°±λ„ ν™˜μ˜ν•©λ‹ˆλ‹€πŸ˜ƒ )

 

Declaring properties

Properties in Kotlin classes can be declared either as mutable, using the var keyword, or as read-only, using the val keyword.

μ½”ν‹€λ¦° 클래슀의 속성듀은 var ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ κ°€λ³€μ μœΌλ‘œ μ„ μ–Έν•˜κ±°λ‚˜ val ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ 읽기 μ „μš©μœΌλ‘œλ§Œ μ„ μ–Έν•  수 μžˆλ‹€.

class Address {
    var name: String = "Holmes, Sherlock"
    var street: String = "Baker"
    var city: String = "London"
    var state: String? = null
    var zip: String = "123456"
}
 

To use a property, simply refer to it by its name:

속성을 μ‚¬μš©ν•˜λ €λ©΄ κ·Έ μ΄λ¦„μœΌλ‘œ μ°Έμ‘°ν•˜λ©΄ λœλ‹€:  

fun copyAddress(address: Address): Address {
    val result = Address() // there's no 'new' keyword in Kotlin
    result.name = address.name // accessors are called
    result.street = address.street
    // ...
    return result
}
 

 

Getters and setters

The full syntax for declaring a property is as follows:

속성을 μ„ μ–Έν•˜κΈ° μœ„ν•œ 전체 문법은 μ•„λž˜μ™€ κ°™λ‹€: 

var <propertyName>[: <PropertyType>] [= <property_initializer>]
    [<getter>]
    [<setter>]
 

The initializer, getter, and setter are optional. The property type is optional if it can be inferred from the initializer or the getter’s return type, as shown below:

μ΄ˆκΈ°ν™”, getter, setter은 선택사항이닀. μ†μ„±μ˜ νƒ€μž…μ€ μ΄ˆκΈ°ν™” κ°’μ΄λ‚˜ getter의 λ°˜ν™˜ νƒ€μž…μœΌλ‘œ μ•„λž˜μ™€ 같이 좔둠될 수 있으면 선택사항이닀:

var initialized = 1 // has type Int, default getter and setter
// var allByDefault // ERROR: explicit initializer required, default getter and setter implied
 

The full syntax of a read-only property declaration differs from a mutable one in two ways: it starts with val instead of var and does not allow a setter:

읽기 μ „μš© μ†μ„±μ˜ μ„ μ–Έμ˜ 문법은 κ°€λ³€ν˜•κ³Ό 두 가지 μ μ—μ„œ 차이가 μžˆλ‹€: var λŒ€μ‹  val둜 μ‹œμž‘ν•˜λ©° setterλ₯Ό ν—ˆμš©ν•˜μ§€ μ•ŠλŠ”λ‹€:

val simple: Int? // has type Int, default getter, must be initialized in constructor
val inferredType = 1 // has type Int and a default getter
 

You can define custom accessors for a property. If you define a custom getter, it will be called every time you access the property (this way you can implement a computed property). Here's an example of a custom getter:

속성을 μœ„ν•œ μ‚¬μš©μž 지정 μ ‘κ·Όμžλ₯Ό μ •μ˜ν•  수 μžˆλ‹€. μ‚¬μš©μž 지정 getterλ₯Ό μ •μ˜ν•˜λ©΄, ν•΄λ‹Ή 속성에 μ ‘κ·Όν•  λ•Œλ§ˆλ‹€ 호좜될 것이닀(μ΄λŸ¬ν•œ 방법은 κ³„μ‚°λœ 속성을 κ΅¬ν˜„ν•  수 μžˆλ‹€). μ‚¬μš©μž 지정 getter의 μ˜ˆμ‹œμ΄λ‹€:  

class Rectangle(val width: Int, val height: Int) {
    val area: Int // property type is optional since it can be inferred from the getter's return type
        get() = this.width * this.height
}
fun main() {
    val rectangle = Rectangle(3, 4)
    println("Width=${rectangle.width}, height=${rectangle.height}, area=${rectangle.area}")
}
Width=3, height=4, area=12

You can omit the property type if it can be inferred from the getter:

getterλ‘œλΆ€ν„° μΆ”λ‘ ν•  수 있으면 속성 νƒ€μž…μ„ μƒλž΅ν•  수 μžˆλ‹€: 

val area get() = this.width * this.height
 

If you define a custom setter, it will be called every time you assign a value to the property, except its initialization. A custom setter looks like this:

μ‚¬μš©μž 지정 setterλ₯Ό μ„ μ–Έν•˜λ©΄ μ΄ˆκΈ°ν™”ν•  λ•Œλ₯Ό μ œμ™Έν•˜κ³  속성에 값을 ν• λ‹Ήν•  λ•Œλ§ˆλ‹€ 호좜될 것이닀. μ‚¬μš©μž 지정 setterλŠ” 이와 κ°™λ‹€: 

var stringRepresentation: String
    get() = this.toString()
    set(value) {
        setDataFromString(value) // parses the string and assigns values to other properties
    }
 

By convention, the name of the setter parameter is value, but you can choose a different name if you prefer.

관둀에 따라 setter νŒŒλΌλ―Έν„°μ˜ 이름은 valueμ΄μ§€λ§Œ μ„ ν˜Έν•˜λŠ” μ΄λ¦„μœΌλ‘œ λ³€κ²½ν•  μˆ˜λ„ μžˆλ‹€. 

If you need to annotate an accessor or change its visibility, but you don't need to change the default implementation, you can define the accessor without defining its body:

μ ‘κ·Όμžμ— μ–΄λ…Έν…Œμ΄μ…˜μ„ λ‹¬μ•„μ•Όν•˜κ±°λ‚˜ visibilityλ₯Ό λ³€κ²½ν•˜κ³  μ‹ΆμœΌλ©΄, κΈ°λ³Έ κ΅¬ν˜„μ„ λ³€κ²½ν•  ν•„μš”λŠ” μ—†κ³  λ°”λ””λ₯Ό μ •μ˜ν•˜μ§€ μ•ŠλŠ” μ ‘κ·Όμžλ₯Ό μ •μ˜ν•΄μ£Όλ©΄ λœλ‹€:

var setterVisibility: String = "abc"
    private set // the setter is private and has the default implementation

var setterWithAnnotation: Any? = null
    @Inject set // annotate the setter with Inject
 

πŸ‘€Backing fields

In Kotlin, a field is only used as a part of a property to hold its value in memory. Fields cannot be declared directly. However, when a property needs a backing field, Kotlin provides it automatically. This backing field can be referenced in the accessors using the field identifier:

μ½”ν‹€λ¦°μ—μ„œ ν•„λ“œλŠ” κ·Έ 값을 λ©”λͺ¨λ¦¬μ— 가지고 있기 μœ„ν•œ μ†μ„±μ˜ λΆ€λΆ„μœΌλ‘œλ§Œ μ‚¬μš©λœλ‹€. ν•„λ“œλŠ” μ§μ ‘μ μœΌλ‘œ 선언될 수 μ—†λ‹€. κ·ΈλŸ¬λ‚˜ 속성에 backing ν•„λ“œκ°€ ν•„μš”ν•œ 경우, 코틀린은 μžλ™μœΌλ‘œ μ œκ³΅ν•΄μ€€λ‹€. 이 backing ν•„λ“œλŠ” ν•„λ“œ μ‹λ³„μžλ₯Ό μ‚¬μš©ν•˜μ—¬ μ ‘κ·Όμžμ—μ„œ μ°Έμ‘°ν•  수 μžˆλ‹€: 

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
    }
 

The field identifier can only be used in the accessors of the property.

ν•„λ“œ μ‹λ³„μžλŠ” μ†μ„±μ˜ μ ‘κ·Όμžμ—μ„œλ§Œ μ‚¬μš©λ  수 μžˆλ‹€. 

A backing field will be generated for a property if it uses the default implementation of at least one of the accessors, or if a custom accessor references it through the field identifier.

μ ‘κ·Όμž 쀑 ν•˜λ‚˜ μ΄μƒμ˜ κΈ°λ³Έ κ΅¬ν˜„μ„ μ‚¬μš©ν•˜κ±°λ‚˜ μ‚¬μš©μž 지정 μ ‘κ·Όμžκ°€ ν•„λ“œ μ‹λ³„μžλ₯Ό 톡해 μ°Έμ‘°ν•˜λŠ” 경우 속성에 λŒ€ν•œ backing ν•„λ“œκ°€ μƒμ„±λ  것이닀. 

For example, there would be no backing field in the following case:

예λ₯Ό λ“€λ©΄, μ•„λž˜μ˜ 경우 backing ν•„λ“œκ°€ μ—†λ‹€. 

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

Backing properties

If you want to do something that does not fit into this implicit backing field scheme, you can always fall back to having a backing property:

λ§Œμ•½ μ΄λŸ¬ν•œ μ•”μ‹œ backing ν•„λ“œ scheme이 잘 λ§žμ§€ μ•ŠλŠ” 무언가λ₯Ό ν•˜κΈΈ 원할 λ•Œ backing 속성을 μ‚¬μš©ν•  수 μžˆλ‹€: 

private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
    get() {
        if (_table == null) {
            _table = HashMap() // Type parameters are inferred
        }
        return _table ?: throw AssertionError("Set to null by another thread")
    }
 

On the JVM: Access to private properties with default getters and setters is optimized to avoid function call overhead.

JVMμ—μ„œ: κΈ°λ³Έ getter와 setterκ°€ μžˆλŠ” private 속성에 λŒ€ν•œ 접근은 ν•¨μˆ˜ 호좜 μ˜€λ²„ν—€λ“œλ₯Ό λ°©μ§€ν•˜κΈ° μœ„ν•΄ μ΅œμ ν™”λ˜μ–΄ μžˆλ‹€. 

 

Compile-time constants

If the value of a read-only property is known at compile time, mark it as a compile time constant using the const modifier. Such a property needs to fulfil the following requirements:

읽기 μ „μš© μ†μ„±μ˜ 값이 컴파일 μ‹œκ°„μ— μ•Œλ €μ§„ 경우, const modifierλ₯Ό μ‚¬μš©ν•˜μ—¬ 컴파일 μ‹œκ°„ μƒμˆ˜λ‘œ ν‘œμ‹œν•œλ‹€. μ΄λŸ¬ν•œ 속성은 λ‹€μŒ μš”κ΅¬μ‚¬ν•­μ„ μΆ©μ‘±ν•΄μ•Όν•œλ‹€: 

  • It must be a top-level property, or a member of an object declaration or a companion object.
  • It must be initialized with a value of type String or a primitive type
  • It cannot be a custom getter
  • λ°˜λ“œμ‹œ μ΅œμƒμœ„ 레벨의 속성이어야 ν•˜κ±°λ‚˜ 객체 μ„ μ–Έμ˜ ν˜Ήμ€ companion 객체의 λ©€λ²„μ—¬μ•„ν•œλ‹€. 
  • String νƒ€μž…μ΄λ‚˜ κΈ°λ³Έ νƒ€μž…μ˜ κ°’μœΌλ‘œ μ΄ˆκΈ°ν™” λ˜μ–΄μ•Όλ§Œ ν•œλ‹€.
  • μ‚¬μš©μž 지정 getter일 수 μ—†λ‹€.

The compiler will inline usages of the constant, replacing the reference to the constant with its actual value. However, the field will not be removed and therefore can be interacted with using reflection.

μ»΄νŒŒμΌλŸ¬λŠ” μƒμˆ˜μ˜ μ‚¬μš©μ„ μΈλΌμΈν•˜μ—¬ μƒμˆ˜μ— λŒ€ν•œ μ°Έμ‘°λ₯Ό μ‹€μ œ κ°’μœΌλ‘œ λ°”κΎΌλ‹€. ν•˜μ§€λ§Œ ν•„λ“œλŠ” μ œκ±°λ˜μ§€ μ•Šμ„ 것이고 κ·Έλ ‡κΈ° λ•Œλ¬Έμ— reflection을 μ‚¬μš©ν•˜μ—¬ μƒν˜Έμž‘μš©ν•  수 μžˆλ‹€. 

Such properties can also be used in annotations:

κ·ΈλŸ¬ν•œ 속성듀은 μ–΄λ…Έν…Œμ΄μ…˜μ—μ„œλ„ μ‚¬μš©ν•  수 μžˆλ‹€: 

const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"

@Deprecated(SUBSYSTEM_DEPRECATED) fun foo() { ... }
 
 
 

Late-initialized properties and variables

Normally, properties declared as having a non-null type must be initialized in the constructor. However, it is often the case that doing so is not convenient. For example, properties can be initialized through dependency injection, or in the setup method of a unit test. In these cases, you cannot supply a non-null initializer in the constructor, but you still want to avoid null checks when referencing the property inside the body of a class.

λŒ€κ°œ 속성듀은 μƒμ„±μžμ—μ„œ μ΄ˆκΈ°ν™”λ˜λŠ” non-null νƒ€μž…μœΌλ‘œ μ„ μ–Έλœλ‹€. ν•˜μ§€λ§Œ 이것이 그닀지 νŽΈν•˜μ§€ μ•Šμ€ κ²½μš°κ°€ μ’…μ’… μžˆλ‹€. 예λ₯Ό λ“€λ©΄, 속성듀은 dependency injectionμ΄λ‚˜ μœ λ‹› ν…ŒμŠ€νŠΈμ˜ setup λ©”μ„œλ“œ λ‚΄μ—μ„œ μ΄ˆκΈ°ν™”λ  수 μžˆλ‹€. μ΄λŸ¬ν•œ κ²½μš°λ“€μ—λŠ” μƒμ„±μžμ—μ„œ non-null μ΄ˆκΈ°ν™”λ₯Ό μ œκ³΅ν•  수 μ—†μ§€λ§Œ 클래슀 λ°”λ”” λ‚΄μ—μ„œ 속성을 μ°Έμ‘°ν•  λ•Œ null 체크λ₯Ό ν”Όν•˜κ³  싢을 것이닀. 

To handle such cases, you can mark the property with the lateinit modifier:

μ΄λŸ¬ν•œ κ²½μš°λ“€μ„ μ²˜λ¦¬ν•˜κΈ° μœ„ν•΄μ„œ ν•΄λ‹Ή 속성을 lateinit modifier둜 ν‘œμ‹œν•  수 μžˆλ‹€:

public class MyTest {
    lateinit var subject: TestSubject

    @SetUp fun setup() {
        subject = TestSubject()
    }

    @Test fun test() {
        subject.method()  // dereference directly
    }
}
 

This modifier can be used on var properties declared inside the body of a class (not in the primary constructor, and only when the property does not have a custom getter or setter), as well as for top-level properties and local variables. The type of the property or variable must be non-null, and it must not be a primitive type.

이 modifierλŠ” 클래슀의 λ°”λ”” 내뢀에(κΈ°λ³Έ μƒμ„±μž λ‚΄λΆ€κ°€ μ•„λ‹ˆκ³  속성이 μ‚¬μš©μž μ„ μ–Έ getterλ‚˜ setterκ°€ μ—†λŠ” κ²½μš°μ—λ§Œ) μ„ μ–Έλœ var 속성과 μ΅œμƒμœ„ 속성, 지역 λ³€μˆ˜μ— μ‚¬μš©λ  수 μžˆλ‹€. μ†μ„±μ΄λ‚˜ λ³€μˆ˜μ˜ νƒ€μž…μ€ non-nullμ΄μ–΄μ•Όν•˜κ³  κΈ°λ³Ένƒ€μž…μ΄ μ•„λ‹ˆμ–΄μ•Ό ν•œλ‹€. 

Accessing a lateinit property before it has been initialized throws a special exception that clearly identifies the property being accessed and the fact that it hasn't been initialized.

lateinit 속성에 μ΄ˆκΈ°ν™” 되기 전에 μ ‘κ·Όν•˜λŠ” 것은 μ ‘κ·Ό 쀑인 속성과 μ΄ˆκΈ°ν™”λ˜μ§€ μ•Šμ•˜λ‹€λŠ” 사싀을 λͺ…ν™•ν•˜κ²Œ μ‹λ³„ν•˜λŠ” νŠΉλ³„ν•œ μ˜ˆμ™Έλ₯Ό λ˜μ§„λ‹€. 

 

Checking whether a lateinit var is initialized

To check whether a lateinit var has already been initialized, use .isInitialized on the reference to that property:

lateinit var이 이미 μ΄ˆκΈ°ν™” λ˜μ—ˆλŠ”μ§€ ν™•μΈν•˜κΈ° μœ„ν•΄ ν•΄λ‹Ή 속성에 λŒ€ν•œ μ°Έμ‘°μ—μ„œ .isInitialized μ‚¬μš©ν•˜λΌ: 

if (foo::bar.isInitialized) {
    println(foo.bar)
}
 

This check is only available for properties that are lexically accessible when declared in the same type, in one of the outer types, or at top level in the same file.

πŸ‘€μ΄λŸ¬ν•œ 확인은 λ™μΌν•œ νƒ€μž…, μ™ΈλΆ€ νƒ€μž… 쀑 ν•˜λ‚˜ λ˜λŠ” 같은 파일의 μ΅œμƒμœ„ λ ˆλ²¨μ—μ„œ μ„ μ–Έ 될 λ•Œ μ–΄νœ˜μ μœΌλ‘œ μ ‘κ·Ό κ°€λŠ₯ν•œ 속성에 λŒ€ν•΄μ„œλ§Œ κ°€λŠ₯ν•˜λ‹€. 

 

Overriding properties

See Overriding properties

 

πŸ‘€Delegated properties

The most common kind of property simply reads from (and maybe writes to) a backing field, but custom getters and setters allow you to use properties so one can implement any sort of behavior of a property. Somewhere in between the simplicity of the first kind and variety of the second, there are common patterns for what properties can do. A few examples: lazy values, reading from a map by a given key, accessing a database, notifying a listener on access.

πŸ‘€κ°€μž₯ 일반적인 μ’…λ₯˜μ˜ 속성은 λ‹¨μˆœνžˆ μ½κ±°λ‚˜ ν˜Ήμ€ μ“Έ 수 μžˆμ§€λ§Œ μ‚¬μš©μž 지정 getter와 setterλ₯Ό μ‚¬μš©ν•˜λ©΄ 속성을 μ‚¬μš©ν•  수 있기 λ•Œλ¬Έμ— μ†μ„±μ˜ λͺ¨λ“  μ’…λ₯˜μ˜ λ™μž‘λ„ κ΅¬ν˜„ν•  수 있게 ν•΄μ€€λ‹€. 첫 번째 μ’…λ₯˜μ˜ λ‹¨μˆœν•¨κ³Ό 두 번째 μ’…λ₯˜μ˜ λ‹€μ–‘μ„± 사이에 μ–΄λ”˜κ°€μ— 속성이 μˆ˜ν–‰ν•  수 μžˆλŠ” μž‘μ—…μ— λŒ€ν•œ 곡톡 νŒ¨ν„΄μ΄ μžˆλ‹€. λͺ‡ 가지 μ˜ˆμ‹œ: lazy values, 주어진 key둜 mapμ—μ„œ μ½μ–΄μ˜€κΈ°, λ°μ΄ν„°λ² μ΄μŠ€μ— μ ‘κ·Όν•˜κΈ°, μ ‘κ·Ό μ‹œ λ¦¬μŠ€λ„ˆμ— μ•Œλ¦Ό. 

Such common behaviors can be implemented as libraries using delegated properties.

μ΄λŸ¬ν•œ λ³΄ν†΅μ˜ λ™μž‘λ“€μ€ delegated 속성을 μ‚¬μš©ν•œ λΌμ΄λΈŒλŸ¬λ¦¬λ“€μ„ 톡해 κ΅¬ν˜„λ  수 μžˆλ‹€. 

'Android & Kotlin' μΉ΄ν…Œκ³ λ¦¬μ˜ λ‹€λ₯Έ κΈ€

[λ‚΄λ³΄λ‚΄λ²ˆ] Interfaces | Kotlin  (0) 2022.03.03
[λ‚΄λ³΄λ‚΄λ²ˆ] Classes | Kotlin  (0) 2022.03.03
[λ‚΄λ³΄λ‚΄λ²ˆ] Inheritance | Kotlin  (0) 2022.03.02
Overloading / Overriding  (0) 2022.03.02
Lesson 6: App Architecture (Persistence)  (0) 2022.02.23