为什么在 Kotlin/Scala 中伴随对象可以实现一些接口,这有什么好处?什么时候使用此功能有用?
3 回答
您可以将companion object
s 和继承用于某种级别的 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>
我们现在有两个类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()
因为companion object
s 是object
s,所以object
s 可以实现接口(或扩展类),并且没有充分的理由companion object
特别禁止 s 使用它。
Scala 中的一个常见用途是工厂:例如Seq
,List
等Vector
伴随对象都扩展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)
同样,所有案例类伴随对象都扩展了函数类型。
当您在程序中只需要一个特定类的实例时,您可以使用单例对象。
例如 Scala 的Nil
工具List[Nothing]
。而不是每种类型的列表来实现它都是特别 Nil
的,只有其中一个。
在typeclass pattern中,您只有一个implicit object
用于相应的实现。
此外,public static Something
在 Java 中创建 a 的地方,您将在伴随对象中创建它。
我个人发现它们对于实现渲染object Blah extends Renderable
到文件的 sbt 插件很有用blah.html
。在这里找到更多关于有用性的信息。我必须知道它实现了这个特性!