반응형

자바에 비해 코틀린에서 프로퍼티를 선언하고 초기화하는 것은 조금 까다롭습니다.

때문에 코틀린에 익숙하지 않은 분들은 클래스 내 프로퍼티 선언 및 초기화 단계에서부터 안드로이드 스튜디오 등의 IDE가 빨간 줄을 띄우는 것을 자주 경험해 보셨을 겁니다.

 

하지만 결코 어려운 내용이 아니기 때문에 한 번 잘 정리한다면 앞으로 빨간 줄을 마주하게 될 확률을 줄이실 수 있을 겁니다.

 

var / val

코틀린의 클래스 내 속성(property)를 선언할 때는 varval 키워드를 사용할 수 있습니다.

여기서 var은 변경할 수 있는 속성(mutable), val은 변경할 수 없는 속성(immutable)을 뜻합니다.

자바로 치면 일반 변수 선언에 해당하는 것이 var 키워드, final 키워드를 통해 불변 값으로 만드는 것이 val 키워드라고 보시면 되겠습니다.

 

예제를 살펴보겠습니다.

class Person {
    var name: String = "Ready Kim"
    val age: Int = 26
}

 

Person이라는 클래스를 만들어 name과 age라는 속성에 대해 각각 var, val로 선언했습니다.

그리고 이에 대해서 main 함수에서 아래와 같이 접근할 수 있습니다.

 

fun main(args: Array<String>) {
    val person = Person()
    person.name = "Kim Ready" // possible
    person.age = 28 // error: val cannot be reassigned
    
    println("name: ${person.name}, age: ${person.age}")
}

 

person.name의 경우 var로 선언했기 때문에 선언된 이후에 외부에서 수정하는 것이 가능합니다. 반면에 age는 val로 선언되었기 때문에 초기화 이후로 값이 변경될 수 없어 컴파일 에러를 띄우게 됩니다.

 

이번엔 조금 코드를 수정해 보겠습니다.

 

class Person {
    var name: String? // error: property must be initialized or be abstract
    val age: Int = 26 // has type Int, default getter and setter
}

 

자, 위와 같은 경우가 많은 분들이 헷갈려하시는 부분이고, 빨간 줄을 많이 경험하게 되는 경우입니다.

특히 자바를 하신 분들이 멤버 변수에 자동으로 null이나 0 등의 초기화 값이 들어갈 것이라 생각하고 Nullable 하게 타입 뒤에 ?를 붙여주면 된다고 생각하지만 var name: String? 부분에서 빨간 줄을 경험하게 됩니다.

그 이유는 주석에 적혀있는 것처럼 코틀린에서 프로퍼티는 반드시 초기화되거나 그렇지 않을 경우 추상화되어야 합니다.

 

프로퍼티에 대해 초기화해주는 방법은 아래처럼 다양한 방법이 있습니다.

 

class Person {
    var name: String = "Ready Kim" // 방법 1
    val age: Int?
    val address: String?

    // 방법 2
    init {
        age = 26
    }

    // 방법 3
    constructor() {
        address = "Seoul"
    }
}

 

방법 1은 선언과 동시에 초기화 해주는 방법입니다.

방법 2는 init 블록을 통해 초기화하는 방법입니다.

방법 3은 constructor를 통해 초기화 하는 방법입니다. 위 예제는 secondary constructor를 통해 초기화되었습니다.

 

이외에도 lateinit을 이용하거나 delegation 등을 통해 뒤늦게 초기화하는 방법도 존재합니다만 이에 대해서는 추후 별도로 포스팅하도록 하겠습니다.

 

이처럼 코틀린에서 클래스 내 속성을 정의하고 선언할 때는 반드시 초기화에 대해 어떻게 처리해줄 것인가에 대한 고민을 하셔야 합니다.

 

Getter & Setter

자바를 경험해보신 분이라면 "클래스 내 멤버 변수는 되도록 private으로 하고, 그에 대한 getter & setter 메소드를 제공하라!"는 말을 들어보셨을 겁니다.

getter & setter 외부에서 직접적으로 참조할 수 없는 프로퍼티(필드)에 대해 간접적으로 접근할 수 있게 해주는 메소드를 의미합니다.

 

