2021. 6. 27. 21:54ㆍKotlin
목차
우리는 보통 kotlin에서 java의 static 변수 또는 메서드를 사용하기 위해 object 키워드 또는 companion object를 사용한다.
아래처럼 말이다. 아래와 같이 object 를 사용하는 것을 object declaration이라고 한다.
object ObjectTest {
const val CONST_STRING = "1"
fun test() {}
}
class CompanionObjectTest {
companion object {
const val CONST_TEST = 2
fun test() { }
}
}
kotlin에서는 static 이란 것이 없고 위와 같이 사용하기 때문에 이것이 static 이다 라고 오해할 수 있다. 나 역시 이번 공부를 통해 정확히 알기 전까지는 static과 동일한 것 아닐까? 하는 잘못된 생각을 가지고 있었다.
TL;DR:
kotlin의 object declaration 과 companion object는 java의 static 과 동일하지 않아.
✅ object declaration :
- Singleton 형태
- thread-safe
- lazy initialized : 실제로 사용될 때 초기화(initialized) 된다
- const val로 선언된 상수는 static 변수.
- object 내부에 선언된 변수와 함수들은 java 의 static 이 아님. 단, 아래 케이스들은 static
- const val 로 상수 선언한 것들.
- @JVMStatic 또는 @JVMField의 어노테이션이 붙은 변수 및 함수들.
✅ companion object :
- 해당 클래스(companion obejct 는 클래스 내부에 들어가는 블럭이므로) 자체가 static 이 아님.
즉, CompanionObjectTest() 로 생성할 때 마다 객체의 주소값은 다름. - 해당 클래스가 로드될 때 초기화 됨.
- const val로 선언된 상수는 static 변수.
- companion object 내부에 선언된 변수와 함수들은 java 의 static 이 아님. 단, 아래 케이스들은 static
- const val 로 상수 선언한 것들.
- @JVMStatic 또는 @JVMField의 어노테이션이 붙은 변수 및 함수들.
✅ object vs companion object 차이점
- 초기화 시점이 다름.
- object declaration 초기화 시점 :
실제로 사용될 때 initialized 된다. 실제로 내부 함수를 접근해야 init 블럭이 호출됨- i.e. ObjectTest.nonStaticFun() 을 호출할 때, 초기화된다는 것.
- companion object 초기화 시점 :
해당 클래스가 로드될 때 initialized 된다. 실제로 해당 클래스 (CompanionObjectTest)를 생성하면 companion object 내부 init 블럭이 호출됨을 확인.- i.e. val test = CompanionObjectTest() 시 초기화된다는 것.
object declaration
직접 java 로 변환된 코드를 통해 확인해보자.
ObjectTest.kt
object ObjectTest {
const val CONST_STRING = "1"
val nonStaticField = "2"
@JvmField
val staticField = "2"
fun nonStaticFun() {
println("test nonStaticFun()")
}
@JvmStatic
fun staticFun() {
println("test staticFun()")
}
}
ObjectTest.kt 가 java 로 변환된 코드 👇 (kotlin bytecode를 decompile 해서 얻은 ObjectTest.decompiled.java)
public final class ObjectTest {
@NotNull
public static final String CONST_STRING = "1";
@JvmField
@NotNull
public static final String staticField;
@NotNull
public static final ObjectTest INSTANCE;
@NotNull
private static final String nonStaticField;
@NotNull
public final String getNonStaticField() {
return nonStaticField;
}
public final void nonStaticFun() {
String var1 = "test nonStaticFun()";
boolean var2 = false;
System.out.println(var1);
}
@JvmStatic
public static final void staticFun() {
String var0 = "test staticFun()";
boolean var1 = false;
System.out.println(var0);
}
private ObjectTest() {
}
static {
ObjectTest var0 = new ObjectTest();
INSTANCE = var0;
nonStaticField = "2";
staticField = "2";
}
}
정리
- java 로 변환된 파일 내부에 INSTANCE라는 ObjectTest 타입의 static 객체가 생성됨.
👉 그래서 ObjectTest는 Singleton 형태라는 것. - const로 선언한 변수들은 public static 필드로 변환됨.
- val 또는 일반 함수로 선언된 애들은 static 필드가 아니어서 INSTANCE 객체로 접근 가능.
- 단, 함수에 @JVMStatic 을 선언한 경우에는 static 함수로 변환됨. @JVMField로 선언된 경우 static 필드가 됨.
따라서, java 파일에서 ObjectTest에 접근 시 아래와 같음.
// 아래 3가지는 static 필드 처리된 것.
String s = ObjectTest.CONST_STRING;
String s2 = ObjectTest.staticField;
ObjectTest.staticFun();
// 아래 2가지는 static 이 아니기 때문에, static 변수인 INSTANCE 로 접근해야 함.
ObjectTest.INSTANCE.getNonStaticField();
ObjectTest.INSTANCE.nonStaticFun();
companion object
직접 java 로 변환된 코드를 통해 확인해보자.
CompanionObjectTest.kt
class CompanionObjectTest {
companion object {
const val CONST_TEST = 2
val valTest = 1
fun nonStaticMethod() {
println("test nonStaticMethod()")
}
@JvmStatic
fun staticMethod() {
println("test staticMethod()")
}
}
}
위 코드가 java 로 변환된 코드 (kotlin bytecode를 decompile 해서 얻은 CompanionObjectTest.decompiled.java)
public final class CompanionObjectTest {
@NotNull
public static final String CONST_STRING = "1";
@JvmField
@NotNull
public static final String staticField = "2";
@NotNull
public static final CompanionObjectTest.Companion Companion = new CompanionObjectTest.Companion((DefaultConstructorMarker)null);
@NotNull
private static final String nonStaticField = "2";
@JvmStatic
public static final void staticFun() {
Companion.staticFun();
}
public static final class Companion {
@NotNull
public final String getNonStaticField() {
return CompanionObjectTest.nonStaticField;
}
public final void nonStaticFun() {
String var1 = "test nonStaticFun()";
boolean var2 = false;
System.out.println(var1);
}
@JvmStatic
public final void staticFun() {
String var1 = "test staticFun()";
boolean var2 = false;
System.out.println(var1);
}
private Companion() {
}
// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
정리
- CompanionObjectTest 클래스 자체가 static 이 아님.
👉 class 자체가 싱글톤은 아님.
👉 즉, CompanionObjectTest() 로 생성할 때마다 객체의 주소 값은 다름. - CONST_TEST와 같은 const val로 선언된 상수는 static 변수.
- companion object 내에서 선언된 일반 변수 또는 함수는 static class 인 Companion을 통해 접근 가능. 즉, 변수 및 함수 자체는 static 하지 않음.
- 단, companion object 내에서 @JVMStatic 또는 @JVMField 을 붙이면 static
따라서, java 파일에서 CompanionObjectTest 에 접근 시 아래와 같음.
String s3 = CompanionObjectTest.CONST_STRING;
String s4 = CompanionObjectTest.staticField;
CompanionObjectTest.staticFun();
CompanionObjectTest.nonStaticFun(); // ❌ error: nonStaticMethod() 는 static method 아님!!
CompanionObjectTest.Companion.getNonStaticField();
CompanionObjectTest.Companion.nonStaticFun();
Reference
'Kotlin' 카테고리의 다른 글
[Kotlin in Action] 8장. 고차함수와 inline function (inline 함수의 장단점, 사용 이유 등) (0) | 2022.01.09 |
---|---|
[Kotlin in Action] 4장. 클래스, 객체, 인터페이스 (0) | 2021.11.03 |
[Kotlin in Action] 3장. 함수 정의와 호출 (0) | 2021.10.09 |
[Kotlin] Coroutine suspend function 은 대체 뭐야? (16) | 2021.06.13 |
[Kotlin] Scope Functions - let, run, with, apply, and also 에 대하여 (0) | 2020.02.17 |