Почему в объектах компаньона Kotlin / Scala могут быть реализованы некоторые интерфейсы, какие преимущества это может иметь? Когда полезно использовать эту функцию?
Поскольку companion object
s является object
s, object
s может реализовывать интерфейсы (или расширять классы), и нет веских причин запретить его для companion object
s в частности.
Одно распространенное использование в 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)
Аналогично, все сопутствующие объекты класса класса расширяют типы функций.
Вы используете одноэлементный объект, если вам нужен только один экземпляр определенного класса в программе.
Например, Scala's Nil
реализует List[Nothing]
. Вместо каждого типа списка для реализации конкретного Nil
есть только один из них.
В шаблоне typeclass , где у вас есть только один implicit object
для соответствующей реализации.
Кроме того, если вы создадите public static Something
в Java, вы создадите это в сопутствующем объекте.
Я лично нашел их полезными для реализации плагина sbt, который отображает object Blah extends Renderable
в файлы blah.html
. Узнайте больше о полезности здесь . Я должен был знать, что он реализует эту черту!
Вы можете использовать companion object
s и наследование для некоторого уровня class-lavel или статического полиморфизма.
Рассмотрим интерфейс
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()
Рассмотрим интерфейс маркера
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()