(Kotlin) Class, 생성자
extends / implements
구분 없이 그냥 kt :
붙이고 적는데, 클래스 상속인 경우 super class의 생성자를 호출해야 하므로 ()
를 붙여주어야 하고 인터페이스 구현인 경우 생성자가 없기 때문에 붙이지 않아야 하므로 이를 보면 둘을 구분할 수 있다.
주 생성자(primary constructor)
주 생성자는 클래스 이름 뒤에 오는 ()
로 둘러싸인 코드를 의미한다. 주 생성자의 파라미터는 1. 초기화 블록, 2. 프로퍼티 초기화 식, 3. 주 생성자 자체 에서만 접근할 수 있다.
1. 정석대로 초기화하면 이런 식이다.
생성자를 정의할 때는 contsructor
키워드를 사용한다. 주 생성자의 body(초기화 블록) 지정은 init
키워드를 사용한다.
1
2
3
4
5
6
7
class User1 constructor(_name: String) {
val name: String
init {
name = _name
// this.name = _name java나 python처럼 이렇게 해도 된다.
}
}
2. 굳이 init을 사용할 필요가 없는 경우.
Note ) 주 생성자 앞에 별다른 애노테이션이나 가시성 변경자가 없다면 constructor
를 생략할 수 있다.
1
2
3
class User2(\_name: String) {
val name = \_name
}
3. 주 생성자에서 파라미터를 받으면서 동시에 프로퍼티 생성 및 초기화.
1
class User3(val name: String)
부 생성자(secondary constructor)
코틀린은 디폴트 파라미터를 지원하기 때문에 보통 주 생성자 만으로 해결이 되는데, 파라미터 목록에 따라 다른 생성 방법을 지정해야 하는 경우 여러 개의 생성자를 정의해야 하는 상황도 있다.
- 디폴트 파라미터. 안되면
- 정적 팩터리 메소드 패턴 . 이것도 안되면
- 부 생성자를 고려한다.
Effective Kotlin Item 33 , Effective Java에서 얘기하듯 추가적인 생성자(부생성자) 보다는 정적 팩터리 메서드를 사용하는 것이 낫다.
주 생성자와 부 생성자를 함께 사용하는 경우
Note ) 주 생성자가 존재한다면 반드시 호출되어야 하므로, 부 생성자에서 호출해주어야 한다. Note ) 부 생성자의 arg에는 val
을 적을 수 없다.
1
2
3
4
5
6
class User (val name: String){
var nick = ""
constructor(_name: String, _nick: String) : this(_name){ // 주 생성자 호출
nick = _nick
}
}
주 생성자를 사용하지 않고 부 생성자만 사용하는 경우
1
2
3
4
5
6
7
8
9
10
11
12
13
14
open class View {
// constructor(ctx: Context) {
// }
constructor(ctx: Context, attr: AttributeSet) {
}
}
class Button : View {
constructor(ctx: Context)
: this(ctx, MY_STYLE) { // 자신의 다른 생성자에 객체 생성을 위임
}
constructor(ctx: Context, attr: AttributeSet)
: super(ctx, attr) { // View 생성자에 객체 생성을 위임
}
}
Note) 클래스에 주 생성자가 없다면 모든 부 생성자는 반드시 상위 클래스를 초기화하거나 다른 생성자에게 생성을 위임해야 한다.
인터페이스의 추상 프로퍼티
인터페이스에 프로퍼티를 선언할 수 있는데, 인터페이스가 상태를 가질 수는 없기 때문에 프로퍼티를 뒷받침하는 필드는 없다. 다만 그때그때 값을 계산해서 돌려줄 수 있는 게터와 세터를 지정할 수는 있다.(확장 프로퍼티와 유사 하다.)
1
2
3
4
5
interface User {
val email: String // 반드시 override 해야 한다.
val nick: String // override하지 않고 사용할 수 있다.
get() = email.substringBefore('@')
}