3

为什么在 Kotlin/Scala 中伴随对象可以实现一些接口,这有什么好处?什么时候使用此功能有用?

4

3 回答 3

3

您可以将companion objects 和继承用于某种级别的 class-lavel 或静态多态性。

示例 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>

我们现在有两个类UserArticle它们代表数据库中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()
于 2017-12-04T15:19:15.320 回答
3

因为companion objects 是objects,所以objects 可以实现接口(或扩展类),并且没有充分的理由companion object特别禁止 s 使用它。

Scala 中的一个常见用途是工厂:例如Seq,ListVector伴随对象都扩展TraversableFactory,因此您可以编写使用 a 的代码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)

同样,所有案例类伴随对象都扩展了函数类型。

于 2017-12-04T12:35:02.990 回答
1

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

例如 Scala 的Nil工具List[Nothing]。而不是每种类型的列表来实现它都是特别 Nil的,只有其中一个。

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

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

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

于 2017-12-04T10:22:25.410 回答