Преимущества сопутствующих объектов в возможности реализации интерфейсов

Почему в сопутствующих объектах Kotlin/Scala реализованы некоторые интерфейсы, какие преимущества это может иметь? Когда полезно использовать эту функцию?

3 ответа

Решение

Так как companion object с object s, object s может реализовывать интерфейсы (или расширять классы), и нет веских причин запрещать его companion object в частности.

Одно из распространенных применений в Scala - для фабрик: например, Seq, List, Vector и т.д. все сопутствующие объекты расширяются TraversableFactory так что вы можете написать код, работающий с TraversableFactory и передайте любой из них, чтобы создать тип, который вы хотите. Например

def build[CC[X] <: Traversable[X] with GenericTraversableTemplate[X, CC], A](factory: TraversableFactory[CC])(elems: A*) = factory(elems)

// build(List)(1,2,3) == List(1, 2, 3)
// build(Set)(1,2,3) == Set(1, 2, 3)

Аналогично, все сопутствующие объекты класса case расширяют типы функций.

Ты можешь использовать companion objects и наследование для некоторого уровня классового уровня или статического полиморфизма.

Пример 1: Фабрики

Рассмотрим интерфейс

interface Factory<T> {
    fun create(): T
}

Теперь мы создаем класс, чей сопутствующий объект реализует его

class Foo {
    companion object: Factory<Foo> {
        override fun create() = Foo()
    }
}

Теперь мы можем создать функцию расширения для всех фабрик, чтобы создать и, например, зарегистрировать объект.

fun <T> Factory<T>.createAndLog(): T {
    val t = create()
    println(t)
    return t
}

И использовать это так

Foo.createAndLog()

Пример 2: Запросы

Рассмотрим интерфейс маркера

interface Queryable<T>

Теперь у нас есть два класса User а также Article которые представляют таблицы в базе данных, чьи companion object реализует интерфейс.

class User(val id: String) {
    companion object: Queryable<User> {}
}

class Article(val authorId: String) {
    companion object: : Queryable<Article> {}
}

Теперь мы можем определить функцию расширения для создания запроса из класса

fun <T> Queryable<T>.query() = db.createQuery<T>()

который мы можем назвать как

User.query()
//or
Article.query()

Вы используете одноэлементный объект, когда вам нужен только один экземпляр определенного класса в программе.

Например, Скала Nil инвентарь List[Nothing], Вместо того, чтобы каждый тип списка для реализации его конкретного Nil есть только один из них.

В шаблоне типов, где у вас есть только один implicit object для соответствующей реализации.

Кроме того, где вы бы создать public static Something в Java вы бы создали это в сопутствующем объекте.

Я лично нашел их полезными для реализации плагина sbt, который рендерит object Blah extends Renderable к файлам blah.html, Узнайте больше о полезности здесь. Я должен был знать, что он реализует эту черту!

Другие вопросы по тегам