Преимущества сопутствующих объектов в возможности реализации интерфейсов
Почему в сопутствующих объектах 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 object
s и наследование для некоторого уровня классового уровня или статического полиморфизма.
Пример 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
, Узнайте больше о полезности здесь. Я должен был знать, что он реализует эту черту!