Hanbit the Developer
Kotlin Documentation | Object expressions and declarations 본문
Category: Concepts - Classes and objects
문서 링크: https://kotlinlang.org/docs/object-declarations.html
Object expressions
객체를 일시적으로 사용할 때 유용하다:
val helloWorld = object {
val hello = "Hello"
val world = "World"
// object expressions extend Any, so `override` is required on `toString()`
override fun toString() = "$hello $world"
}
Inheriting anonymous objects from subtypes
window.addMouseListener(**object : MouseAdapter()** {
override fun mouseClicked(e: MouseEvent) { /*...*/ }
override fun mouseEntered(e: MouseEvent) { /*...*/ }
})
Using anonymous objects as return and value types
interface A {
fun funFromA() {}
}
interface B
class C {
// The return type is Any; x is not accessible
fun getObject() = object {
val x: String = "x"
}
// The return type is A; x is not accessible
fun getObjectA() = object: A {
override fun funFromA() {}
val x: String = "x"
}
// The return type is B; funFromA() and x are not accessible
fun getObjectB(): B = object: A, B { // explicit return type is required
override fun funFromA() {}
val x: String = "x"
}
}
val x에 접근할 수 없는 이유는 object expression을 통해 임시로 정의되어서 외부에서 인터페이스를 확인할 수 없기 때문이다.(getObjectA()로 얻은 인스턴스에 대해서는 funFromA() 호출이 가능하다.)
Accessing variables from anonymous objects
fun countClicks(window: JComponent) {
var **clickCount** = 0
var **enterCount** = 0
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
**clickCount**++
}
override fun mouseEntered(e: MouseEvent) {
**enterCount**++
}
})
// ...
}
anonymous object에서 해당 scope 내의 변수에 접근할 수 있다.
*이것이 가능한 이유는 object expression이 로컬 변수처럼 작동하기 때문이다. 다음은 코틀린 코드와 디컴파일된 자바 코드이다:
fun countClicks() {
var clickCount = 0
val obj = object {
fun addClickCount() {
clickCount++
}
}
}
public final class DataSourceModuleKt {
public static final void countClicks() {
final IntRef clickCount = new IntRef();
clickCount.element = 0;
Object var10000 = new Object() {
public final void addClickCount() {
int var10001 = clickCount.element++;
}
};
}
}
Object
싱글톤 객체를 정의하기 쉽다:
object DataProviderManager {
fun registerDataProvider(provider: DataProvider) {
// ...
}
val allDataProviders: Collection<DataProvider>
get() = // ...
}
Data objects
data class와 비슷하며 equals(), toString(), hashCode()을 자동으로 생성한다.
data object MyDataObject {
val x: Int = 3
}
fun main() {
println(MyDataObject) // MyDataObject
}
Companion objects
companion 키워드로 클래스 내부에서의 object를 정의한다:
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
val instance = MyClass.create()
val x = MyClass.Companion
Without name:
class MyClass2 {
companion object { }
}
val y = MyClass2.Companion
@JvmStatic 주석을 사용하게 되면 companion object를 실제 스태틱 메소드나 필드로 생성할 수 있다. Java에서 MyFragment.Companion.newInstance()로 호출해야만 했던 것을 MyFragment.newInstance()로 호출할 수 있게 되는 것이다:
class MyClass {
companion object {
val a = 1
}
}
class MyClassWithJvmStatic {
companion object {
@JvmStatic
val b = 2
}
}
public final class MyClass {
private static final int a = 1;
@NotNull
public static final MyClass.Companion Companion = new MyClass.Companion((DefaultConstructorMarker)null);
@Metadata(
mv = {1, 7, 1},
k = 1,
d1 = {"\\u0000\\u0014\\n\\u0002\\u0018\\u0002\\n\\u0002\\u0010\\u0000\\n\\u0002\\b\\u0002\\n\\u0002\\u0010\\b\\n\\u0002\\b\\u0003\\b\\u0086\\u0003\\u0018\\u00002\\u00020\\u0001B\\u0007\\b\\u0002¢\\u0006\\u0002\\u0010\\u0002R\\u0014\\u0010\\u0003\\u001a\\u00020\\u0004X\\u0086D¢\\u0006\\b\\n\\u0000\\u001a\\u0004\\b\\u0005\\u0010\\u0006¨\\u0006\\u0007"},
d2 = {"Lcom/hanbikan/core/database/entity/MyClass$Companion;", "", "()V", "a", "", "getA", "()I", "core-database_debug"}
)
public static final class Companion {
public final int getA() {
return MyClass.a;
}
private Companion() {
}
// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
public final class MyClassWithJvmStatic {
private static final int b = 2;
@NotNull
public static final MyClassWithJvmStatic.Companion Companion = new MyClassWithJvmStatic.Companion((DefaultConstructorMarker)null);
public static final int getB() {
MyClassWithJvmStatic.Companion var10000 = Companion;
return b;
}
@Metadata(
mv = {1, 7, 1},
k = 1,
d1 = {"\\u0000\\u0014\\n\\u0002\\u0018\\u0002\\n\\u0002\\u0010\\u0000\\n\\u0002\\b\\u0002\\n\\u0002\\u0010\\b\\n\\u0002\\b\\u0004\\b\\u0086\\u0003\\u0018\\u00002\\u00020\\u0001B\\u0007\\b\\u0002¢\\u0006\\u0002\\u0010\\u0002R\\u001c\\u0010\\u0003\\u001a\\u00020\\u00048\\u0006X\\u0087D¢\\u0006\\u000e\\n\\u0000\\u0012\\u0004\\b\\u0005\\u0010\\u0002\\u001a\\u0004\\b\\u0006\\u0010\\u0007¨\\u0006\\b"},
d2 = {"Lcom/hanbikan/core/database/entity/MyClassWithJvmStatic$Companion;", "", "()V", "b", "", "getB$annotations", "getB", "()I", "core-database_debug"}
)
public static final class Companion {
/** @deprecated */
// $FF: synthetic method
@JvmStatic
public static void getB$annotations() {
}
public final int getB() {
return MyClassWithJvmStatic.b;
}
private Companion() {
}
// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
Semantic difference between object expressions and declarations
object expression과 object declaration의 의미상 차이는 다음과 같다:
- object expression은 코드에서 즉시 수행된다.(Object expressions are executed (and initialized) immediately, where they are used.)
- object declaration은 해당 오브젝트가 액세스될 때 lazily하게 초기화된다.(Object declarations are initialized lazily, when accessed for the first time.)
- companion object는 상응하는 클래스가 로드될 때 초기화된다.(A companion object is initialized when the corresponding class is loaded (resolved) that matches the semantics of a Java static initializer.)
*”클래스가 클래스 로더에 의해 로드되는 시점은 JVM(Java Virtual Machine)에서 클래스가 처음으로 참조될 때입니다." - GPT 4.0
'Kotlin' 카테고리의 다른 글
Kotlin Documentation | Delegated properties (0) | 2023.05.23 |
---|---|
Kotlin Documentation | Delegation (0) | 2023.05.23 |
Kotlin Documentation | Inline classes (0) | 2023.05.23 |
Kotlin Documentation | Enum classes (0) | 2023.05.23 |
Kotlin Documentation | Nested and inner classes (0) | 2023.05.23 |