伴随对象实现接口的可能性的好处

Companion objects benefits of posibility to implement interfaces

为什么在Kotlin/Scala companion objects 中可以实现一些接口,这有什么好处?什么时候使用这个功能有用?

当您在程序中只需要特定 class 的一个实例时,您可以使用单例对象。

例如Scala 的 Nil 实现 List[Nothing]。不是每种类型的列表都需要实现 它是特殊的 Nil 只有其中一个。

typeclass pattern中,您只有一个implicit object用于相应的实现。

此外,在 Java 中创建 public static Something 的地方,您会在伴随对象中创建它。

我个人发现它们对于实现 sbt 插件很有用,该插件将 object Blah extends Renderable 渲染到文件 blah.html。找到更多关于实用性的信息 here。我 必须知道它实现了 这个特征!

因为companion objects是objects,objects可以实现接口(或扩展classes),没有充分的理由禁止它特别是 companion objects。

Scala 中的一个常见用途是工厂:例如SeqListVector 等伴随对象都扩展了 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 class 伴随对象都扩展了函数类型。

您可以使用 companion objects 和继承来实现某种级别的 class-lavel 或静态多态性。

示例 1:工厂

考虑一个接口

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

现在,我们创建一个 class 其伴随对象实现它

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>

我们现在有两个 classes UserArticle 表示数据库中的表,其 companion object 实现了接口。

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

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

我们现在可以定义一个扩展函数来从 class

创建一个查询
fun <T> Queryable<T>.query() = db.createQuery<T>()

我们可以称之为

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