Kotlin вложени и вътрешни класове

1. Въведение

В този урок ще разгледаме четири начина за създаване на вложени и вътрешни класове в Kotlin.

2. Бързо сравнение с Java

За тези, които мислят за вложени класове Java, нека направим кратко преразглеждане на свързани термини:

Котлин Java
Вътрешни класове Нестатични вложени класове
Местни класове Местни класове
Анонимни обекти Анонимни класове
Вложени класове Статични вложени класове

Макар със сигурност да не е идентична, можем да използваме тази таблица като ориентир, когато мислим за възможностите и случаите на използване за всяка.

3. Вътрешни класове

Първо, можем да декларираме клас в друг клас, използвайки ключовата дума inner .

Тези класове имат достъп до членове на затварящия клас, дори частни членове .

За да го използваме, първо трябва да създадем екземпляр на външния клас; не можем да използваме вътрешни класове без него.

Нека създадем вътрешен клас HardDisk вътре в компютърния клас:

class Computer(val model: String) { inner class HardDisk(val sizeInGb: Int) { fun getInfo() = "Installed on ${[email protected]} with $sizeInGb GB" } }

Имайте предвид, че използваме квалифициран този израз за достъп до членове на класа Computer , който е подобен на този, когато правим Computer.this в Java еквивалента на HardDisk .

Сега нека го видим в действие:

@Test fun givenHardDisk_whenGetInfo_thenGetComputerModelAndDiskSizeInGb() { val hardDisk = Computer("Desktop").HardDisk(1000) assertThat(hardDisk.getInfo()) .isEqualTo("Installed on Computer(model=Desktop) with 1000 GB") }

4. Местни вътрешни класове

След това можем да дефинираме клас в тялото на метода или в блока на обхвата .

Нека направим бърз пример, за да видим как работи.

Първо, нека дефинираме метод на powerOn за нашия клас Computer :

fun powerOn(): String { //... }

Вътре в метода powerOn нека декларираме Led клас и го накараме да мига:

fun powerOn(): String { class Led(val color: String) { fun blink(): String { return "blinking $color" } } val powerLed = Led("Green") return powerLed.blink() }

Обърнете внимание, че обхватът на класа Led е само вътре в метода.

С локалните вътрешни класове можем да имаме достъп и да променяме променливи, декларирани във външния обхват . Нека добавим defaultColor в метода powerOn :

fun powerOn(): String { var defaultColor = "Blue" //... } 

Сега, нека добавим changeDefaultPowerOnColor в нашия Led клас:

class Led(val color: String) { //... fun changeDefaultPowerOnColor() { defaultColor = "Violet" } } val powerLed = Led("Green") log.debug("defaultColor is $defaultColor") powerLed.changeDefaultPowerOnColor() log.debug("defaultColor changed inside Led " + "class to $defaultColor")

Кои изходи:

[main] DEBUG c.b.n.Computer - defaultColor is Blue [main] DEBUG c.b.n.Computer - defaultColor changed inside Led class to Violet

5. Анонимни обекти

Анонимните обекти могат да се използват за дефиниране на изпълнение на интерфейс или абстрактен клас, без да се създава многократна реализация .

Голяма разлика между анонимните обекти в Kotlin и анонимните вътрешни класове в Java е, че анонимните обекти могат да реализират множество интерфейси и методи.

Първо, нека добавим интерфейс Switcher в нашия клас Computer :

interface Switcher { fun on(): String }

Сега, нека добавим реализация на този интерфейс в метода powerOn :

fun powerOn(): String { //... val powerSwitch = object : Switcher { override fun on(): String { return powerLed.blink() } } return powerSwitch.on() }

Както виждаме, за да дефинираме нашия анонимен обект powerSwitch , използваме израз на обект. Също така трябва да вземем предвид, че всеки път, когато се извика обектният израз, се създава нов екземпляр на обекта.

С анонимни обекти като вътрешни класове можем да модифицираме променливи, предварително декларирани в обхвата. Това е така, защото Kotlin няма ефективното окончателно ограничение, което сме очаквали в Java.

Сега, нека добавим changeDefaultPowerOnColor в нашия обект PowerSwitch и го извикаме:

val powerSwitch = object : Switcher { //... fun changeDefaultPowerOnColor() { defaultColor = "Yellow" } } powerSwitch.changeDefaultPowerOnColor() log.debug("defaultColor changed inside powerSwitch " + "anonymous object to $defaultColor")

Ще видим резултат като този:

... [main] DEBUG c.b.n.Computer - defaultColor changed inside powerSwitch anonymous object to Yellow

Също така имайте предвид, че ако нашият обект е екземпляр на интерфейс или клас с един абстрактен метод; можем да го създадем с помощта на ламбда израз.

6. Вложени класове

И последно, можем да дефинираме клас в друг клас без ключовата дума inner :

class Computer(val model: String) { class MotherBoard(val manufacturer: String) }

In this type of class, we don't have access to the outer class instance. But, we can access companion object members of the enclosing class.

So, let's define a companion object inside our Computer class to see it:

companion object { const val originCountry = "China" fun getBuiltDate(): String { return "2018-07-15T01:44:25.38Z" } }

And then a method inside MotherBoard to get information about it and the outer class:

fun getInfo() = "Made by $manufacturer - $originCountry - ${getBuiltDate()}"

Now, we can test it to see how it works:

@Test fun givenMotherboard_whenGetInfo_thenGetInstalledAndBuiltDetails() { val motherBoard = Computer.MotherBoard("MotherBoard Inc.") assertThat(motherBoard.getInfo()) .isEqualTo( "Made by MotherBoard Inc. installed in China - 2018-05-23") }

As we can see, we create motherBoard without an instance of Computer class.

7. Conclusion

In this article, we’ve seen how to define and use nested and inner classes in Kotlin to make our code more concise and encapsulated.

Also, we've seen some similarities to the corresponding Java concepts.

Напълно работещ пример за този урок можете да намерите в GitHub.