
코틀린을 처음 시작하면 프로퍼티라는 용어가 굉장히 헷갈릴 수 있다.
그래서 프로퍼티에 대한 용어 정리를 한 번 해보려고 한다.
프로퍼티
Kotlin In Action의 말을 빌리자면
코틀린 프로퍼티는 자바의 필드와 접근자 메서드(getter, setter)를 완전히 대신한다.
오해하지 말자.
이 말은 필드와 접근자 메서드를 모두 합친게 프로퍼티라는 말이 아니다.
프로퍼티는 필드의 역할도, 접근자 메서드의 역할도 할 수 있구나! 라고 받아들이면 된다.
+ 코틀린에선 자바의 필드를 뒷받침하는 필드(backing field) 라고 부른다.
코틀린을 처음 배울 때 가장 먼저 접하는 프로퍼티는 아래와 같은 형태일 것이다.
val name: String = "빅스"
위 name 프로퍼티는 뒷받침하는 필드와 접근자 메서드가 모두 포함되어있다.
자바로 본다면 이런 형태가 될 것이다.
private final String name = "빅스";
public final String getName() {
return this.name;
}
name 프로퍼티를 읽기 전용인 val로 선언했기 때문에 setter는 존재하지 않고 필드 또한 final로 선언되었다.
getter와 setter가 모두 존재하는 모습을 보고싶다면 var로 선언하면 된다.
// Kotlin
var name: String = "빅스"
// Java
private String name = "빅스";
public final String getName() {
return this.name;
}
public final void setName(String var1) {
this.name = var1;
}
이렇게 코틀린 프로퍼티는 자바의 필드, 접근자 메서드를 완전히 대신한다.
그럼 위 예시를 보고 나서 "그럼 필드는 무조건 있는건가?" 라는 의문을 가질 수 있다.
이제 접근자 메서드만을 대신하는 프로퍼티를 보자!
계산된 프로퍼티
커스텀 접근자를 이용하면, 필드가 없고 접근자 메서드만 존재하는 계산된 프로퍼티를 만들 수 있다.
위에서 봤던 name 프로퍼티에선 기본적인 접근자 메서드를 사용했기 때문에 생략되어 보이지 않았다.
그럼 커스텀 접근자를 통해 접근자를 만나보도록 하자.
class User(private val email: String) {
val nickName: String
get() = email.substringBefore('@')
}
자바로 치면 getter에 상응하는 접근자를 커스텀 했다.
이제 nickName 프로퍼티는 email의 @ 앞쪽만 가져올 것이다.
fun main() {
val user = User("bixx@naver.com")
println(user.nickName)
}

위처럼 사용하면 왼쪽처럼 출력되는 것을 볼 수 있다.
nickName 프로퍼티를 자바로 본다면 이런 형태가 된다.
public final String getNickName() {
return StringsKt.substringBefore$default(this.email, '@', (String)null, 2, (Object)null);
}
자바에 subStringBefore 함수가 없어서 디컴파일 과정에서 알아보기 어렵게 되었다.
하지만 현재 중점은 subStringBefore가 아니므로 보지 않기로 하자.
보면 우린 프로퍼티로 선언했는데 자바에선 함수인 것을 알 수 있다.
사실 생각해보면 backing field가 없고 email 값을 계산해서 가져오므로 함수와 다름없다.
그래서 아래 프로퍼티와 함수는 사실상 거의 차이가 없다.
class User(private val email: String) {
val nickName: String
get() = email.substringBefore('@')
fun nickName(): String {
return email.substringBefore('@')
}
}
그럼 둘 중 뭘 사용해야할까?
Kotlin In Action은 이렇게 말한다.
파라미터가 없는 함수를 정의하는 방식과 커스텀 게터를 정의하는 방식 중 어느 쪽이 더 나은지 궁금한 독자도 있을 것이다. 두 방식 모두 비슷하다. 구현이나 성능상 차이는 없다. 차이가 나는 부분은 가독성뿐이다. 일반적으로 클래스의 특성(프로퍼티에는 특성이라는 뜻이 있다)을 정의하고 싶다면 프로퍼티로 그 특성을 정의해야 한다.
그렇다고 한다.
뭘 사용할지는 각자 알아서 잘 판단하도록 하자.
개인적으로 필자는 책에 나온대로 특성이라고 생각된다면 프로퍼티를 사용한다.
이렇게 헷갈릴 때는 코틀린이 제공하는 api들이 어떤식으로 구현되었는지 살펴보면 좋다.
코틀린 EmptyList의 일부다.
특성이라고 생각되는 size는 커스텀 접근자를 통해 구현하고 있다.

