在参加"Kotlin for Java Developers"Coursera课程时,我遇到了这种结构,其中包含实例的扩展函数似乎可以访问包含类的私有字段。请注意Data
类是如何在Container
外部实现的,并且无法访问Container
的字段,但Data.printMe()
扩展函数可以访问Container
的私有containerVal
成员:
data class Data (val data: String)
class Container (private val containerVal: String, val data: Data){
fun Data.printMe() {
println("data: $data - in container: ${this@Container.containerVal}")
}
}
fun main() {
val c = Container("mycontainer", Data("mydata"))
// Can I call Data.printMe without using `with`?
// val d = c.data.printMe();
with (c) {
// Prints: data: mydata - in container: mycontainer
data.printMe();
}
}
很酷的是,Data.printMe
可以访问Container
的私人成员。with(c)
显然是允许这样做的原因。我可以看到很多用例,就像在课程示例中,游戏Cell
的扩展函数可以访问包含Board
,而不会Cell
类本身受到Board
污染并与紧密耦合。
这种"方法"有名字吗?有没有一种语法可以在不使用with
的情况下"直接"调用c.data.printMe
?with 文档未提及此用例。
printMe()
方法有两个接收方:调度接收方和扩展接收方。调度接收器是在其中声明扩展的类的实例,扩展接收器是扩展方法的接收器类型的实例。您是在Container
实例(调度接收器)的上下文中访问contaiverVal
,而不是在Data
实例(扩展接收器)的上下文中访问。
如果您尝试做相反的操作并访问printMe()
内Data
类的私有属性,您将获得编译器错误。
将扩展函数定义为类的成员时,它只能在该类的上下文中使用。您可以在类中或使用 scope 函数访问它,其中上下文对象可用作 lambda 接收器 (this)。这些函数是run
、with
和apply
。