[Kotlin in Action] 17. 클래스의 생성자와 초기화 블록, 주 생성자와 부 생성자

반응형
728x90
반응형

생성자

자바에서는 생성자를 하나 이상 선언할 수 있다. 코틀린은 주(primary) 생성자와 부(secondary) 생성자를 구분한다. 또한 코틀린에서는 초기화블록(initializer block)을 통해 초기화 로직을 추가할 수 있다.

 

 

주 생성자 (primary)

  • 중괄호가 없고 괄호 사이에 val 선언만 존재한다.
  • 클래스 이름 뒤에 오는 괄호로 둘러싸인 코드를 '주 생성자'라고 부른다.
class User1(val nickname: String)

 

위 코드의 실제 로직은 아래와 같다.

class User2 constructor(val _nickname: String) { //_nickname : 프로퍼티와 생성자 파라미터를 구분해준다.
    // 주 생성자는 생성자 파라미터를 저장하고 그 생성자 파라미터에 의해 초기화되는 프로퍼티를 정의하는 2가지 목적에 쓰인다.
    val nickname: String
    init { // 초기화 블록
        nickname = _nickname
    }
}

1) constructor 키워드

주 생성자나 부 생성자 정의를 시작할때 사용한다.

 

2) init 초기화 블록

init { // 초기화 블록
    nickname = _nickname
}

초기화 블록을 시작한다. 초기화 블록에는 클래스의 객체가 만들어질때 실행될 초기화 코드가 들어간다.

초기화 블록은 주 생성자와 함께 사용된다. 주 생성자는 제한적이기 때문에 별도의 코드를 포함할 수 없으므로 초기화 블록이 필요하다. 필요하다면 클래스 안에 여러 초기화 블록을 선언할 수 있다.

 

 

초기화 코드를 프로퍼티 선언에 포함

class User3(_nickname: String) { // 파라미터가 하나뿐인 주 생성자
    // 프로퍼티를 주 생성자의 파라미터로 초기화한다.
    val nickname = _nickname
}

nickname 프로퍼티를 초기화하는 코드를 nickname 프로퍼티 선언에 포함시킬 수 있어서 초기화 코드를 초기화 블록에 넣을 필요가 없다. 또 주 생성자 앞에 별다른 애노테이션이나 가시성 변경자가 없다면 constructor를 생략해도 된다.

 

 

클래스 본문에서 val 키워드로 프로퍼티 정의

'val'은 이 파라미터에 상응하는 프로퍼티가 생성된다는 뜻이다.

 

프로퍼티를 초기화하는 식이나 초기화 블록 안에서만 주 생성자의 파라미터를 참조할 수 있다. 클래스 본문에서 val 키워드를 통해 프로퍼티를 정의했다. 주 생성자의 파라미터로 프로퍼티를 초기화한다면 그 주 생성자 파라미터 이름 앞에 val 을 추가하는 방식으로 프로퍼티 정의 초기화를 간략히 쓸 수 있다.

class User4(val nickname: String)

 

 

생성자 파라미터에 디폴트 값 정의

  • 생성자 파라미터에 대한 디폴트 값을 제공한다.
class User(val nickname: String, val isSubscribed: Boolean = true)

 

 

클래스 인스턴스 생성

클래스의 인스턴스를 만들려면 new 키워드 없이 생성자를 직접 호출하면 된다.

fun main() {
    val test1 = User("test1");
    val test2 = User("test2", false); // 모든 인자를 파라미터 선언 순서대로 지정
    val test3 = User("test3", isSubscribed = false); // 생성자 인자 중 일부에 대해 이름 지정
    
    println(test1.isSubscribed) // true (default)
    println(test2.isSubscribed) // false
    println(test3.isSubscribed) // false
}

 

 

기반클래스에서의 생성자

기반클래스란?

https://devfunny.tistory.com/764

 

[Kotlin in Action] 16. 봉인된 클래스: 클래스 계층 정의시 클래스 확장 제한 (sealed 변경자)

상위클래스 Expr 숫자를 표현하는 Num 덧셈 연산을 표현하는 Sum 위 2개의 하위 클래스가 있다. when 식에서 이 모든 하위클래스를 처리하면 편리하다. interface Expr class Num(val value: Int) : Expr class Su..

devfunny.tistory.com

 

기반클래스 생성자 호출

클래스에 기반 클래스가 있다면 주 생성자에서 기반 클래스의 생성자를 호출해야할 필요가 있다. 기반 클래스를 초기화하려면 기반 클래스 이름 뒤에 괄호를 치고 생성자 인자를 넘긴다.