또 다른 예시로 ArrayAsCollection에서도 커스텀 접근자를 통해 size를 정해준다.
(필자도 이번에 처음 보는 클래스로 Array를 Collection으로 바꿔주는 역할인 것 같다. 일단 size에 집중하자.)

사람 클래스를 만들고 생성자로 키와 몸무게를 받는다고 해보자.
이때 키와 몸무게를 이용 bmi를 계산하고자 한다. 이때 함수가 좋을까, 프로퍼티가 좋을까?
(이 예시는 우테코 5기의 '글로'가 했던 고민이다. 글로 땡큐)
class Person(val height: Double, val weight: Double) {
val bmi: Double get() = weight / sqrt(height)
fun getBmi(): Double {
return weight / sqrt(height)
}
}
bmi 수치는 몸무게를 키의 제곱으로 나눈다.
bmi 프로퍼티와 getBmi 함수 중 어떤 것을 사용하고 싶은가?
필자는 이럴 경우 프로퍼티를 선호한다.
정답은 없으니 잘 생각해서 써보자!
스토어드 프로퍼티 ( stored property / 저장된 프로퍼티 )
방금 backing field가 없는 계산된 프로퍼티를 살펴보았다.
그럼 backing field가 있는 프로퍼티는 뭐라고 부를까?
바로 스토어드 프로퍼티다.
이 명칭은 코틀린 공식 문서 어디에도 없지만 통용되는 말이다.
저장된 프로퍼티는 처음 프로퍼티를 설명하면서 이미 예시를 보여줬으므로 넘어가도록 하겠다.
프로퍼티가 자바의 필드, 접근자 메서드를 완전히 대신한다고 했는데
여기서 헷갈릴만한 부분 한 가지만 언급하고 끝내겠다.
값을 외부에 드러내면서 내부에서만 변경하고 싶어
자 그럼 어떻게 해야할까? 우선 자바의 경우 아래처럼 될 것이다.
public class Person {
private String name = "bixx";
public final String getName() {
return this.name;
}
public void changeName(String newName) {
name = newName;
}
}
그럼 코틀린은 어떻게 해야할까?
var로 만들어 변경 가능하도록 하고, setter에 상응하는 접근자 메서드를 private으로 감추면 된다.
class Person() {
var name: String = "bixx"
private set
fun changeName(newName: String) {
name = newName
}
}
혹시 전혀 헷갈리지도 않고 이미 알고 있었고 이딴걸 왜 모르는지 정말 이해가 안된다면 양해를 부탁한다.
나는 처음 공부할 때 생각지도 못했었다..
어차피 이 글은 나 같은 바보들이 볼 것이라고 생각하므로.. 도움이 됐을거라 믿는다!
+ 추가로 확장 프로퍼티라는 것도 존재하지만, 커스텀 접근자의 응용이라고 생각돼어 확장함수 편에서 소개하도록 하겠다. 확장함수에 대한 글을 언제 적을진 모르겠지만~
'Kotlin' 카테고리의 다른 글
[Kotlin] 확장 함수! 신기한 당신의 정체는? (0) | 2023.04.24 |
---|---|
[Kotlin] 인터페이스와 추상클래스의 차이 (1) | 2023.03.22 |
[Kotlin] 코틀린 인터페이스(Interface) (0) | 2023.02.21 |
[Kotlin] 코틀린의 헷갈리는 생성자와 프로퍼티 알아보기 (Java와 비교하여) (0) | 2023.02.20 |

