D.S

kunny.github.io

코틀린에는 static이 없다? - companion object

코틀린에는 static이 없다? - companion object Home • About 코틀린에는 static이 없다? - companion object 코틀린에는 정적(static) 변수 혹은 메소드가 없고, 대신 패키지 내에 함수를 선언하여 사용할 수 있습니다. 예를 들어 자바에서 다음과 같이 사용했다면, [Foo.java] package foo.bar; class Foo { public static final String BAR = "bar"; public static void baz() { // Do something } } 코틀린에서는 다음과 같이 함수를 정의하여 사용할 수 있습니다. [Foo.kt] package foo.bar const val BAR = "bar" fun baz() { // Do something } 프로젝트 전체를 코틀린으로 작성하는 경우라면 위와 같이 함수를 정의하여 사용해도 큰 문제가 없습니다. 하지만, 프로젝트의 일부에만 코틀린을 사용한 경우 자바에서 코틀린에서 만든 함수를 사용하려면 다소 깔끔하지 않은 방법을 사용해야 합니다. 위와 같이 Foo.kt 파일에 정의한 속성 및 함수는 자바에서 각각 FooKt.BAR 및 FooKt.baz()로 접근할 수 있습니다. 파일 이름과 동일한 이름의 클래스 선언 없이 선언된 필드나 메서드를 자바에서 접근할 수 있도록 하는 과정에서 파일 이름 뒤에 kt라는 접미사를 추가하기 때문입니다. 다음은 자바에서 위에서 정의한 필드 및 메서드를 사용하는 예를 보여줍니다. public void doSomething(Bundle args) { // args 내에 "bar" 키로 정의되어 있는 데이터 추출 int bar = args.getInt(FooKt.BAR) // baz() 메서드 수행 FooKt.bar() } 물론, 이러한 규칙을 무시하고 자신이 원하는 이름으로 생성되도록 하려면 파일의 맨 첫 줄에 @file:JvmName(name: String)을 사용하면 됩니다. 다음과 같이 사용하면 FooKt 대신 Foo를 사용하여 필드 및 메서드에 접근할 수 있습니다. @file:JvmName("Foo") // 'Foo' 라는 이름으로 자바에서 접근할 수 있도록 함 package foo.bar const val BAR = "bar" fun baz() { // Do something } object를 사용하면 기존에 자바에서 사용하던 방식과 매우 유사한 형태로 구현이 가능합니다. package foo.bar object Foo { const val BAR = "bar" fun baz() { // Do something } } 그런데, 이를 안드로이드에 적용하려고 보니… 안드로이드에서 정적 변수 및 메서드를 사용하는 경우는 크게 다음과 같습니다. 액티비티/프래그먼트의 인텐트 Extra로 사용하는 키 로그 출력을 위한 태그(Tag) 이름 정의 뷰 내부에서 사용하는 고정된 길이 값 (너비, 높이 등) 각종 유틸리티 클래스 내 메서드 위 경우들은 모두 선언된 클래스와 밀접하게 관련된 경우로, 클래스 외부에 별도로 선언하기엔 모호한 녀석들입니다. 물론, 다음과 같이 파일 내에 함수/필드 + 클래스를 정의할 수도 있습니다. [Foo.kt] package foo.bar const val BAR = "bar" fun baz() { // Do something } // Foo 클래스 선언 class Foo { ... } 하지만, 이렇게 할 경우 처음과 마찬가지로 자바에서 위에 정의된 함수 및 필드에 접근하려면 Foo.BAR, Foo.baz() 대신 FooKt.BAR, FooKt.baz()를 사용해야 합니다. object를 사용하면 해결할 수 있을 것 같지만, 클래스 및 object 이름은 중복될 수 없으므로 위와 같이 외부에 선언하는 것은 불가합니다. 그렇다면 이 문제는 어떻게 해결할 수 있을까요? 자바와 동일한 방식으로 사용하기: companion object companion object를 사용하면 위에서 나열했던 문제 없이 자바에서 정적 변수/메서드를 사용했던 것과 동일하게 사용할 수 있습니다. 다음은 사용 예를 보여줍니다. class Foo { companion object { const val BAR = "bar" fun baz() { // Do something } } } companion object 내에 선언된 속성과 함수는 {클래스 이름}.{필드/함수 이름} 형태로 바로 호출할 수 있습니다. 즉, 위의 Foo클래스 내 companion object에 선언된 baz() 함수는 다음과 같이 호출 가능합니다. override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... Something ... // Foo.baz() 함수 호출 Foo.baz() } 자바에선 저렇게 못 쓰는데요? - @JvmStatic companion object를 사용하여 위와 같이 구성한 코드를 자바에서 사용하려면 속성 및 함수가 자바의 필드/메서드로 해석되도록 알려주어야 합니다. const 선언이 되어 있는 속성은 별도 처리가 필요 없이 자바에서도 동일하게 사용 가능하며, 함수는 @JvmStatic 어노테이션을 사용하여 자바에서 정적 메서드로 사용할 수 있게 합니다. class Foo { companion object { // 자바에서도 동일하게 Foo.BAR 로 접근 가능 const val BAR = "bar" // 자바에서 정적 메서드(static method)처럼 사용할 수 있도록 함 @JvmStatic fun baz() { // Do something } } } 위와 같이 처리해 두면, 자바의 정적 필드 및 메서드를 사용하는 것과 동일하게 사용할 수 있습니다. public void doSomething(Bundle args) { // args 내에 "bar" 키로 정의되어 있는 데이터 추출 int bar = args.getInt(Foo.BAR) // baz() 메서드 수행 Foo.bar() } Primitive type이나 String이 아닌 타입은 어떻게 처리하나요? - @JvmField const 키워드는 Primitive type과 String 에만 사용 가능합니다. 따라서 이에 해당하지 않는 타입의 객체를 자바에서 정적 필드처럼 사용하려면 @JvmField 어노테이션을 사용해야 합니다. 다음은 Bar 타입의 속성 BAR를 자바의 정적 필드처럼 사용할 수 있도록 @JvmField 어노테이션을 사용한 예를 보여줍니다. class Foo { companion object { @JvmField BAR = Bar() } } class Bar { } 위와 같이 처리하면 다음과 같이 자바 코드에서 BAR 속성을 정적 필드처럼 사용할 수 있습니다. public void doSomething(Bundle args) { // Foo.BAR 필드 접근 Bar myBar = Foo.BAR; } 추가 참고자료: Object Expressions and Declarations - companion object Calling Kotlin from Java Written by 김태호 (Taeho Kim) 안드로이드와 오픈소스, 코틀린(Kotlin)에 관심이 많습니다. 현재 Google Developer Groups(GDG) Korea Android 운영자와 Google Developer Expert(GDE)로 활동하고 있습니다. kunny Androidhuman Published 10 Jul 2016 Supported by Proudly published with Jekyll and Amplify You should subscribe to my feed. All content copyright 김태호 (Taeho Kim) © 2017 All rights reserved.