【问题标题】:try-with-resources: "use" extension function in Kotlin does not always worktry-with-resources:Kotlin 中的“使用”扩展功能并不总是有效
【发布时间】:2016-05-10 06:27:34
【问题描述】:

我在 Kotlin 中表达 Java 的 try-with-resources 构造时遇到了一些麻烦。在我的理解中,作为AutoClosable 实例的每个表达式都应该提供use 扩展函数。

这是一个完整的例子:

import java.io.BufferedReader;
import java.io.FileReader;

import org.openrdf.query.TupleQuery;
import org.openrdf.query.TupleQueryResult;

public class Test {

    static String foo(String path) throws Throwable {
        try (BufferedReader r =
           new BufferedReader(new FileReader(path))) {
          return "";
        }
    }

    static String bar(TupleQuery query) throws Throwable {
        try (TupleQueryResult r = query.evaluate()) {
          return "";
        }
    }
}

Java-to-Kotlin 转换器创建以下输出:

import java.io.BufferedReader
import java.io.FileReader

import org.openrdf.query.TupleQuery
import org.openrdf.query.TupleQueryResult

object Test {

    @Throws(Throwable::class)
    internal fun foo(path: String): String {
        BufferedReader(FileReader(path)).use { r -> return "" }
    }

    @Throws(Throwable::class)
    internal fun bar(query: TupleQuery): String {
        query.evaluate().use { r -> return "" } // ERROR
    }
}

foo 工作正常,但bar 中的代码无法编译:

Error:(16, 26) Kotlin: Unresolved reference.
None of the following candidates is applicable
because of receiver type mismatch: 
public inline fun <T : java.io.Closeable, R>
???.use(block: (???) -> ???): ??? defined in kotlin.io

query.evaluate() 来自Sesame 并实现AutoClosable。是 Kotlin 的错误,还是有原因导致它不起作用?


我将 IDEA 15.0.3 与 Kotlin 1.0.0-beta-4584-IJ143-12 和以下 sasame-runtime 版本一起使用:

<groupId>org.openrdf.sesame</groupId>
<artifactId>sesame-runtime</artifactId>
<version>4.0.2</version>

【问题讨论】:

    标签: java kotlin try-with-resources


    【解决方案1】:

    Kotlin 目前以 Java 6 为目标,因此其标准库不使用 AutoCloseable 接口。 use 函数仅支持 Java 6 Closeable 接口。参考the issue tracker

    您可以在项目中创建use 函数的副本并修改它以将Closeable 替换为AutoCloseable

    public inline fun <T : AutoCloseable, R> T.use(block: (T) -> R): R {
        var closed = false
        try {
            return block(this)
        } catch (e: Exception) {
            closed = true
            try {
                close()
            } catch (closeException: Exception) {
                e.addSuppressed(closeException)
            }
            throw e
        } finally {
            if (!closed) {
                close()
            }
        }
    }
    

    【讨论】:

    • 正如 mfulton26 在他的评论中指出的那样,kotlin.Throwable 不包含 addSuppressed(Throwable) 方法,因此我们必须将 kotlin.Throwable 转换为 java.lang.Throwable 才能使代码正常工作。 stackoverflow.com/a/36260918/191975
    • 我相信这有效 @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") (e as java.lang.Throwable).addSuppressed(closeException)
    【解决方案2】:

    对于不支持“使用”功能的类,我做了下一个自制的try-with-resources:

    inline fun <T:AutoCloseable,R> trywr(closeable: T, block: (T) -> R): R {
        try {
            return block(closeable);
        } finally {
            closeable.close()
        }
    }
    

    那你就可以用下一种方式了:

    fun countEvents(sc: EventSearchCriteria?): Long {
        return trywr(connection.prepareStatement("SELECT COUNT(*) FROM event")) {
            var rs = it.executeQuery()
            rs.next()
            rs.getLong(1)
        }
    }
    

    【讨论】:

      【解决方案3】:

      Kotlin 1.1+ 有一个以 Java 8 为目标的标准库以支持 Closeable resource 模式 - kotlin-stdlib-jre8

      Gradle

      compile "org.jetbrains.kotlin:kotlin-stdlib:1.1.1"
      compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:1.1.1"
      

      Maven

      <dependency>
          <groupId>org.jetbrains.kotlin</groupId>
          <artifactId>kotlin-stdlib</artifactId>
          <version>1.1.1</version>
      </dependency>
      <dependency>
          <groupId>org.jetbrains.kotlin</groupId>
          <artifactId>kotlin-stdlib-jre8</artifactId>
          <version>1.1.1</version>
      </dependency>
      

      示例

      val resource: AutoCloseable = getCloseableResource() 
      resource.use { r -> //play with r }
      

      【讨论】:

      • kotlin-stdlib-jre8 现在已弃用,取而代之的是 kotlin-stdlib-jdk8。此外,AutoCloseable 是在 Java 7 中引入的,因此 jdk7 版本就足够了。根据为什么必须包含这些库,Kotlin 默认以 Java 6 为目标。
      【解决方案4】:

      只要确保类路径中的 kotlin-stdlib-jdk7.jar,它默认没有添加

      【讨论】:

        猜你喜欢
        • 2011-12-14
        • 2011-06-18
        • 2017-05-11
        • 1970-01-01
        • 1970-01-01
        • 2021-08-02
        • 1970-01-01
        • 1970-01-01
        • 2012-06-21
        相关资源
        最近更新 更多