일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- Class
- 크롤러
- kotlin
- 함수
- Collection
- Yocto
- set
- animation
- 웹크롤러
- 플러터
- pushnamed
- 콜렉션
- 다트
- DART
- python
- map
- 파이썬
- 코틀린
- Android
- import
- text
- textstyle
- function
- Flutter
- variable
- package
- 클래스
- crawler
- List
- ML
- Today
- Total
조용한 담장
코틀린(Kotlin) 클래스(Class) : 프로퍼티 와 필드 (Properties and Fields) 본문
코틀린 클래스의 프로퍼티(property) 와 필드(field) 에 대해 살펴보자.
원문 https://kotlinlang.org/docs/reference/properties.html 을 보며 정리.
Declaring Properties
property 는 var 또는 var 로 선언된다.
class Address {
var name: String = "Holmes, Sherlock"
var street: String = "Baker"
var city: String = "London"
var state: String? = null
var zip: String = "123456"
}
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
property 선언 문법:
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
property_initializer 는 property 의 데이터를 초기화 하기 위한 것이다.
getter, setter 는 property 의 데이터 얻거나 변경하기 위해 사용되며 접근자(accessor) 라 한다.
initializer, getter, setter 는 생략이 가능하며, 암묵적으로 기본적인 기능이 구현된다.
initializer 로 부터 property 의 타입이 추론이 가능한 경우에는 type 을 생략할 수 있다.
var allByDefault: Int? // 명시적으로 `initializer` 가 필요한 경우, default getter and setter implied
var initialized = 1 // has type Int, default getter and setter
var initialized 의 타입은 initializer 값 1 로 부터 타입 Int 가 추론되어 지정된다.
property 에 접근하기 위해 사용되는 getter 는 기본 할당되는 코드 말고 직접 구현해서 쓸 수도 있다.
val isEmpty: Boolean
get() = this.size == 0
setter 도 직접 구현할 수 있다.
var stringRepresentation: String
get() = this.toString()
set(value) {
setDataFromString(value) // parses the string and assigns values to other properties
}
getter 를 통해 property 의 타입이 추론이 가능한 경우에도 type 선언을 생략할 수 있다.
val isEmpty get() = this.size == 0 // has type Boolean
isEmpty 의 데이터를 접근하면 항상 this.size == 0 의 값을 가지므로 boolean 타입으로 추론된다.
접근자(accessor)의 코드 변경없이 visibility 를 설정하거나 annotate 할 수 있다.
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
kotlin 과 Java 의 클래스 내부 변수에 대한 차이점에 대한 내용이다. 문서의 설명을 먼저 보면...
kotlin 클래스 에서는 field 를 바로 선언할 수 없고 property 가 backing field 가 필요하면 알아서 제공한다. backing field 는 접근자 안에서 field 로 참조할 수 있다.
코드로 이해해 보자.
class A {
var counter = 0 // Note: the initializer assigns the backing field directly
set(value) {
if (value >= 0) field = value
}
}
fun main() {
var a = A()
a.counter = 1
println(a.counter)
}
위 예제는 property counter 를 생성하고 initializer 로 초기값을 선언하고 접근자 set 를 구현하여 저장하는 코드를 작성했다.
a.counter = 1 에서 set 함수가 호출되어 값을 변경하게 될 것이다.
이를 자바로 변환해보면,
public final class A {
private int counter;
public final int getCounter() {
return this.counter;
}
public final void setCounter(int value) {
if (value >= 0) {
this.counter = value;
}
}
}
public static final void main() {
A a = new A();
a.setCounter(1);
int var1 = a.getCounter();
boolean var2 = false;
System.out.println(var1);
}
Java 코드에서는 class A 내에 counter field 가 생성되어 있고 두개의 접근자 함수가 따로 생성되었다.
즉, kotlin 의 property 의 값 저장이 Java 의 field 를 통해 이루어 지는 것이다.
하지만 kotlin 코드에서 이 field 를 직접 설정하는 것이 아니라 property 를 선언하면 아래의 상황에 따라서 자동으로 backing field 를 만들어 준다.
- field 를 통해 custom 생성자에서 값을 참조하는 경우
- 적어도 하나의 생성자가 기본 생성자를 사용하는 경우
위 상황에 해당이 안되는 경우, 생성자를 모두 따로 선언하고 field 를 사용하지 않는 다면 backing field 를 생성하지 않는다.
class A {
var size = 0
val isEmpty: Boolean
get() = this.size == 0
}
public final class A {
private int size;
public final int getSize() {
return this.size;
}
public final void setSize(int var1) {
this.size = var1;
}
public final boolean isEmpty() {
return this.size == 0;
}
}
isEmpty property 는 실제로 메모리에 아무값도 저장을 하지 않는, 상태만 리턴하는 getter 만 존재하는 코드여서 field 가 생성되지 않는다.
참조 : What's Kotlin Backing Field For?
결론은 kotlin 이 알아서 하는 일이니 큰 신경쓸 필요가 없다...
Backing Properties
궂이 자동으로 backing field 를 만들어주는 것을 피해 무엇인가를 해보고자 한다면 backing property 를 만들 수도 있다.
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")
}
Compile-Time Constants
compile time 에 값을 알 수 있는 property 는 const 를 써서 compile time constant 로 선언할 수 있다.
아래의 조건을 만족해야 한다.
- Top-level, or member of an object declaration or a companion object.
- Initialized with a value of type String or a primitive type
- No custom getter
annotation 에도 사용될 수 있다.
const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"
@Deprecated(SUBSYSTEM_DEPRECATED) fun foo() { ... }
Late-Initialized Properties and Variables
보통의 property 는 constructor 에서 null 이 아닌 어떤 값으로 초기화 되어야 한다.
바로 초기화를 하지 않고 특정 조건이 만족된 후에 초기화를 하고싶은 경우에는 lateinit 를 사용할 수 있다.
public class MyTest {
lateinit var subject: TestSubject
@SetUp fun setup() {
subject = TestSubject()
}
@Test fun test() {
subject.method() // dereference directly
}
}
lateinit 은 클래스의 body 안에서 var property 에 사용될 수 있고, top-level properties 와 지역 변수들에 쓸 수 있다.
property 의 타입이나 변수는 null 이 될 수 없고 primitive type 이 아니어야 한다.
primary constructor 안에서는 사용할 수 없으며 custom 생성자를 쓰지 않는 경우에만 사용할 수 있다.
Checking whether a lateinit var is initialized (since 1.2)
lateinit var 의 초기화 여부를 확인하려면 property 의 레퍼런스의 .isInitialized 를 사용한다.
if (foo::bar.isInitialized) {
println(foo.bar)
}
'kotlin' 카테고리의 다른 글
코틀린(Kotlin) 클래스(Class) : Visibility Modifiers (0) | 2019.12.31 |
---|---|
코틀린(kotlin) 클래스(Class) : 인터페이스 (Interfaces) (0) | 2019.12.31 |
코틀린(Kotlin) : 리턴 과 점프 (Returns and Jumps) (0) | 2019.12.31 |
[코틀린] Kotlin : Control Flow (0) | 2019.12.31 |
[코틀린] Kotlin : packages (0) | 2019.12.31 |