在Kotlin中,如何将扩展方法添加到另一个类中,但仅在特定上下文中可见? [英] In Kotlin, how do I add extension methods to another class, but only visible in a certain context?
问题描述
在Kotlin中,我想向类添加扩展方法,例如,向类Entity
添加扩展方法.但是,我只想在Entity
在事务内(否则隐藏)时查看这些扩展.例如,如果我定义了这些类和扩展名:
In Kotlin, I want to add extension methods to a class, for example to class Entity
. But I only want to see these extensions when Entity
is within a transaction, otherwise hidden. For example, if I define these classes and extensions:
interface Entity {}
fun Entity.save() {}
fun Entity.delete() {}
class Transaction {
fun start() {}
fun commit() {}
fun rollback() {}
}
我现在可以随时意外地调用save()
和delete()
,但是我只希望它们在事务的start()
之后可用,而在commit()
或rollback()
之后不再可用?目前,我可以这样做,这是错误的:
I now can accidentally call save()
and delete()
at any time, but I only want them available after the start()
of a transaction and no longer after commit()
or rollback()
? Currently I can do this, which is wrong:
someEntity.save() // DO NOT WANT TO ALLOW HERE
val tx = Transaction()
tx.start()
someEntity.save() // YES, ALLOW
tx.commit()
someEntity.delete() // DO NOT WANT TO ALLOW HERE
如何使它们在正确的上下文中显示和消失?
How do I make them appear and disappear in the correct context?
Note: this question is intentionally written and answered by the author (Self-Answered Questions), so that the idiomatic answers to commonly asked Kotlin topics are present in SO. Also to clarify some really old answers written for alphas of Kotlin that are not accurate for current-day Kotlin. Other answers are also welcome, there are many styles of how to answer this!
推荐答案
基础知识:
在Kotlin中,我们倾向于使用传递给其他类的lambda赋予它们作用域",或在执行lambda之前和之后发生行为,包括错误处理.因此,您首先需要更改Transaction
的代码以提供范围.这是修改后的Transaction
类:
The Basics:
In Kotlin, we tend to use lambdas passed into other classes to give them "scope" or to have behaviour that happens before and after the lambda is executed, including error handling. Therefore you first need to change the code for Transaction
to provide scope. Here is a modified Transaction
class:
class Transaction(withinTx: Transaction.() -> Unit) {
init {
start()
try {
// now call the user code, scoped to this transaction class
this.withinTx()
commit()
}
catch (ex: Throwable) {
rollback()
throw ex
}
}
private fun Transaction.start() { ... }
fun Entity.save(tx: Transaction) { ... }
fun Entity.delete(tx: Transaction) { ... }
fun Transaction.save(entity: Entity) { entity.save(this) }
fun Transaction.delete(entity: Entity) { entity.delete(this) }
fun Transaction.commit() { ... }
fun Transaction.rollback() { ... }
}
在这里,我们有一个事务,该事务在创建时需要一个lambda来执行事务内的处理,如果未引发异常,它将自动提交事务. (Transaction
类的构造函数的作用类似于高阶函数)
Here we have a transaction that when created, requires a lambda that does the processing within the transaction, if no exception is thrown it auto commits the transaction. (The constructor of the Transaction
class is acting like a Higher-Order Function)
我们还将Entity
的扩展功能移到了Transaction
之内,因此,如果不在此类的上下文中,这些扩展功能将不可见或不可调用.这包括commit()
和rollback()
的方法,这些方法现在只能从类本身内部调用,因为它们现在是在类范围内的扩展函数.
We have also moved the extension functions for Entity
to be within Transaction
so that these extension functions will not be seen nor callable without being in the context of this class. This includes the methods of commit()
and rollback()
which can only be called now from within the class itself because they are now extension functions scoped within the class.
由于接收到的lambda是Transaction
的扩展函数,因此它在该类的上下文中运行,因此可以看到扩展. (请参阅:带有接收器的函数文字)
Since the lambda being received is an extension function to Transaction
it operates in the context of that class, and therefore sees the extensions. (see: Function Literals with Receiver)
此旧代码现在无效,编译器给我们一个错误:
This old code is now invalid, with the compiler giving us an error:
fun changePerson(person: Person) {
person.name = "Fred"
person.save() // ERROR: unresolved reference: save()
}
现在,您将编写代码而不是存在于Transaction
块中:
And now you would write the code instead to exist within a Transaction
block:
fun actsInMovie(actor: Person, film: Movie) {
Transaction { // optional parenthesis omitted
if (actor.winsAwards()) {
film.addActor(actor)
save(film)
} else {
rollback()
}
}
}
由于传入的lambda没有正式声明,因此被推断为Transaction
上的扩展函数.
The lambda being passed in is inferred to be an extension function on Transaction
since it has no formal declaration.
要将一系列动作"链接到一个事务中,只需创建一系列可在事务中使用的扩展功能,例如:
To chain a bunch of these "actions" together within a transaction, just create a series of extension functions that can be used within a transaction, for example:
fun Transaction.actsInMovie(actor: Person, film: Movie) {
film.addActor(actor)
save(film)
}
创建更多类似的内容,然后在传递给交易的lambda中使用它们...
Create more like this, and then use them in the lambda passed to the Transaction...
Transaction {
actsInMovie(harrison, starWars)
actsInMovie(carrie, starWars)
directsMovie(abrams, starWars)
rateMovie(starWars, 5)
}
现在回到原始问题,我们有事务处理方法和实体方法仅在正确的时间出现.作为使用lambda或匿名函数的副作用,我们最终探索了有关代码组成的新想法.
Now back to the original question, we have the transaction methods and the entity methods only appearing at the correct moments in time. And as a side effect of using lambdas or anonymous functions is that we end up exploring new ideas about how our code is composed.
这篇关于在Kotlin中,如何将扩展方法添加到另一个类中,但仅在特定上下文中可见?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!