코틀린을 처음 시작하면 프로퍼티라는 용어가 굉장히 헷갈릴 수 있다.
그래서 프로퍼티에 대한 용어 정리를 한 번 해보려고 한다.
프로퍼티
Kotlin In Action의 말을 빌리자면
코틀린 프로퍼티는 자바의 필드와 접근자 메서드(getter, setter)를 완전히 대신한다.
오해하지 말자.
이 말은 필드와 접근자 메서드를 모두 합친게 프로퍼티라는 말이 아니다.
프로퍼티는 필드의 역할도, 접근자 메서드의 역할도 할 수 있구나! 라고 받아들이면 된다.
+ 코틀린에선 자바의 필드를 뒷받침하는 필드(backing field) 라고 부른다.
코틀린을 처음 배울 때 가장 먼저 접하는 프로퍼티는 아래와 같은 형태일 것이다.
val name: String = "빅스"
위 name 프로퍼티는 뒷받침하는 필드와 접근자 메서드가 모두 포함되어있다.
자바로 본다면 이런 형태가 될 것이다.
private final String name = "빅스";
public final String getName() {
return this.name;
}
name 프로퍼티를 읽기 전용인 val로 선언했기 때문에 setter는 존재하지 않고 필드 또한 final로 선언되었다.
getter와 setter가 모두 존재하는 모습을 보고싶다면 var로 선언하면 된다.
// Kotlin
var name: String = "빅스"
// Java
private String name = "빅스";
public final String getName() {
return this.name;
}
public final void setName(String var1) {
this.name = var1;
}
이렇게 코틀린 프로퍼티는 자바의 필드, 접근자 메서드를 완전히 대신한다.
그럼 위 예시를 보고 나서 "그럼 필드는 무조건 있는건가?" 라는 의문을 가질 수 있다.
이제 접근자 메서드만을 대신하는 프로퍼티를 보자!
계산된 프로퍼티
커스텀 접근자를 이용하면, 필드가 없고 접근자 메서드만 존재하는 계산된 프로퍼티를 만들 수 있다.
위에서 봤던 name 프로퍼티에선 기본적인 접근자 메서드를 사용했기 때문에 생략되어 보이지 않았다.
그럼 커스텀 접근자를 통해 접근자를 만나보도록 하자.
class User(private val email: String) {
val nickName: String
get() = email.substringBefore('@')
}
자바로 치면 getter에 상응하는 접근자를 커스텀 했다.
이제 nickName 프로퍼티는 email의 @ 앞쪽만 가져올 것이다.
fun main() {
val user = User("bixx@naver.com")
println(user.nickName)
}

위처럼 사용하면 왼쪽처럼 출력되는 것을 볼 수 있다.
nickName 프로퍼티를 자바로 본다면 이런 형태가 된다.
public final String getNickName() {
return StringsKt.substringBefore$default(this.email, '@', (String)null, 2, (Object)null);
}
자바에 subStringBefore 함수가 없어서 디컴파일 과정에서 알아보기 어렵게 되었다.
하지만 현재 중점은 subStringBefore가 아니므로 보지 않기로 하자.
보면 우린 프로퍼티로 선언했는데 자바에선 함수인 것을 알 수 있다.
사실 생각해보면 backing field가 없고 email 값을 계산해서 가져오므로 함수와 다름없다.
그래서 아래 프로퍼티와 함수는 사실상 거의 차이가 없다.
class User(private val email: String) {
val nickName: String
get() = email.substringBefore('@')
fun nickName(): String {
return email.substringBefore('@')
}
}
그럼 둘 중 뭘 사용해야할까?
Kotlin In Action은 이렇게 말한다.
파라미터가 없는 함수를 정의하는 방식과 커스텀 게터를 정의하는 방식 중 어느 쪽이 더 나은지 궁금한 독자도 있을 것이다. 두 방식 모두 비슷하다. 구현이나 성능상 차이는 없다. 차이가 나는 부분은 가독성뿐이다. 일반적으로 클래스의 특성(프로퍼티에는 특성이라는 뜻이 있다)을 정의하고 싶다면 프로퍼티로 그 특성을 정의해야 한다.
그렇다고 한다.
뭘 사용할지는 각자 알아서 잘 판단하도록 하자.
개인적으로 필자는 책에 나온대로 특성이라고 생각된다면 프로퍼티를 사용한다.
이렇게 헷갈릴 때는 코틀린이 제공하는 api들이 어떤식으로 구현되었는지 살펴보면 좋다.
코틀린 EmptyList의 일부다.
특성이라고 생각되는 size는 커스텀 접근자를 통해 구현하고 있다.

