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

查看:259
本文介绍了在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?

注意: 该问题是作者故意写并回答的(

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天全站免登陆