Создание анонимного класса от абстрактного в Kotlin
Быстрый ответ
Для инициализации экземпляра абстрактного класса в Kotlin используется объектное выражение:
abstract class AbstractFoo {
abstract fun bar()
}
val myObject = object : AbstractFoo() {
override fun bar() {
println("Действие дня: Bar!")
}
}
В приведенном примере мы переопределяем абстрактный метод, оформляя его реализацию с помощью фигурных скобок, так создается анонимный класс.
Разбор объектных выражений
В каких случаях стоит использовать объектное выражение
Опирайтесь на объектные выражения тогда, когда вам нужен экземпляр класса с некоторыми модификациями для уникальных случаев. Это подходит для абстрактных классов или интерфейсов, когда нет желания создавать полноценный подкласс.
Отличие Kotlin от Java
В Java для управления событиями в пользовательском интерфейсе часто используют анонимные классы. В Kotlin вместо этого используется слово object
, что делает код более лаконичным:
// Подход Java
Window.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
System.out.println("Я Java, и меня кликнули.");
}
});
// Подход Kotlin
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
println("Я Kotlin. Классный клик!")
}
})
Kotlin отсекает излишние детали, упрощая чтение кода.
Преобразование SAM (Single Abstract Method)
Для функциональных интерфейсов с единственным методом Kotlin предлагает возможность SAM-преобразования:
// Без SAM-преобразования
val runnable = object : Runnable {
override fun run() {
println("Бегите, как будто за финишной чертой вас ждёт приз!")
}
}
// С SAM-преобразованием
val runnable = Runnable { println("Я бегу легко и непринуждённо, благодаря SAM!") }
Однако стоит отметить, что SAM-преобразование доступно только для интерфейсов Java, а не для тех, что определены в Kotlin.
Продвинутые сценарии
Реализация нескольких интерфейсов
В Kotlin вы можете реализовать несколько интерфейсов одновременно при помощи одного анонимного класса:
interface A {
fun doSomethingA()
}
interface B {
fun doSomethingB()
}
val myObject = object : A, B {
override fun doSomethingA() {
println("Я занимаюсь делом A!")
}
override fun doSomethingB() {
println("И занимаюсь делом B тоже!")
}
}
Наследование и интерфейсы вместе
В Kotlin один анонимный класс может расширить существующий класс и реализовать интерфейс:
open class C {
open fun foo() {}
}
interface D {
fun bar()
}
val myObject = object : C(), D {
override fun foo() {
println("Переопределяю foo из C. Не волнуйтесь, всё будет рифмоваться с moo.")
}
override fun bar() {
println("И реализую bar из D. И это не шутка.")
}
}
Переопределение свойств
В анонимных классах Kotlin также разрешается переопределение свойств, что позволяет индивидуализировать экземпляр:
abstract class Vehicle {
abstract val numberOfWheels: Int
abstract fun ride()
}
val bike = object : Vehicle() {
override val numberOfWheels = 2
override fun ride() {
println("Еду на $numberOfWheels колёсах, но это не велосипед!")
}
}
Визуализация
Создание анонимного класса на основе абстрактного класса в Kotlin можно сравнить с подготовкой импровизированного представления:
// Ваш абстрактный шаблон класса:
abstract class AbstractActor {
abstract fun perform()
}
// Ваш уникальный сценарий:
val actor = object : AbstractActor() {
override fun perform() {
// Проявите свой талант!
}
}
AbstractActor
— это ваша творческая площадка. С помощью perform
вы становитесь звездой своего уникального шоу.
Будьте внимательны!
SAM-преобразование и интерфейсы Kotlin
Распространенная ошибка — это применение SAM-преобразования к интерфейсам Kotlin. Это невозможно без использования функциональных типов или inline-классов.
Inline-функции и объектные выражения
В inline-функциях нельзя использовать объектные выражения, если они не реализуют fun
-интерфейс:
inline fun inlineFoo(crossinline action: () -> Unit) {
val myObject = object : Runnable {
override fun run() = action()
}
// Недопустимо: литеральные выражения объектов запрещены в inline-функциях
}
Лямбды могут служить альтернативой для обхода этого ограничения.
Инициализация свойств в объектных выражениях
Свойства в объектном выражении инициализируются при создании экземпляра, поэтому требуется внимательность.
abstract class SomeAbstractClass {
abstract val someProperty: String
}
val myObject = object : SomeAbstractClass() {
override val someProperty = computeExpensiveValue()
// Внимание: инициализация происходит при создании экземпляра
}
Полезные материалы
- Официальная документация Kotlin – подробное описание использования объектных выражений и анонимных классов.
- Статья на Medium – содержит ценные советы по переводу кода из Java в Kotlin.
- Kotlin Playground – обучение Kotlin на практических примерах.
- Baeldung – вводное руководство по анонимным внутренним классам в Kotlin.
- Руководство для разработчиков Android – полный материал о Kotlin в контексте Android.
- Kodeco – для тех, кто стремится углубить свои знания Kotlin.