또 다른 예시로 ArrayAsCollection에서도 커스텀 접근자를 통해 size를 정해준다.
(필자도 이번에 처음 보는 클래스로 Array를 Collection으로 바꿔주는 역할인 것 같다. 일단 size에 집중하자.)

사람 클래스를 만들고 생성자로 키와 몸무게를 받는다고 해보자.
이때 키와 몸무게를 이용 bmi를 계산하고자 한다. 이때 함수가 좋을까, 프로퍼티가 좋을까?
(이 예시는 우테코 5기의 '글로'가 했던 고민이다. 글로 땡큐)
class Person(val height: Double, val weight: Double) {
val bmi: Double get() = weight / sqrt(height)
fun getBmi(): Double {
return weight / sqrt(height)
}
}
bmi 수치는 몸무게를 키의 제곱으로 나눈다.
bmi 프로퍼티와 getBmi 함수 중 어떤 것을 사용하고 싶은가?
필자는 이럴 경우 프로퍼티를 선호한다.
정답은 없으니 잘 생각해서 써보자!
스토어드 프로퍼티 ( stored property / 저장된 프로퍼티 )
방금 backing field가 없는 계산된 프로퍼티를 살펴보았다.
그럼 backing field가 있는 프로퍼티는 뭐라고 부를까?
바로 스토어드 프로퍼티다.
이 명칭은 코틀린 공식 문서 어디에도 없지만 통용되는 말이다.
저장된 프로퍼티는 처음 프로퍼티를 설명하면서 이미 예시를 보여줬으므로 넘어가도록 하겠다.
프로퍼티가 자바의 필드, 접근자 메서드를 완전히 대신한다고 했는데
여기서 헷갈릴만한 부분 한 가지만 언급하고 끝내겠다.
값을 외부에 드러내면서 내부에서만 변경하고 싶어
자 그럼 어떻게 해야할까? 우선 자바의 경우 아래처럼 될 것이다.
public class Person {
private String name = "bixx";
public final String getName() {
return this.name;
}
public void changeName(String newName) {
name = newName;
}
}
그럼 코틀린은 어떻게 해야할까?
var로 만들어 변경 가능하도록 하고, setter에 상응하는 접근자 메서드를 private으로 감추면 된다.
class Person() {
var name: String = "bixx"
private set
fun changeName(newName: String) {
name = newName
}
}
혹시 전혀 헷갈리지도 않고 이미 알고 있었고 이딴걸 왜 모르는지 정말 이해가 안된다면 양해를 부탁한다.
나는 처음 공부할 때 생각지도 못했었다..
어차피 이 글은 나 같은 바보들이 볼 것이라고 생각하므로.. 도움이 됐을거라 믿는다!
+ 추가로 확장 프로퍼티라는 것도 존재하지만, 커스텀 접근자의 응용이라고 생각돼어 확장함수 편에서 소개하도록 하겠다. 확장함수에 대한 글을 언제 적을진 모르겠지만~
'Kotlin' 카테고리의 다른 글
[Kotlin] 확장 함수! 신기한 당신의 정체는? (0) | 2023.04.24 |
---|---|
[Kotlin] 인터페이스와 추상클래스의 차이 (1) | 2023.03.22 |
[Kotlin] 코틀린 인터페이스(Interface) (0) | 2023.02.21 |
[Kotlin] 코틀린의 헷갈리는 생성자와 프로퍼티 알아보기 (Java와 비교하여) (0) | 2023.02.20 |