프로그래밍백엔드 개발자

Kotlin에서 상수 선언 및 동반 객체(companion objects) 작업의 특징을 설명하십시오. 제약 및 뉘앙스를 포함합니다.

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

답변.

상수 선언 및 companion objects 사용은 Kotlin의 중요한 개념으로, Java의 기존 static 멤버와 OOP와 함수형 프로그래밍 패러다임 간의 어려움을 대체하게 되었습니다.

문제의 역사: Java에서는 상수를 위해 일반적으로 static final 필드를 사용하고, 정적 메서드는 유틸리티나 팩토리 함수 용도로 사용됩니다. Kotlin에서는 static 대신 object와 companion object를 도입하고, compile-time 상수에 대해서는 const 키워드를 사용합니다.

문제: 클래스 인스턴스에 의존하지 않는 값을 선언하고, 팩토리 메서드와 정적 상태를 조직하여 OOP의 무결성을 유지해야 합니다.

해결책: companion object는 클래스 내에 선언되어 모든 인스턴스에 공통적인 멤버를 배치할 수 있게 해줍니다:

코드 예시:

class MyClass { companion object { const val DEFAULT_LIMIT = 10 fun create(): MyClass = MyClass() } } val limit = MyClass.DEFAULT_LIMIT val instance = MyClass.create()

주요 특징:

  • companion object 내부의 모든 것은 Java의 static 멤버처럼 동작하지만 OOP 통합 및 상속/인터페이스 가능성을 유지합니다.
  • companion object 내부의 compile-time 상수에는 const 키워드가 필수입니다.
  • companion object 자체는 객체로서 접근할 수 있고(참조를 저장할 수 있음) 인터페이스를 구현할 수 있습니다.

트릭 질문.

companion object가 클래스 내에서 여러 인스턴스를 가질 수 있습니까?

아니요, 하나의 클래스 내에는 오직 하나의 companion object만 있을 수 있습니다. 두 번째 companion object를 선언하려고 하면 컴파일 오류가 발생합니다. 하지만 companion object 내부에는 여러 메서드/속성을 포함할 수 있습니다.

companion object 내에서 lateinit 변수를 초기화할 수 있습니까?

아니요, const가 있는 속성이나 companion object 내의 변수가 즉시 초기화되거나 명시적인 초기화가 있는 val/var이어야 합니다. companion object 내의 속성에 대해서는 lateinit이 허용되지 않습니다.

companion object가 고유한 이름을 가질 수 있으며, 이것이 필요할 때가 있습니까?

네, companion object의 이름은 명시적으로 설정할 수 있으며, 객체 이름으로 접근하거나 인터페이스를 구현할 필요가 있을 때 사용됩니다. 다른 경우에는 선택적입니다. 예:

class Foo { companion object Factory { fun create(): Foo = Foo() } } val instance = Foo.Factory.create()

전형적인 오류 및 안티 패턴

  • companion object 내에서 mutable-static 변수를 사용하는 것은 멀티스레드 코드에서 경쟁 조건을 초래할 수 있습니다.
  • 같은 객체 내에 compile-time 상수(const)와 runtime 값을 혼합하는 것
  • file-level 함수/속성으로 충분한 경우 불필요한 companion object 사용

실생활 예시

부정적 사례

보조 기능 및 프로그램의 전역 변수가 companion object에 배치되고, var 대신 val/const가 사용됩니다:

장점:

  • 모든 "정적" 함수와 변수가 한 곳에 있어 찾기 쉽습니다.

단점:

  • 테스트의 어려움, 전역 상태가 있어 코드의 다른 부분에서 우연히 변경될 수 있습니다.

긍정적 사례

companion object 내에서 오직 compile-time 상수(const val)와 pure functions만 사용되고, 모든 변경 가능한 것은 국소화되거나 DI를 통해 전달됩니다:

장점:

  • 예측 가능하고 안전한 코드, 명확한 아키텍처로 인해 캡슐화 수준이 향상됩니다.

단점:

  • 글로벌 유틸리티를 위해 file-level 객체를 생성해야 하는 경우가 있으며, 약간의 보일러플레이트가 필요합니다.