문법은 아래와 같습니다.

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

 

대괄호[]는 생략 가능을 의미합니다. 자세히 보시면 프로퍼티 선언 아래 getter과 setter이 생략 가능하다고 나와있습니다.

 

그리고 변수 이름 바로 옆에 있는 [: <PropertyType>]의 경우 뒤에 따라오는 <property_initializer> 또는 <getter>를 통해 타입 추론이 가능한 경우에 생략 가능합니다.

 

코틀린에서 객체의 프로퍼티에 접근은 변수에 직접 접근하는 것이 아니라 Accessor 메소드(getter & setter)를 통해서 이루어집니다.

 

프로퍼티를 선언할 때 별도로 getter & setter 메소드를 구현하지 않으면 해당 속성은 디폴트로 정의된 메소드가 생략되어 있습니다.

이때, var은 변경 가능한 속성이기에 getter & setter 메소드를 가질 수 있고, val의 경우 변경 불가능한 속성이기에 setter 메소드 없이 getter 메소드만 가질 수 있습니다.

 

기본으로 제공되는 getter 함수를 나만의 커스텀 getter 함수로 구현해보겠습니다.

class MyList {
    var size: Int? = 0
    val isEmpty: Boolean
        get() = this.size == 0
}

 

자바를 하시던 분들께는 조금 어색한 문법입니다. getter 메소드의 이름도 따로 붙여주지 않을뿐더러 메소드를 정의할 때 중괄호{}도 쓰지 않고 있으니 어색할 수밖에 없습니다.

 

위 코드를 해석하자면 MyList 객체의 isEmpty라는 속성에 접근할 때에 MyList 객체의 size 속성 값이 0일 경우 true를 리턴, 아닐 경우 false를 리턴하는 코드입니다.

즉, 위 코드만 놓고 봤을 때는 사실상 getter의 결과와 isEmpty의 실제 값이 상관이 없게 됩니다.

 

이번에는 커스텀 setter 함수를 구현해보겠습니다.

class Person {
    var name: String = "Not Assigned"
        set(value) {
            field = "Dev." + value
        }
}

fun main(args: Array<String>) {
    val person = Person()
    person.name = "Ready"

    println(person.name)
}
Dev.Ready

 

위 예제를 보시면 getter보다 조금 더 낯선 내용이 등장합니다. 그건 바로 Backing Field라고 불리는 field 입니다.

코틀린에서는 클래스 내에서 직접적으로 Fields에 대해 선언할 수 없으나 프로퍼티가 Backing field를 필요로 할 때 자동으로 Accessor 메소드 안에서 참조할 수 있도록 field라는 식별자를 제공해줍니다.

 

위 코드에서 사용된 field 역시 이러한 Backing Field를 의미하며, field가 가리키는 것이 곧 name이라 보시면 되겠습니다.

조금 더 설명하자면, 아래 main 함수에서 person.name = "Ready"가 실행될 때 person 객체의 name 프로퍼티를 직접 접근하는 것이 아니라 setter 함수를 호출하는 것이고, 그에 따라 set(value)에서 value에 "Ready"라는 값이 전달되어 field(name)에 "Dev.Ready" 값이 할당되게 되어 println()으로 출력했을 때 "Dev.Ready"라는 결과가 나오게 됩니다.

 


이렇게 코틀린에서 클래스 내 프로퍼티에 대한 내용과 getter & setter 함수까지도 살펴보았습니다.

이 부분이 코틀린으로 언어를 시작하신 분과 자바를 숙지하신 채로 코틀린을 공부하시는 분들께 다른 의미로 어렵고 헷갈리게 느껴질 것입니다. 그렇기에 예제를 직접 확장해가면서 다양한 훈련을 해보실 것을 권장합니다. 

반응형
  • Favicon of https://codingdog.tistory.com BlogIcon 코딩강아지 2019.11.13 01:10 신고

    var하고 val하고 꽤 헷갈리겠네요..
    거기에 함수형 ㄷㄷㄷ

    • Favicon of https://readystory.tistory.com BlogIcon Dev. Ready Kim 2019.11.13 01:16 신고

      ㅎㅎ코딩 강아지님도 나중에 기회 되시면 코틀린 해보세요!

반응형