Swift的defer关键字的Kotlin等价物

8yparm6h  于 2023-02-09  发布在  Kotlin
关注(0)|答案(5)|浏览(159)

在Kotlin中有没有类似的东西可以提供和斯威夫特关键字“推迟”一样的能力?
defer关键字的作用是,它确保defer块中的代码在从函数返回之前得到执行。
下面是一个假设Kotlin中存在defer关键字的示例。

class MyClass {

        var timeStamp = 0L

        fun isEdible(fruit: Fruit): Boolean {
          defer { 
           timeStamp = System.currentTimeMillis()
          }
          if (fruit.isExpired) {
             return false
          }
          if (fruit.isRipe) {
            return true
          }
          return false
        }
     }

在上面的例子中,无论函数在什么点返回,defer中的块都会被执行,timestamp的值也会被更新,就在函数结束之前。
我知道Java中有finally {}关键字与try{} catch{}沿着使用,但这并不是defer所提供的。

7cjasjjr

7cjasjjr1#

在Kotlin中没有这样的关键字,但是你可以自己构造一个类似的结构,类似于下面这样(注意这并不处理延迟块中的异常):

class Deferrable {
    private val actions: MutableList<() -> Unit> = mutableListOf()

    fun defer(f: () -> Unit) {
        actions.add(f)
    }

    fun execute() {
        actions.forEach { it() }
    }
}

fun <T> defer(f: (Deferrable) -> T): T {
    val deferrable = Deferrable()
    try {
        return f(deferrable)
    } finally {
        deferrable.execute()
    }
}

然后,您可以像这样使用它:

class MyClass {

    var timeStamp = 0L

    fun isEdible(fruit: Fruit): Boolean = defer { d ->
      d.defer { 
       timeStamp = System.currentTimeMillis()
      }
      if (fruit.isExpired) {
         return false
      }
      if (fruit.isRipe) {
        return true
      }
      return false
    }
}
ebdffaop

ebdffaop2#

最接近的等价物是try/finally。如果没有抛出异常,则不需要catch

try {
    println("do something")
    // ... the rest of your method body here
}
finally {
    println("Don't forget about me!");
}

在Swift中,defer通常用来确保你不会忘记清理某种资源(文件句柄、数据库连接、共享内存Map等)。为此,Kotlin使用with,它接受一个闭包,资源作为参数传递给闭包。资源在闭包的生命周期内有效,并在结束时自动关闭。

FileWriter("test.txt")
  .use { it.write("something") }
// File is closed by now
bxgwgixi

bxgwgixi3#

包含异常处理的解决方案:

class DeferContext {
    private val list = mutableListOf<() -> Unit>()

    fun defer(payload: () -> Unit) {
        list += payload
    }

    /** lombok `@Cleanup` analog */
    fun AutoCloseable.deferClose() = apply {
        defer { close() }
    }

    fun executeDeferred(blockError: Throwable?) {
        var error: Throwable? = blockError
        for (element in list.reversed()) {
            try {
                element()
            } catch (e: Throwable) {
                if (error == null) {
                    error = e
                } else {
                    error.addSuppressed(e)
                }
            }
        }
        error?.let { throw it }
    }
}

inline fun <T> deferBlock(payload: DeferContext.() -> T): T {
    val context = DeferContext()
    var error: Throwable? = null
    var result: T? = null
    try {
        result = context.payload()
    } catch (e: Throwable) {
        error = e
    } finally {
        context.executeDeferred(error)
    }
    return result as T
}

IMHO,defer功能的要点是执行延迟的操作,而不管以前抛出的异常。
用法:

deferBlock {
    defer { println("block exited") }
    val stream = FileInputStream("/tmp/a").deferClose()
}
soat7uwm

soat7uwm4#

我今天遇到了同样的问题。
虽然我认为marstran提供的答案是好的,但我决定稍微重构一下。

fun <T> deferred(f: ((() -> Unit) -> Unit) -> T): T {
    val actions: MutableList<() -> Unit> = mutableListOf()
    try {
        return f(actions::add)
    } finally {
        actions.asReversed().forEach { it() }
    }
}

我通过直接在deffered函数中使用列表来摆脱Deferrable类。这也解决了整个Deferrable对象被传递给需要调用it.defer/d.defer的调用代码的问题。在这个版本中,可变列表的add方法被直接传递给lambda,从而允许代码更接近它的go/快速版本。
为了解决mvndaai提出的使用Stack的建议,我决定在列表中调用.asReversed()。也许Kotlin中有一个LI-FO类型,在非JVM变体中也可用,但如果没有,我认为这是一个很好的解决方案。
给定的样本将看起来像:

class MyClass {

    var timeStamp = 0L

    fun isEdible(fruit: Fruit): Boolean = deferred { defer ->
      defer { 
       timeStamp = System.currentTimeMillis()
      }
      if (fruit.isExpired) {
         return false
      }
      if (fruit.isRipe) {
        return true
      }
      return false
    }
}
z18hc3ub

z18hc3ub5#

如果类是Closeable,则可以使用use block

class MyClass : Closeable {
        var timeStamp = 0L

        override fun close() {
            timeStamp = System.currentTimeMillis()
        }

        fun test(): Boolean {
            this.use {
                if (fruit.isExpired) {
                    return false
                }
                if (fruit.isRipe) {
                    return true
                }
                return false
            }
        }
    }

相关问题