8. Properties and Fields
1. 프로퍼티 선언
자바에서는 프로퍼티를 지원하지 않음 ( getter, setter ) 그래서 lombok을 이용하였다.
코틀린에서는 기본으로 지원해준다.
- 프로퍼티 선언시 구분
- var (mutable)
- val (read-only)
- 프로퍼티 사용은 자바의 필드를 사용하듯 하면 된다.
class Member {
var nickName: String = "SHIPJH"
val name: String = "BAE JAE HYUN"
} // 프로퍼티인지, 필드(멤버변수)인지, 뭔지 알기 힘들다.....
fun copyName(member: Member): Member {
val customer = MEmber()
customer.name = member.name
//...
return customer
}
프로퍼티인지, 필드(멤버변수)인지, 뭔지 알기 힘들다.....
1-1. 프로퍼티 문법
- 전체문법
var <propertyName>[: <PropertyType>] [=<property_initializer>]
[<getter>]
[<setter>]
- 옵션 (생략가능 [ ]부분 )
- PropertyType
- property_initializer에서 타입을 추론 가능한 경우 생략가능
- property_initializer
- getter
- setter
- PropertyType
생략이 가능하기 때문에, 프로퍼티인지 필드인지 구분하기 조금 어렵다.
fun main(args: Array<String>) {
var obj = Member()
println(obj.name) // JAE HYUN
// 디컴파일하여 본다면, getter가 생성되있는것을 볼 수 있다.
}
class Member {
var name: String = "JAE HYUN"
}
코틀린은 캡슐화에 특화되있다.!
1-2. 프로퍼티 getter setter 재정의.
fun main(args: Array<String>) {
var obj = Member()
println(obj.name)
obj.name = "_JH"
println(obj.name)
}
class Member {
var name: String = "JAE HYUN"
get() { return "BAE $field" } // field 는 this.name이라 생각하면됨.
set(value) { field = value} // value는 받는 인자파라미터라고 생각하면 됨.
}
/*
BAE JAE HYUN
BAE _JH
*/
1-3. var (mutable) 프로퍼티
class Member {
// default로 getter setter가 생김
// 타입은 Int
var initialValue = 1
// error 발생
// default로 getter와 setter에 명시적인 초기화를 해주어야 한다.
var initialValue1: Int?
}
1-4. val (read-only) 프로퍼티
val은 read-only 이기 때문에 값을 재정의할 수 없다. 때문에 setter가 없다.
class Member {
// default로 getter setter가 생김
// 타입은 Int
val initialValue = 1
// error 발생
// default로 getter에 경우 명시적인 초기화를 해주어야 한다.
val initialValue1: Int?
}
1-5. 프로퍼티 접근제한자(가시성)
var name: String = "BAE"
private set // body없이 작성해도 된다. private 이기 때문에.
var name2: Any? = null
@Inject set // 어노테이션도 마찬가지.
2. Fields
2-1. Backing Fields
- 코틀린 클래스는 field를 가질 수 없다.
- field 라는 식별자를 통해 접근할 수 있는 automatic backing filed를 제공한다.
- filed는 프로퍼티의 accessor 에서만 사용가능하다.
var count = 0
set(value) {
if (value >= 0) field = value
}
2-2. Backing Fileds 생성 조건
- accessor(getter, setter) 중 1개라도 기본 구현을 사용하는 경우
- accessor(getter, setter) 재정의하여 filed 식별자를 참조하는 경우.
var count = 0
set(value) {
if (value >= 0) field = value
}
- 아래의 경우는 Backing Fileds 를 생성하지 않음
val isEmpty: Boolean
get() = this.size == 0
2-3. Backing Properties 생성 방법
backing field 방식이 맞지 않는 경우에는 Backing Properties를 이용할 수도 있다.
private var _table: Map<String, Int>? = null
public val table: MAp<String, Int>
get() {
if (_table == null) {
_table = HashMap()
}
return _table ?: throw Exception(....)
}
2-4. const ( Compile-Time Constants ) 생성 조건
- 상수의 개념이며, 어노테이션에서도 사용이 가능하다.
- 조건
- Top - level
- String이나 프리미티브 타입으로 초기화된 경우에 사용가능
const val STRING_DASH: String = "-"
@Deprecated(STRING_DASH)
fun foo() {...}
2-5. Late-Initialized Properties
- 일반적으로 프로퍼티는 non-null 타입으로 선언이된다.
하지만, 간혹 non-null타입 프로퍼티를 사용하고 싶지만, 생성자에서 초기화를 해줄 수 없는 경우가 생긴다
- Dependency injection
- Butter knife ( view 부분 사용할때.. 미리 값을 할당하지 않을때. )
- Unit test의 setup 메소드
public class MyTest {
// 실제로 컴파일하려면 ?를 써서 nullalbe을 써서 null로 초기화 해야된다.
// 그렇게 되면 모든곳에서 null체크를 해야됨..
lateinit var subject: TestSubject
// 때문에 setup에서 값할당.
@Setup fun setup() {
subject = TestSubject()
}
@Test fun test() {
subject.method()
}
}
lateinit 를 사용하여 나중에 할당한다는것 알려주면 된다.
- 사용 조건
- 클래스의 바디에서 선언된 프로퍼티만 가능
- 기본생성자에서 선언된 프로퍼티는 안됨
- var 프로퍼티만 가능
- non-null 타입이어야 함
- 프리미티브 타입이면 안됨
- lateinit 프로퍼티가 초기화 되기 전에 접근하면 오류발생
- custom accessor(getter, setter 재정의) 가 있으면 안됨.
class Member {
lateinit var name: String // 클래스 바디에 선언되어야 함
fun setUp() {
name = "late"
} // name에 lateinit을 안써주면 에러가 발생함. 이닛블록이 아니여서.
}
class Member2(lateinit var name: String ) { // 생성자에 선언된 프로퍼티 안됨
//...
}
class Member3 {
lateinit val name: String // val은 안됨. var만 가능
//...
}
class Member4 {
lateinit var name: String
gert() {return field} // custom accessor(getter, setter 재정의) 가 있으면 안됨.
...
}
class Member5 {
lateinit val name: Int // 프리미티브 타입이면 안됨
//...
}
class Member6 {
lateinit val name: String? // nullable은 안됨. non-null 타입이어야함.
//...
}