在 Kotlin 中,如何将扩展方法添加到另一个类,但仅在特定上下文中可见? [英] In Kotlin, how do I add extension methods to another class, but only visible in a certain context?

查看:17
本文介绍了在 Kotlin 中,如何将扩展方法添加到另一个类,但仅在特定上下文中可见?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 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?

注意:这个问题是作者有意编写和回答的(Self-Answered Questions),以便常见问题的 Kotlin 主题的惯用答案出现在 SO 中.还要澄清一些为 Kotlin alphas 编写的非常旧的答案,这些答案对于当前的 Kotlin 来说是不准确的.也欢迎其他答案,有多种回答方式!

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屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