open class OpenUser(val nickname: String) {}
class TwitterUser(nickname: String) : OpenUser(nickname) {}

 

 

디폴트 생성자

클래스를 정의할때 별도로 생성자를 정의하지 않으면 컴파일러가 자동으로 아무 일도 하지 않는 인자가 없는 디폴트 생성자를 만든다.

open class Button // 인자가 없는 디폴트 생성자가 만들어진다.

Button 생성자는 아무 인자도 받지 않지만, Button 클래스를 상속한 하위 클래스는 반드시 Button 클래스의 생성자를 호출해야한다.

class RadioButton: Button()

이 규칙으로 인해, 기반 클래스의 이름 뒤에는 꼭 빈 괄호가 들어간다.

반면 인터페이스는 생성자가 없기 때문에 어떤 클래스가 인터페이스를 구현하는 경우, 그 클래스의 상위 클래스 목록에 있는 인터페이스 이름 뒤에는 아무 괄호도 없다.

 

클래스 정의에 있는 상위 클래스 및 인터페이스 목록에서 이름 뒤에 괄호가 붙었는지 살펴보면 쉽게 기반 클래스와 인터페이스를 구별할 수 있다. 어떤 클래스를 클래스 외부에서 인스턴스화하지 못하게 막고 싶다면 모든 생성자를 private 으로 만들면 된다.

 

 

private 변경자

주 생성자에 private 변경자를 붙일 수 있다.

class Secretive private constructor() {} // 이 클래스의 유일한 주 생성자는 비공개다.

외부에서는 Secretive를 인스턴스화할 수 없다. 생성자에 아무 파라미터도 없는 클래스도 많고, 생성자 코드 안에서 생성자가 인자로 받은 값을 프로퍼티에 설정하기만 하는 생성자도 많다.

 

 

부 생성자 (secondary)

코틀린에서는 생성자가 여럿있는 경우가 자바보다 훨씬 적다.

자바에서 오버로드한 생성자가 필요한 상황 중 상당수는 코틀린의 디폴트 파라미터 값과 이름 붙인 인자 문법으로 해결할 수 있다.

 

그래서 생성자가 여럿 필요한 일반적인 상황은, 프레임워크 클래스를 확장해야하는데 여러가지 방법으로 인스턴스를 초기화할 수 있게 다양한 생성자를 지원해야 하는 경우다.

 

예제

예를 들어 자바에서 선언된 생성자가 2개인 View 클래스가 있다고 하자. 그 클래스를 코틀린으로는 다음과 비슷하게 정의할 수 있다.

open class View {
    // 이 클래스는 주 생성자를 선언하지 않고, 부 생성자만 2가지 선언한다.
    constructor(ctx: Context) { // 부 생성자도 constructor 키워드로 시작한다.

    }

    constructor(ctx: Context, attr: Attributes) {

    }
}

 

 

상위 클래스의 생성자 호출

생성자가 상위 클래스 생성자에게 객체 생성을 위임한다.

class MyButton: View {
    // 상위 클래스의 생성자를 호출한다.
    // 생성자가 상위 클래스 생성자에게 객체 생성을 위임한다.
    constructor(ctx: Context) : super() {
    }

    constructor(ctx: Context, attr: Attributes) : super() {
    }
}

 

 

this 키워드 사용

Mybutton2 클래스의 생성자중 하나가 파라미터의 디폴트 값을 넘겨서 같은 클래스의 다른 생성자(this 사용하여 참조)에게 생성을 위임한다.

두번째 생성자는 여전히 super()를 호출하여 상위 클래스 생성자에게 객체 생성을 위임한다.

class MyButton2: View {
    // this를 사용해서 클래스 자신의 다른 생성자를 호출할 수 있다.
    constructor(ctx: Context) : this(ctx, MY_STYLE) {
    }

    constructor(ctx: Context, attr: Attributes) : super(ctx, attr) {
    }
}

 

 

주 생성자가 없는 경우

클래스에 주 생성자가 없다면 모든 부 생성자는 반드시 상위 클래스를 초기화하거나, 다른 생성자에게 생성을 위임해야한다. 각 부 생성자에서 객체 생성을 위임하는 내용을 보면, 그 끝에는 상위 클래스 생성자를 호출한다.

 

부 생성자가 필요한 주된 이유는 자바 상호운용성이다. 하지만 부 생성자가 필요한 다른 경우도 있다. 클래스 인스턴스를 생성할 때 파라미터 목록이 다른 생성 방법이 여럿 존재하는 경우에는 부 생성자를 여럿 둘수 밖에 없다.

 

 

반응형

Designed by JB FACTORY