Companion Objects in Kotlin



reference: 

https://blog.kotlin-academy.com/a-few-facts-about-companion-objects-37e18429b725





 Static in Kotlin

Kotlin에서는 Java의 static 키워드가 없습니다. Static과 유사하게 사용하기 위해서 아마 많은 분들이 companion object를 사용하고 계실 것 같습니다. 이 포스팅에서는 Java static과 Kotlin companion object와 간략하게 비교해보려 합니다.


 Companion objects

Companion object는 기본적으로 그 클래스 안에 존재하는 singleton 객체입니다. Java로 convert된 결과를 보더라도 companion object는 Companion이라는 이름을 가진 static singleton임을 알 수 있습니다. 이말인 즉, companion이라는 키워드는 사실 Companion이라는 이름을 갖는 클래스의 객체를 가리키는 shortcut 역할을 할 뿐입니다. 그리고 더 나아가 굳이 companion이 아니더라도 static 멤버들을 가질 수 있습니다. 아래 코드는 완벽히 동작합니다.

class TopLevelClass {

companion object {
fun doSomeStuff() {
...
}
}

object FakeCompanion {
fun doOtherStuff() {
...
}
}
}

fun testCompanion() {
TopLevelClass.doSomeStuff()
TopLevelClass.Companion.doSomeStuff()
TopLevelClass.FakeCompanion.doOtherStuff()

}


그러면 위의 companion과 FakeCompanion의 차이는 무엇일까요. 

Java로 decompile을 해보면 약간 다른 점이 있습니다. companion은 부모 클래스의 static 멤버 필드로 가지고 있는 반면, FakeCompanion은 INSTANCE라는 이름으로 본인의 객체 레퍼런스를 내부적으로 갖습니다. 즉, 위의 test코드가 Java에서는 아래와 같이 변형된다는 이야기입니다.

public void testCompanion() {
TopLevelClass.Companion.doSomeStuff();
TopLevelClass.FakeCompanion.INSTANCE.doOtherStuff();
}


 @JvmField, @JvmStatic

Annotation을 한번 살펴봅시다. 

@JvmField는 getter와 setter를 생성하지 않도록 명시하고, 그저 Java의 일반적인 필드로서 사용되도록 합니다. Companion 안에서 사용된다면 companion이 아닌 부모 클래스의 static field가 됩니다. 정리하자면, companion object 안의 일반 적인 멤버는 부모 클래스의 private 멤버가 되며 companion의 getter, setter를 통해서만 접근되고, @JvmField가 붙는다면 부모 클래스의 public 멤버가 되어 외부에서 자유롭게 접근됩니다. 물론 둘다 static인 건 같습니다.

@JvmStatic은 부모 클래스에게 해당 멤버에 대한 접근을 제공하는 용도로 사용됩니다. 특히 조심해야할 점은, companion의 멤버 필드는 부모 클래스의 static 멤버로 선언되지만, 멤버 메소드는 companion 안에서 선언된다는 점입니다. 즉, @JvmStatic을 붙여줘야 외부에서 companion object의 메소드를 접근할 수 있습니다. 예를 들어 Dagger를 사용할 때 provide 메소드에 @JvmStatic을 붙여줘야 하는 이유가 그것입니다. 아래는 companion의 getFoo1() 메소드에 @JvmStatic을 붙여줬을 때 생성되는 부모 메소드입니다. 이게 없다면 외부에서는 아예 접근조차 안되겠네요.

public static final int getFoo1() {
return Companion.getFoo1();
}

 

"object" keyword

object 키워드는 앞서 잠깐 언급된 바와 같이 클래스 안의 singleton 객체로서 동작합니다. 즉  예를 들어 Dagger에서 사용할 모듈은 굳이 companion일 이유가 없습니다.

@Module
object MyModule {

@Provides
@Singleton
@JvmStatic
fun provideSomething(anObject: MyObject): MyInterface {
return myObject
}
}

companion 또한 이름을 가지거나 interface를 구현할 수도 있습니다. 아래는 Kotlin에서 Parcelable을 만드는 전통적인 코드입니다. CREATOR가 companion object네요.

class ParcelableClass() : Parcelable {

constructor(parcel: Parcel) : this()

override fun writeToParcel(parcel: Parcel, flags: Int) {}

override fun describeContents() = 0

companion object CREATOR : Parcelable.Creator<ParcelableClass> {
override fun createFromParcel(parcel: Parcel): ParcelableClass = ParcelableClass(parcel)

override fun newArray(size: Int): Array<ParcelableClass?> = arrayOfNulls(size)
}
}


Conclusion

아무래도 Kotlin은 Java의 static을 별로 좋아하지 않는 느낌입니다. 코드 상으로 느껴지는 점은 static한 것들이 companion안에 다 모여있으니 한눈에 들어오지만.. 뭔가 코드가 더러워보이는 건 Java에 익숙해진 탓인 것 같습니다. Kotlin스럽게 코딩하기 어렵네요.

'programming > android' 카테고리의 다른 글

View - AppWidget  (0) 2018.08.23
View - Widget  (0) 2018.08.23
어떤 Context를 써야 하나요?  (0) 2018.03.26
기본적인 메모리 관리  (0) 2018.03.06
Splash 화면 구성하기  (3) 2018.02.20

+ Recent posts