I'm trying to convert a Java method:
private <T> Callable<T> createCallable(final Callable<T> task) {
return () -> {
try {
return task.call();
} catch (Exception e) {
handle(e);
throw e;
}
};
}
from the following Java file ExceptionHandlingAsyncTaskExecutor.java into Kotlin.
The code gets converted automatically using IntelliJ IDEA into:
private fun <T> createCallable(task: Callable<T>): Callable<T> {
return {
try {
return task.call()
} catch (e: Exception) {
handle(e)
throw e
}
}
}
which is not correct. But I have to idea what the correct implementation for this should be. Any ideas?
I think this is an Kotlin converter bug. It converted your code to () -> T instead of Callable<T> (which is basically the same but these are actually different types).
This is the working code
private fun <T> createCallable(task: Callable<T>): Callable<T> {
return Callable {
try {
task.call()
} catch (e: Exception) {
handle(e)
throw e
}
}
}
This is how I did it, might be too verbose, but it works. I also implement a handle function.
import java.util.concurrent.*
private fun <T> createCallable(task: Callable<T>): Callable<T> {
return object : Callable<T> {
override fun call(): T {
try {
return task.call()
} catch (e: Exception) {
handle(e)
throw e
}
}
}
}
private fun handle(e: Exception): Unit { println("got exception") }
And this how I call it in a test...
fun main(vararg argv: String): Unit {
val callable1 = object : Callable<Int> {
override fun call(): Int = 1
}
val c1 = createCallable(callable1)
println("callable1 = ${c1.call()}")
val callable2 = object : Callable<Unit> {
override fun call(): Unit { println("Hello"); throw Exception("Hello") }
}
val c2 = createCallable(callable2)
c2.call()
}
Related
using mockk 1.9.3, junit 4
having a function which will report the exceptions for different conditions, need to test and verify the correct exception is reported.
class NetworkApi {
fun actionAndLogExceptions(appContext: Context, optionalParams: Map<String, String>) {
try {
val result = doNetWorkCall(optionalParams)
when (result) {
TIMEOUT -> {throw SocketTimeoutException(...)}
NETWORKERROR -> {throw HttpConnectionException(...)}
JSON_EROOR -> {throw JSONException(...)}
OK -> { handleResponce()}
}
} catch (ex: Throwable) {
System.out.println("+++ !!! exp:" + ex.toString())
ErrorReportManager.logHandledException(ex)
}
}
internal fun doNetWorkCall(optionalParams: Map<String, String>): String {
... ...
}
}
object ErrorReportManager {
fun logHandledException(ex: Throwable) {
... ...
}
}
the test
#Test
fun test_actionAndLogExceptions_report_exception() {
val networkApiSpy = spyk(NetworkApi::class)
every { networkApiSpy.doNetWorkCall(any(), any()) } returns JSON_EROOR. //<== test case one
mockkStatic(ErrorReportManager::class)
val spyApp = spyk(application)
networkApiSpy.actionAndLogExceptions(spyApp, HashMap())
// this any(JSONException::class) does not compile
io.mockk.verify(exactly = 1) {ErrorReportManager.logHandledException(any(JSONException::class))} //<===
//how to verify that here the JSONException
}
thanks #Raibaz help
verify {
ErrorReportManager.logHandledException(ofType(JSONException::class))
}
verify {
ErrorReportManager.logHandledException(match { it is JSONException })
}
val slot = slot<Throwable>()
verify {
ErrorReportManager.logHandledException(capture(slot))
}
assertTrue(slot.captured is JSONException)
My sample Kotlin about BufferedReader().use {}
I wonder if the close() is called when I return early in the use block
fun main() {
sendGet()
}
fun sendGet() {
val queryUrl = "http://www.google.com/search?q=kotlin&ie=utf-8&oe=utf-8"
val url = URL(queryUrl)
val conn = url.openConnection() as HttpURLConnection
conn.requestMethod = "GET"
conn.setRequestProperty("User-Agent", "Mozilla/5.0")
val responseCode = conn.responseCode
println("Response code: ${responseCode}")
when (responseCode) {
200 -> {
println("response: ${conn.getResponseText()}")
}
else -> println("Bad response code: ${responseCode}")
}
}
private fun HttpURLConnection.getResponseText(): String {
BufferedReader(InputStreamReader(inputStream)).use {
return it.readText()
}
}
You can see the source code for use in stdlib by navigating using "Go to implementation" (Cmd + B on a Mac):
public inline fun <T : Closeable?, R> T.use(block: (T) -> R): R {
var exception: Throwable? = null
try {
return block(this)
} catch (e: Throwable) {
exception = e
throw e
} finally {
when {
apiVersionIsAtLeast(1, 1, 0) -> this.closeFinally(exception)
this == null -> {}
exception == null -> close()
else ->
try {
close()
} catch (closeException: Throwable) {
// cause.addSuppressed(closeException) // ignored here
}
}
}
}
Because the call to close is inside the finally block, it will execute even on an early return.
I've just added
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-rx2:1.3.3"
to the project. And I have suspend fun foo(): Flow<Bar> in class A (from external package).
I need to get Flowable<Bar> to use in java.
I'd like to use extention fun A.fooRx(): Flowable<Bar> if possible.
You have to sneak out the returned Foo<Bar> from the coroutine in Kotlin:
// SomeSuspendAPI.kt
// -----------------
// the method to convert
suspend fun <T> Flow<T>.foo() : Flow<Int> {
return flow { emit(0) }
}
#ExperimentalCoroutinesApi
fun <T> Flow<T>.fooRx() : CompletableFuture<Flowable<Int>> {
val self = this
val future = CompletableFuture<Flowable<Int>>()
GlobalScope.launch {
try {
future.complete(self.foo().asFlowable())
} catch (ex: Throwable) {
future.completeExceptionally(ex);
}
}
return future
}
// Demo purposes
fun <T> just(v: T) = flow { emit(v) }
Then you can use that within Java:
public class UseFoo {
public static void main(String[] args) throws Exception {
SomeSuspendAPIKt.fooRx(
SomeSuspendAPIKt.just(1)
)
.thenAccept(flowable -> flowable.subscribe(System.out::println))
.join();
}
}
Edit 1:
You can, of course move some code back to the kotlin side:
fun <T> Flow<T>.fooRx2() : Flowable<Int> {
val self = this
val subject = SingleSubject.create<Flowable<Int>>()
GlobalScope.launch {
try {
subject.onSuccess(self.foo().asFlowable())
} catch (ex: Throwable) {
subject.onError(ex)
}
}
return subject.flatMapPublisher { it }
}
Then
public class UseFoo {
public static void main(String[] args) throws Exception {
SomeSuspendAPIKt.fooRx2(SomeSuspendAPIKt.just(1))
.blockingSubscribe(System.out::println);
}
}
Edit 2:
You can generalize this by using a transformation on the Kotlin side which gets you a continuation object to pass along:
fun <T, R: Any> Flow<T>.transformAsync(fn: suspend (t: Flow<T>) -> Flow<R>) : Flowable<R> {
val self = this
val subject = SingleSubject.create<Flowable<R>>()
GlobalScope.launch {
try {
val r = fn(self).asFlowable();
subject.onSuccess(r)
} catch (ex: Throwable) {
subject.onError(ex)
}
}
return subject.flatMapPublisher { it }
}
public class UseFoo {
public static void main(String[] args) throws Exception {
SomeSuspendAPIKt.transformAsync(
SomeSuspendAPIKt.just(1),
(source, cont) -> SomeSuspendAPIKt.foo(source, cont)
)
.blockingSubscribe(System.out::println);
}
}
I've subclassed DataInputStream and added little-endian methods. Observe that I add a new inp property that represents the original InputStream. I need to reference this property in the new methods. The following code works fine:
import java.io.*
import org.apache.poi.util.*
class MyDataInputStream(val inp: InputStream) : DataInputStream(inp) {
fun readShortLittle(): Short {
val r: Short
try {
r = LittleEndian.readShort(inp)
} catch (e: LittleEndian.BufferUnderrunException) {
throw EOFException()
}
return r
}
fun readIntLittle(): Int {
val r: Int
try {
r = LittleEndian.readInt(inp)
} catch (e: LittleEndian.BufferUnderrunException) {
throw EOFException()
}
return r
}
fun readLongLittle(): Long {
val r: Long
try {
r = LittleEndian.readLong(inp)
} catch (e: LittleEndian.BufferUnderrunException) {
throw EOFException()
}
return r
}
}
fun main(args: Array<String>) {
var i: Int
val inp = MyDataInputStream(System.`in`)
while (true) {
// llegir int en binari si EOF break
try {
i = inp.readIntLittle()
} catch (e: EOFException) {
break
}
println(i);
}
}
However I wonder how to do this with extension methods such as:
fun DataInputStream.readShortLittle(): Short {
...
}
fun DataInputStream.readIntLittle(): Int {
....
}
fun DataInputStream.readLongLittle(): Long {
....
}
I've problems when defining the new inp property.
Strictly speaking, you can't, because it's accessible by a protected field which isn't visible to an extension method. But using this instead of inp should work because LittleEndian.read methods will call read on the DataInputStream which will delegate to in.read.
Kotlin provides the use function for Closeable objects, but it seems they forgot to consider AutoCloseable (e.g. DB prepared statements) for the try-with-resources full Java equivalent.
I've implemented the next "home-made" solution:
inline fun <T:AutoCloseable,R> trywr(closeable: T, block: (T) -> R): R {
try {
return block(closeable);
} finally {
closeable.close()
}
}
Then you can use it the next way:
fun countEvents(sc: EventSearchCriteria?): Long {
return trywr(connection.prepareStatement("SELECT COUNT(*) FROM event")) {
var rs = it.executeQuery()
rs.next()
rs.getLong(1)
}
}
I'm new to Kotlin and I would like to know if I'm missing something important in my own solution that could give me problems/leakages in a production environment.
Your implementation will work fine but it's different from a standard try-with-resources implementation. If you want it to work like in Java you should do something like that:
inline fun <T : AutoCloseable, R> trywr(closeable: T, block: (T) -> R): R {
var currentThrowable: java.lang.Throwable? = null
try {
return block(closeable)
} catch (throwable: Throwable) {
currentThrowable = throwable as java.lang.Throwable
throw throwable
} finally {
if (currentThrowable != null) {
try {
closeable.close()
} catch (throwable: Throwable) {
currentThrowable.addSuppressed(throwable)
}
} else {
closeable.close()
}
}
}
UPDATE:
As mfulton26 pointed out in his comment kotlin.Throwable doesn't contain addSuppressed(Throwable) method so we have to cast kotlin.Throwable to java.lang.Throwable to make the code work.
Since Kotlin 1.1, .use has an AutoCloseable implementation.
#SinceKotlin("1.1")
#Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
#kotlin.internal.InlineOnly
public inline fun <T : AutoCloseable?, R> T.use(block: (T) -> R): R {
var exception: Throwable? = null
try {
return block(this)
} catch (e: Throwable) {
exception = e
throw e
} finally {
this.closeFinally(exception)
}
}
Copied from source
I think what you want is use() as defined on Closable.