코틀린 클래스의 initializer blocks
코틀린의 primary constructor(이하 주생성자)는 별도의 코드를 포함할 수 없습니다. 대신 초기화 코드는 init 이라는 키워드를 이용한 initializer blocks에서 선언할 수 있습니다.
fun kotlin() { Student(name = "John") }
class Student(var name: String, var id: Int = 0) { constructor(name: String) : this(name, 100) { println("constructor block :: name is $name and id is $id") } init { name = "kim" println("init block :: name is $name and id is $id") } } |
위 코드의 결과는 아래와 같습니다.
init block :: name is kim and id is 100 constructor block :: name is John and id is 100 |
이것으로 init의 호출 순서를 유추해 볼 수 있습니다.
먼저 객체를 생성하면 호출되면 부생성자를 통해 주생성자가 호출됩니다. [name=John, id=100] 이때 initializer block이 존재하기 때문에 해당 block에서 name을 초기화 합니다. [name=kim, id=100]
이 과정이 끝난 후에야 부생성자의 출력문이 동작합니다.
익숙한(?) Java 코드로 보면 이해가 쉽습니다.
public static final class Student { private String name; private int id;
public Student(String name) { this(name, 100); System.out.println(constructor block :: name is " + name + " and id is " + this.id); } public Student(String name, int id) { this.name = name; this.id = id; this.name = "kim"; // init block System.out.println("init block :: name is " + this.name + " and id is " + this.id); } } |
Java 코드를 참고하니 조금 더 생각하기가 쉬워졌습니다. 그러면 다음과 같이 객체를 생성하면 어떻게 동작할까요?
Student(name = "John", id = 500) |
Java 코드로 유추해보면 바로 다음 코드를 실행할 것입니다.
public Student(String name, int id) { this.name = name; this.id = id; System.out.println("init block :: name is " + this.name + " and id is " + this.id); } |
이걸 그대로 코틀린으로 생각해보면 다음과 같겠죠.
class Student(var name: String, var id: Int = 0) { constructor(name: String) : this(name, 100) { println("constructor block :: name is $name and id is $id") } init { println("init block :: name is $name and id is $id") } } |
곧바로 주생성자를 거쳐 init block을 실행할 것입니다. 따라서 결과는 아래처럼 나옵니다.
init block :: name is John and id is 500 |
이제 여러가지 방법으로 테스트를 진행하면서 익숙해질 수 있습니다. 실제로 위에서 설명한 내용을 유추하고 이해하기 위해서 테스트한 코드는 다음과 같습니다.
class ExampleUnitTest { @Test fun kotlin() { Student(name = "John", id = 500) Student2(name = "John") Student3(name = "John") }
class Student(var name: String, var id: Int = 0) { constructor(name: String) : this(name, 100) { println("constructor block :: name is $name and id is $id") } init { name = "kim" println("init block :: name is $name and id is $id") } }
class Student2(var name: String, var id: Int = 0) { constructor(name: String) : this(name, 100) { println("constructor block2 :: name is $name and id is $id") } init { name = "kim" println("init block2-1 :: name is $name and id is $id") } init { id = 200 println("init block2-2 :: name is $name and id is $id") } }
class Student3(var name: String, var id: Int = 0) { constructor(name: String) : this(name, 100) { println("constructor block3 :: name is $name and id is $id") } init { id = 600 println("init block3-1 :: name is $name and id is $id") } init { name = "kim" id = 200 println("init block3-2 :: name is $name and id is $id") } }
} |
init block :: name is kim and id is 500 init block2-1 :: name is kim and id is 100 init block2-2 :: name is kim and id is 200 constructor block2 :: name is John and id is 200 init block3-1 :: name is John and id is 600 init block3-2 :: name is kim and id is 200 constructor block3 :: name is John and id is 200 |