Getter, Setter
JAVA는 각 필더를 읽고, 값을 할당하기 위해 Getter, Setter를 설정해야 한다.
하지만 Kotlin에서는 각 프로퍼티에서 Getter, Setter가 자동으로 만들어진다.
이처럼 .변수명 을 사용하여 값을 할당하거나 읽어오면 된다.
이 때, val로 선언한 id 의 경우 값을 변경할 수 없으므로 Getter만 사용이 가능하게 된다.
기본으로 생성해주는 게터, 세터 말고 사용자가 직접 지정할 수 있다.
var 프로퍼티 이름[: 프로퍼티 자료형] [= 프로퍼티 초기화]
[get() {게터 본문}]
[set(value) {세터 본문}]
val 프로퍼티 이름[: 프로퍼티 자료형] [= 프로퍼티 초기화]
[get() {게터 본문}]
val은 위에서 설명한 것과 마찬가지로 변경이 불가능하므로 세터 함수는 따로 지정이 불가능 하다.
게터, 세터를 선언할 때 field 변수가 있는데. 이것을 보조 필드라고도 한다.
보조 필드
각 프로퍼티 값을 읽는 특별한 식별자. get() = field 하면 해당 프로퍼티를 읽어오게 되지만, get() = age 와 같이 사용하면 해당 프로퍼티의 get()이 다시 호출되는 것과 같으므로 무한 재귀 호출에 빠져 스택 오버플로우 오류를 발생할 수 있다.
따라서 이러한 오류를 막기 위해 임시적인 보조 필드인 field를 사용한다.
보조 프로퍼티
보조 필드를 사용하지 않는 경우 임시적으로 사용할 프로퍼티를 선언해 놓고 게터와 세터에서 사용이 가능하다.
프로퍼티의 오버라이딩
프로퍼티는 기본적으로 오버라이딩이 불가능한 final 형태로 선언된다. 따라서 오버라이딩을 가능하게 하려면 open 키워드를 사용해 프로퍼티를 선언해야 한다.
프로퍼티를 오버라이딩 해서 게터와 세터를 각 클래스 다르게 커스터마이징을 할 수 있다.
지연 초기화
lateinit 키워드
lateinit 키워드를 사용하면 프로퍼티에 값이 바로 할당되지 않아도 컴파일러에서 허용한다. lateinit 키워드는 다음과 같은 제한을 가지고 있다.
- 실행할 때 까지 값이 비어있으면 오류가 발생할 수 있다.
- var로 선언된 프로퍼티만 가능하다.
- 프로퍼티에 대한 게터와 세터를 사용할 수 없다.
lazy 키워드
lateinit는 val을 허용하지 않고 var로 선언해야 했다. 이 경우, 언제든지 값이 변경될 수 있다는 단점이 존재한다.
따라서, 읽기 전용의 val로 선언한 객체나 프로퍼티를 지연 초기화 하기 위해서는 lazy 키워드를 사용하면 된다.
val 타입 프로퍼티인 subject는 flow에서 두 번째 println문을 호출했을 때 초기화 된다.
위의 예시에서 해당 부분을 추가해보면, lazy와 by lazy의 차이점을 알 수 있다.
by lazy는 객체의 위임을 나타내고 lazy는 변수에 위임된 객체 자체를 나타내므로, 해당 객체의 값을 사용하기 위해서 lazy는 한 단계 더 거쳐야 한다.
by lazy의 경우 객체.프로퍼티 를 통해 사용이 가능하지만, lazy의 경우 객체.value.프로퍼티 와 같이 사용해야 한다.
lazy 모드
lazy 모드는 3가지가 존재하며 각 모드마다 특성이 존재해 필요에 맞춰서 모드를 설정해 주어야 한다.
- SYNCHRONIZED : default 값. lock을 사용해 단일 스레드만이 사용하는 것을 보장
- PUBLICATION : 여러 군데에서 호출될 수 있으나 처음 초기화된 후 반환 값을 사용
- NONE : lock을 사용하지 않기 때문에 빠르지만 다중 스레드의 접근 위험이 있음. 즉, 값의 일관성을 보장할 수 없다.
이처럼 lazy 옆에 괄호를 통해 모드를 설정해 주면 된다. 위의 예시는 by lazy이지만, lazy 옆에도 사용이 가능하다.
by 키워드
특정 클래스를 확장하거나 이용할 수 있도록 by 키워드를 사용해 위임을 할 수 있다.
by 키워드를 사용하면 하나의 클래스가 다른 클래스에 위임하도록 선언하여 위임된 클래스가 가지는 멤버를 참조 없이 호출 할 수 있게 된다.
프로퍼티 위임이란, 프로퍼티의 게터와 세터를 특정 객체에 위임하고 그 객체가 값을 읽거나 쓸 때 수행하도록 만드는 것.
<val | var | class> 프로퍼티 혹은 클래스 이름: 자료형 by 위임자
위와 같이 by를 통해 위임을 하게 되면, go() 메서드를 생성된 위임자에 맞춰서 호출할 수 있다.
SportImpl이 아닌 다른 하나의 클래스를 추가하여 go() 메서드를 다르게 override 하고, 해당 클래스로 객체를 생성한다면 새롭게 생성한 클래스에서 override 한 go() 메서드가 호출될 것이다.
observable() 함수와 vetoable() 함수의 위임
- observable() : 프로퍼티를 감시하고 있다가 특정 코드의 로직에서 변경이 일어날 때 호출되어 처리. 콜백이라고도 한다.
- vetoable() : observable과 비슷하지만 반환값에 따라 프로퍼티 변경을 허용하거나 취소할 수 있다.
observable을 통해 초깃값을 default로 설정해준다.
그 후, name 변수 값이 변경될 때마다 해당 이벤트가 수행된다.
vetoable은 조건에 따라 값을 갱신할 수 있다. 위의 예제는 new > old 라는 조건을 만족하면 max 값을 new 값으로 갱신하고, 만족하지 못하면 값을 거부하게 된다.
컴패니언 객체
Kotlin에서는 static 키워드가 없는 대신 컴패니언 객체를 제공한다.
컴패니언 객체는 실제 객체의 싱글톤으로 정의된다.
@JvmStatic 어노테이션을 선언하게 되면 JAVA 에서 Kotlin의 컴패니언 객체를 접근할 때 companion 키워드를 생략하고 사용할 수 있다.
object 선언
object로 선언되는 경우 프로퍼티와 메서드를 객체 생성 없이 . 표기법으로 바로 사용이 가능하다.
object 선언 방식을 사용하면 접근 시점에 객체가 생성된다. 따라서 생성자 호출을 하지 않기 때문에 주/부 생성자를 사용할 수 없다.
object 표현식
object 표현식은 object 선언과 달리 이름이 없으며 싱글톤이 아니다.
object 표현식이 사용될 때 마다 새로운 인스턴스가 생성된다. 결과적으로 익명 내부 클래스로 불리는 형태를 object 표현식으로 만들 수 있다.
'Language > Kotlin' 카테고리의 다른 글
[Kotlin] 7장. 다양한 클래스와 인터페이스 2 - 데이터 클래스와 연산자 오버로딩 (0) | 2020.07.06 |
---|---|
[Kotlin] 7장. 다양한 클래스와 인터페이스 1 - 추상 클래스와 인터페이스 (0) | 2020.07.03 |
[Kotlin] 5장. 클래스와 객체 (0) | 2020.06.29 |
[Kotlin] 4장. 프로그램의 흐름 제어 (0) | 2020.06.26 |
[Kotlin] 3장. 함수와 함수형 프로그래밍 3 - 다양한 함수 (0) | 2020.06.25 |