try-with-resources: использование функции расширения в Kotlin не всегда работает

У меня возникли проблемы с выражением 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>

person Philipp Claßen    schedule 01.02.2016    source источник


Ответы (4)


Kotlin на данный момент нацелен на Java 6, поэтому его стандартная библиотека не использует интерфейс AutoCloseable. Функция use поддерживает только интерфейс Java 6 Closeable. См. средство отслеживания проблем для справки.

Вы можете создать копию функции 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()
        }
    }
}
person yole    schedule 01.02.2016
comment
Как указал mfulton26 в своем комментарии, kotlin.Throwable не содержит метода addSuppressed (Throwable), поэтому мы должны преобразовать kotlin.Throwable в java.lang.Throwable, чтобы код работал. stackoverflow.com/a/36260918/191975 - person orjan; 08.04.2016
comment
я считаю, что это работает @Suppress (PLATFORM_CLASS_MAPPED_TO_KOTLIN) (e как java.lang.Throwable) .addSuppressed (closeException) - person activedecay; 29.10.2016

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 }
person Quy Tang    schedule 07.04.2017
comment
kotlin-stdlib-jre8 устарел, заменен на kotlin-stdlib-jdk8 . Кроме того, AutoCloseable был введен в Java 7, поэтому версии jdk7 достаточно. Что касается того, почему эти библиотеки должны быть включены, Kotlin по умолчанию нацелен на Java 6. - person Minh Nghĩa; 01.01.2021

Для классов, которые не поддерживают функцию "использования", я сделал следующую самодельную попытку с ресурсами:

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)
    }
}
person Mario    schedule 28.03.2016

Просто убедитесь, что kotlin-stdlib-jdk7.jar в пути к классам, он не добавлен по умолчанию

person Constantine    schedule 16.04.2020