1.1.50.18. fejezet, Objektum kifejezések és deklarációk
Néha létre kell hoznia egy olyan objektumot, amely egy osztály enyhe módosítása, anélkül, hogy kifejezetten új alosztályt deklarálna. A Kotlin ezt objektumkifejezésekkel és objektumdeklarációkkal tudja kezelni.
Anonim objektumok az alapoktól
val helloWorld = object { val hello = "Hello" val world = "World" // object expressions extend Any, so `override` is required on `toString()` override fun toString() = "$hello $world" } print(helloWorld)
Névtelen objektumok öröklése szupertípusokból
window.addMouseListener(object : MouseAdapter() { override fun mouseClicked(e: MouseEvent) { /*...*/ } override fun mouseEntered(e: MouseEvent) { /*...*/ } })
Ha egy szupertípusnak van konstruktora, adjuk át neki a megfelelő konstruktor paramétereket. Több szupertípus is megadható vesszővel tagolt listaként a kettőspont után.
open class A(x: Int) { public open val y: Int = x } interface B { /*...*/ } val ab: A = object : A(1), B { override val y = 15 }
Névtelen objektumok használata visszatérési és értéktípusként
class C { private fun getObject() = object { val x: String = "x" } fun printX() { println(getObject().x) } }
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" } }
Változók elérése névtelen objektumokból
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++ } }) // ... }
Objektum deklarációk
A Singleton minta számos esetben hasznos lehet, és a Kotlin megkönnyíti a szingulárisok deklarálását.
object DataProviderManager { fun registerDataProvider(provider: DataProvider) { // ... } val allDataProviders: Collection<DataProvider> get() = // ... }
Ezt objektumdeklarációnak nevezzük, és mindig van egy neve az object kulcsszó után. A változódeklarációhoz hasonlóan az objektumdeklaráció sem kifejezés, és nem használható a hozzárendelési utasítás jobb oldalán. Az objektumdeklaráció inicializálása szálbiztos, és az első hozzáféréskor történik.
Az objektumra hivatkozás így néz ki:
DataProviderManager.registerDataProvider(...)
Ezeknek az objektumoknak is lehet szupertípusa:
object DefaultListener : MouseAdapter() { override fun mouseClicked(e: MouseEvent) { ... } override fun mouseEntered(e: MouseEvent) { ... } }
Adat objektumok
data object MyDataObject { val x: Int = 3 } fun main() { println(MyDataObject) // MyDataObject }
Ezeknél az objektumoknál sose használjuk a referencia szerinti összehasonlítás operátort (===). Ez segít elkerülni a buktatókat, ha egy adatobjektum egynél több példánya létezik futásidőben.
import java.lang.reflect.Constructor data object MySingleton fun main() { val evilTwin = createInstanceViaReflection() println(MySingleton) // MySingleton println(evilTwin) // MySingleton // Even when a library forcefully creates a second instance of MySingleton, its `equals` method returns true: println(MySingleton == evilTwin) // true // Do not compare data objects via ===. println(MySingleton === evilTwin) // false } fun createInstanceViaReflection(): MySingleton { // Kotlin reflection does not permit the instantiation of data objects. // This creates a new MySingleton instance "by force" (i.e. Java platform reflection) // Don't do this yourself! return (MySingleton.javaClass.declaredConstructors[0].apply { isAccessible = true } as Constructor<MySingleton>).newInstance() }
Adatobjektumok használata lezárt hierarchiával
sealed interface ReadResult data class Number(val number: Int) : ReadResult data class Text(val text: String) : ReadResult data object EndOfFile : ReadResult fun main() { println(Number(7)) // Number(number=7) println(EndOfFile) // EndOfFile }
Kísérő objektumok
Java static tulajdonság megfelelője, példányosított osztály változója.
class MyClass { companion object Factory { fun create(): MyClass = MyClass() } } val instance = MyClass.create()
A kísérő objektum neve elhagyható, ebben az esetben a Compation név kerül felhasználásra:
class MyClass { companion object { } } val x = MyClass.Companion
Az osztály tagjai hozzáférhetnek a megfelelő társobjektum privát tagjaihoz.
Egy osztály neve önmagában (nem egy másik név minősítőjeként) az osztály társobjektumára való hivatkozásként szolgál (függetlenül attól, hogy megnevezett-e vagy sem):
class MyClass1 { companion object Named { } } val x = MyClass1 class MyClass2 { companion object { } } val y = MyClass2
Vegye figyelembe, hogy bár a társobjektumok tagjai statikus tagoknak tűnnek más nyelvekben, futásidőben ezek továbbra is valós objektumok példánytagjai, és például interfészeket valósíthatnak meg:
interface Factory<T> { fun create(): T } class MyClass { companion object : Factory<MyClass> { override fun create(): MyClass = MyClass() } } val f: Factory<MyClass> = MyClass
Van három fontos szemantikai különbség az objektumkifejezések és az objektumdeklarációk között:
- Az objektumkifejezések végrehajtása (és inicializálása) azonnal megtörténik, ahol használatban vannak.
- Az objektumdeklarációk inicializálása lazy módon történik, amikor először férnek hozzá.
- A társobjektum inicializálása akkor történik, amikor a megfelelő osztály betöltődik (fel van oldva), amely megfelel a Java statikus inicializáló szemantikájának.
- A hozzászóláshoz be kell jelentkezni