-
[Kotlin] Property란 ( + 자바의 field와 다른점)Kotlin/Property 2021. 7. 13. 19:05
이번에 다뤄볼 내용
코틀린이 property에 접근하는 방식
코틀린 getter,setter선언법
코틀린 backing field의 개념과 field키워드
코틀린 getter,setter선언 방식에 따른 backing field 생성유무
코틀린 property, getter, setter의 visibility modifier
property overriding은 다른 포스팅에 존재
(내용 추가됨)
코틀린의 property는 field, getter, setter 이 세가지를 통칭하는 개념이다.
코틀린의 property의 값을 읽어올때는 getter가 호출되고 값을 쓸땐 setter가 할당된다.
코틀린의 var name: String ="hyun" 코드는 자바에서 처럼 String name="hyun" 과 같이 field만 형성된 것인까?
getter, setter또한 만들어진다.
다음의 property를 정의하는 full syntax를 보자
var <propertyName>[: <PropertyType>] [= <property_initializer>] [<getter>] [<setter>]
각 [ ] 부분은 형태에 따라 생략이 될 수 있는 부분이다.
var name: String ="hyun"으로 선언하면 getter, setter를 생략했기 때문에 자동으로 기본 getter, setter가 만들어진다.
그렇기 때문에 다음과 같은 자바 코드가 된다.
private String name = "hyun"; public String getName() { return name; } public void setName(String name) { this.name = name; }
var변수, val변수
var변수는 getter와 setter모두 가질 수 있지만
val변수는 한번 값을 할당하면 변경할 수 없기 때문에 setter가 필요없게되는 이유로 setter가 존재할 수 없다.
var변수에서 getter,setter를 정의하지 않으면 자동으로 기본 getter, setter가 생성되고
val변수에서 getter를 정의하지 않으면 자동 기본 getter가 생성된다.
값을 propety에 할당할때마다 내부적으로 setter가 호출되고 property를 읽어올때마다 내부적으로 getter가 호출된다.
class Person() { var name: String = "hyun" } fun main() { var person: Person = Person() //필드를 직접 참조한 것이 아니라 내부적으로 getter, setter가 호출된다. println(person.name) //getter호출 person.name = "kim" //setter호출 println(person.name) }
custom getter, setter
동작에 따라 getter, setter를 원하는 형태로 바꿔줘야할 필요가 있다. 그럴때 getter, setter를 입맛에 따라 변경할 수 있다. getter, setter의 재정의하는 것에 따라 backing field가 생성될 수도 있고 안될수도 있는 점을 살펴보자.
getter의 return형은 property선언부의 자료형의 Boolean 을 통해 Boolean을 return해야 한다.
setter의 value는 인자인데 이 또한 property선언부의 자료형인 Boolean인 변수로 취급된다.
isEmpty property의 cutom getter, setter를 선언하였다.
getter의 메서드를 보면 size가 0이면 true 이외에는 false를 리턴하고
setter또한 size가 0이면 true 이외에는 false를 리턴한다.
(예제코드를 대충 편집하다보니 setter에는 value와 전혀관계없이 isEmpty가 정해지게 되었다. ㅎ..)
위의 코틀린 코드를 decomplie해보면 다음과 같이 된다.
field에 isEmpty는 포함되어 있지 않고 size만 생성되어있다.
isEmpty는 field로 생성되있지 않지만 코틀린 main함수의 println을 보면 property의 getter는 사용이 가능함을 볼 수 있다. 물론 setter도 사용가능하다. 필드로 생성되있지 않은데 getter,setter를 사용할 수 있다는 점을 주목하자.
decompile된 코드에서 자바 field를 코틀린에서는 backing field라고 부른다. setter, getter의 custom형태에 따라 이 backing field가 생성될 수도 있고 생성되지 않을 수도 있다.
추가적으로 isEmpty는 backing field로 생성되지 않기 때문에 인스턴스가 만들어질때 초기화를 하면 컴파일 에러가 발생한다.
backing field라는 개념을 이해하기 전이라면 초기화를 했는데 왜 에러가 일어나지라고 생각할 수도 있을 것이다.
그렇다면 backing field는 어느경우 만들어지고 어느경우에는 만들어지지 않는 것일까??
backing field의 생성조건
1. accessor중 하나라도 기본구현을 하는 경우( 생략된 getter,setter가 하나라도 있는 경우)
2. cutom accessor내부에서 사용할 수 있는 field키워드를 사용한 경우
생성조건에 field키워드라고 나와있다.
field는 backing field를 대체하는 개념이다.
코틀린에서 field키워드를 제공하는 이유를 보자
class User { var name: String get() = name set(value) { name = value } } // 출처: https://zerodice0.tistory.com/112 [검은곰의 아카이브]
setter의 value라는 값이 있다.
setter의 String 파라메터인데 String형인 name 프로퍼티로부터 String형으로 유추하여 파라메터가 지정되는 것이다.
setter내부에서 name=value를 호출하고 있다. 코틀린에서 property에 값을 할당하면 내부적으로 setter가 호출된다고 하였다. setter내부에서 setter를 호출하므로 recursive한 호출로 무한반복에 빠지게 된다.
다음과 같은식으로 말이다.
무한반복을 막기위해 field키워드를 제공한다.
field키워드를 사용하여 이전의 코드를 바꿔보면
class User { var name: String = "" get() = name set(value) { field = value } }
다음과 같은 식으로 무한반복을 방지하였다.
그러나 setter에서 field키워드를 사용하지만 field=name 대신 name= field로 하면 똑같이 무한반복에 빠지게 된다.
밑에서 설명할 내용이였지만 지금 field키워드를 getter,setter내부에서 하나라도 field키워드를 사용하여서 backing field로 name이 생성된다.
backing field생성조건중 2번에 해당하는 getter,setter에서 field키워드를 사용한 경우 예는 위의 field키워드가 필요한 이유에서 봤으니 1번 조건 예를 한번 보자.
1. accessor중 하나라도 기본구현을 하는 경우( 하나라도 생략된 생성자가 있는 경우)
생략된 생성자가 하나라도 있을 경우 backing field를 만들어준다.
맨위의 custom getter,setter의 형태에 따라 field의 생성 유무가 달라진다는 소개부분에서 사용한 예제를 다시 가져와봤다.
custom getter, setter를 모두 만들어 생략된 생성자가 없지만 getter,setter모두에서 field키워드를 사용하지 않아 isEmpty backing field는 생성되지 않았다.
코틀린 공식문서의 내용이다. getter의 return형 타입추론으로 인해 property타입을 생략할 수 있다.
backing field를 이해하지 못한 상태에서 이해를 하지 못했던 부분이다.
val변수는 setter를 가질 수 없고 custom getter구현을 했다. 기본구현 생성자가 없다. 1번조건을 체크했으니 2번조건 field키워드 유무 체크를 하면 field키워드가 없어 backing field가 생성되지 않는다.
그렇기 때문에 위의 코틀린 코드는 프로퍼티 값 초기화가 아닌 getter선언에 타입이 자동 추론된 것이다.
getter는 this.size==0식이고 this.size==0의 결과가 Boolean이므로 getter는 return형이 Boolean이라는 getter선언이다.
isEmpty2는 Boolean타입의 프로퍼티가 된다.
즉 초기화가 아닌 타입 자동 추론문이다. get() =this.zie ==0는 property 선언줄 말고 그 다음줄에 써줄 수 있다.
표현식 대신 return 키워드를 사용했더니 타입 자동추론이 안되고 컴파일 에러가 일어나는데 이유는 아직 확실히 모르겠다. 아마도 return문으로는 타입추론이 불가능하고 expression인 표현식으로 = 써줘야하는 것 같다.
property를 사용할 때는 실제로 getter, setter가 호출되는 것이고
custom geter, setter의 정의형태에 따라 backing field가 만들어지는지 안만들어지는지에 대해서 공부했다.
이제 getter,setter,property의 visibility modifier를 알아보자.
https://stackoverflow.com/questions/37906607/getters-and-setters-in-kotlin
테스트는 코틀린 코드를 decompile해서 각자 확인해보고 개념만 설명하겠다.
코틀린에서 visibility modifier를 생략하면 class, function, property는 기본으로 public으로 지정된다.
property를 overriding시킬수 있다고 한다
모든 property는 final이 default값이다. 자바의 final을 말하는 것이 아니다. final클래스처럼 다른 클래스의 조상이 될 수 없는 final의 개념에 해당된다.. .property를 자손쪽에 오버라이딩이 가능하게 만드려면 open키워드로 선언하자.
(추가)
https://hellose7.tistory.com/30?category=1205050
property, getter, setter에 상관없이 backing field는 무조건 private로 생성된다.
코틀린에서는 무조건 field로의 접근은 getter,setter로 하기 때문에 private로 생성되는 것 같다.
custom getter, setter를 선언하지 않은 경우에 getter,setter메서드확인해보니 property의 visibility와 똑같이 설정되었다.
getter는 무조건 property의 visibility modifier를 따른다. (= getter를 제한자는 설정할 필요가 x)
var name:String="" //public -> getter는 무조건 property의 제한자를 따른다.
//이때 custom getter에 public을 붙이는 것은 의미가 없고, public 이외의 것을 붙이면 에러가 난다.
var name:String=""
public get(){........ //getter 제한자 설정 무의미
setter는 property의 visibility modifier보다 넓게 지정할 수 없다( 같거나 좁게 설정 )
custom getter, setter를 설정하지 않으면 자동으로 생성되는 getter,setter는 모두 property의 제한자로 자바 메서드가 생성되고
getter는 항상 property의 제한자를 따르므로 제한자 설정이 불필요하다.
setter는 property의 제한자보다 같거나 좁은 범위로 사용가능하다.
setter가 private로 설정된 경우 private인 setter메서드가 되므로 setter메서드는 자기 자신 클래스 내부에서 밖에 사용되지 못한다.
더 알아볼 것
Backing Properties