9강. 코틀린에서 클래스를 다루는 방법
- 클래스와 프로퍼티
- 생성자와 init
- 커스텀 getter, setter
클래스와 프로퍼티
- 프로퍼티(property) = 필드(field) + getter + setter
다음은 개명이 불가능한 Java 클래스입니다.
public class JavaPerson {
private final String name;
private int age;
public JavaPerson(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Kotlin으로 구현하면 다음과 같습니다. Kotlin에서는 필드만 만들면 getter, setter를 자동으로 만들어 줍니다. 그러므로 Kotlin에서는 프로퍼티라고 부릅니다.
public class Person constructor(name: String, age: Int) {
val name = name
var age = age
}
- 접근 지시어, public은 생략 가능합니다.
- constructor는 생략할 수 있습니다.
- 클래스의 필드 선언과 생성자를 동시에 선언할 수 있습니다.
- 클래스의 body에 아무 것도 없다면 생략 가능합니다.
그러므로, 다음과 같이 간결하게 구현할 수 있습니다.
class Person(
val name: String,
var age: Int
)
getter, setter에 대한 호출은 .필드를 사용합니다. Kotlin에서 Java 클래스를 사용할 경우에도 .필드로 getter, setter를 사용합니다.
val person = Person("Kotlin", 100)
println(person.name) // getter
person.age = 10 // setter
println(person.age)
생성자와 init
다음은 클래스가 생성되는 시점에 나이를 검증하는 Java 코드입니다.
public JavaPerson(String name, int age) {
if (age <= 0) {
throw new IllegalArgumentException(String.format("나이는 %s일 수 없습니다", age));
}
this.name = name;
this.age = age;
}
Kotlin으로 구현하면 다음과 같습니다.
class Person(
val name: String,
var age: Int
) {
init {
if (age <= 0) {
throw IllegalArgumentException("나이는 ${age}일 수 없습니다.")
}
}
}
- init(초기화 블록): 클래스가 초기화되는 시점에 한 번 호출되는 블록입니다. 값을 적절히 만들어 주거나 validation 로직을 넣는 용도로 사용됩니다.
다음은 새로운 생성자를 추가한 Java 코드입니다.
public JavaPerson(String name, int age) {
if (age <= 0) {
throw new IllegalArgumentException(String.format("나이는 %s일 수 없습니다", age));
}
this.name = name;
this.age = age;
}
public JavaPerson(String name) {
this(name, 1);
}
Kotlin에서 새로운 생성자는 constructor 키워드와 함께 만들어져야 합니다.
class Person(
val name: String,
var age: Int
) { // primary constructor
init {
if (age <= 0) {
throw IllegalArgumentException("나이는 ${age}일 수 없습니다.")
}
println("init block")
}
constructor(name: String) : this(name, 1) { // secondary constructor
println("secondary constructor")
}
}
- constructor(파라미터)로 생성자를 추가합니다.
- 클래스를 만들 때 함께 만들었던 생성자를 주생성자(primary constructor)라고 합니다.
- 주생성자는 반드시 존재해야 합니다. 단, 주생성자에 파라미터가 하나도 없다면 생략 가능합니다.
- 아래 있는 생성자를 부생성자(secondary constructor)라고 합니다. 부생성자는 있을 수도 있고 없을 수도 있습니다.
- 부생성자는 최종적으로 주생성자를 this로 호출해야 합니다.
- 부생성자는 body를 가질 수 있습니다.
- 본문은 역순으로 실행됩니다. 즉, 부생성자를 통해 인스턴스를 생성하면 초기화 블록의 body가 먼저 호출됩니다.
Kotlin에서는 다음과 같이 부생성자보다는 default parameter를 권장합니다. 또한, Converting과 같은 경우도 부생성자를 사용할 수 있지만, 그보다 정적 팩토리 메서드 사용을 권장합니다.
class Person(
val name: String = "Kotlin",
var age: Int = 1,
) { // primary constructor
init {
if (age <= 0) {
throw IllegalArgumentException("나이는 ${age}일 수 없습니다.")
}
}
}
커스텀 getter, setter
다음은 성인인지 확인하는 Java 코드입니다.
public boolean isAdult() {
return this.age >= 20;
}
Kotlin에서는 Java와 같이 함수로 구현할 수도 있고, 프로퍼티로도 구현할 수 있습니다.
// Function
fun isAdult(): Boolean {
return this.age >= 20
}
// Custom getter
val isAdult: Boolean
get() = this.age >= 20
val isAdult: Boolean
get() {
return this.age >= 20
}
Custom getter를 사용하면 자기 자신을 변형해 줄 수도 있습니다. 다음은 name을 get할 때 무조건 대문자로 바꾸어 주는 코드입니다.
class Person(
name: String,
var age: Int
) {
val name = name
get() = field.uppercase()
}
- field: 자기 자신을 가리키는 예약어입니다. 보이지 않는 field라고 해서 backing field라고 부릅니다.
다음은 Custom setter를 사용한 예제 코드입니다. name을 set할 때 무조건 대문자로 바꾸는 코드입니다.
class Person(
name: String,
var age: Int
) {
var name = name
set(value) {
field = value.uppercase()
}
}
감사합니다.