프로그래밍Kotlin 중급 개발자

Kotlin에서 생성자 위임(constructor delegation)이란 무엇이며, 기본/보조 생성자가 호출되는 방식과 사용 시 주의할 점은 무엇인가요?

Hintsage AI 어시스턴트로 면접 통과

답변.

Kotlin에서 각 클래스는 하나의 기본 생성자(primary constructor)와 여러 개의 보조 생성자(secondary constructors)를 가질 수 있습니다. 생성자 위임은 보조 생성자가 반드시 기본 생성자를 직접 호출하거나 같은 클래스의 다른 보조 생성자를 호출해야 하는 메커니즘입니다(결국 기본 생성자를 호출하게 됩니다). 클래스가 다른 클래스를 상속할 경우, 자식 클래스의 각 보조 생성자는 필요할 경우 반드시 수퍼 클래스의 생성자를 명시적으로 위임해야 합니다.

질문의 배경

Java에서 생성자는 this()나 super()를 통해 서로 직접 호출할 수 있으며, 다양한 조합으로 생성자를 오버로딩할 수 있습니다. Kotlin에서는 이 개념이 형식화되어 있으며, 클래스는 오직 하나의 기본 생성자만 가질 수 있고, 보조 생성자는 명시적으로 지정해야 하는 위임 로직을 사용할 수 있습니다.

문제

위임이 잘못 구현되면 컴파일 오류가 발생할 수 있습니다: 기본 생성자가 선언되었을 경우 이를 호출하지 않거나, 상속된 클래스에서 기본 클래스에 기본 생성자가 없는 경우 슈퍼 생성자를 호출하지 않아야 합니다. 언제 init 블록이 호출되는지, 매개변수가 어떻게 전달되는지, 위임이 초기화 순서에 어떤 영향을 미치는지를 이해하는 것이 중요합니다.

해결책

기본 위임 예시:

class Person(val name: String) { constructor(name: String, age: Int) : this(name) { println("보조 생성자: $name, $age") } }

상속을 사용할 경우:

open class Parent(val name: String) class Child : Parent { constructor(name: String) : super(name) { println("자식 보조: $name") } }

주요 특징:

  • 보조 생성자는 반드시 같은 클래스의 다른 생성자(기본 또는 보조)로 명시적 위임을 해야 합니다.
  • init 블록은 항상 기본 생성자 호출 후에 실행되며, 보조 생성자가 호출된 방식에 관계없이 수행됩니다.
  • 기본 클래스에 인자가 없는 생성자가 존재하지 않는 경우, super(...) 호출은 필수입니다.

꼬리표가 붙은 질문들.

보조 생성자가 아무 데도 위임하지 않을 수 있나요?

아니요, 컴파일러는 this(...)나 super(...)를 명시적으로 호출할 것을 요구하며, 그렇지 않으면 오류가 발생합니다.

보조 생성자를 사용할 경우 초기화 및 init 블록은 어떤 순서로 실행되나요?

항상 가장 먼저 기본 생성자와 init 블록이 호출되며, 그 다음 보조 생성자의 초기화 코드가 실행됩니다.

class Demo(val value: String) { init { println("init 블록") } constructor(value: String, code: Int) : this(value) { println("보조: $code") } } Demo("kotlin", 7) // 출력: // init 블록 // 보조: 7

자식이 부모의 보조 생성자가 있을 경우 기본 생성자만 호출할 수 있나요?

네, 하지만 반드시 super(...)를 통해 명시적으로 호출해야 하며, 보조 생성자는 자식에게 직접적으로 보이지 않습니다.

일반적인 오류 및 안티 패턴

  • 보조 생성자에서만 초기화 논리를 사용하고 init 블록을 잊는 것.
  • 위임이 없을 경우 "보조 생성자는 기본 생성자에 위임해야 합니다" 오류.
  • 필수 슈퍼 생성자가 없기 때문에 부모의 생성자를 호출할 수 없는 상속.

실생활 예시

부정적인 사례

프로젝트에서 보조 생성자가 기본 생성자를 호출하지 않으면, 중요한 초기화(예: 필수 필드 설정)가 이루어지지 않아 런타임에서 버그와 크래시를 초래하게 됩니다.

장점:

  • 코드가 줄어듭니다.

단점:

  • 초기화되지 않은 속성.
  • 탐지하기 어려운 오류들.

긍정적인 사례

모든 보조 생성자가 반드시 this(...)를 통해 위임하며, 필요한 초기화가 기본/초기화 블록에 중앙 집중화되어 구조가 명확하여 유지보수가 용이합니다.

장점:

  • 모든 객체가 올바르게 완전하게 초기화된다는 보장.

단점:

  • 초기화 순서를 명확하게 이해해야 합니다.