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.
Related
I need to run a task, which emits some data. I want to subscribe to this data like PublishSubject. But I can't solve a problem of one-instance flow. If I try to call it again, it will create another instance and the job will be done twice.
I tried to run the flow internally and post values to the BroadcastChannel, but this solution doesn't seem correct.
What is the best practice for such a task?
This will do the magic:
fun <T> Flow<T>.refCount(capacity: Int = Channel.CONFLATED, dispatcher: CoroutineDispatcher = Dispatchers.Default): Flow<T> {
class Context(var counter: Int) {
lateinit var job: Job
lateinit var channel: BroadcastChannel<T>
}
val context = Context(0)
fun lock() = synchronized(context) {
if (++context.counter > 1) {
return#synchronized
}
context.channel = BroadcastChannel(capacity)
context.job = GlobalScope.async(dispatcher) {
try {
collect { context.channel.offer(it) }
} catch (e: Exception) {
context.channel.close(e)
}
}
}
fun unlock() = synchronized(context) {
if (--context.counter == 0) {
context.job.cancel()
}
}
return flow {
lock()
try {
emitAll(context.channel.openSubscription())
} finally {
unlock()
}
}
}
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);
}
}
Playground sample
Given this code, the exception thrown in getRecords() is not caught in testFlattenMerge() - should it not be catchable though? Also, in getPeople(), the exception can be caught which causes flattenMerge() to work as expected, but it prints out "Caught in people" before any numbers are printed, and not after 64, as I expected. Is this the correct behavior? I can't quite fit my mental model of flattenMerge() around this.
import kotlin.reflect.KProperty
import kotlin.system.measureTimeMillis
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.*
fun getRecords(id: Int) = flow {
repeat(5) { emit("A record for $id") }
if (id == 6) throw RuntimeException("Anything but #6!")
repeat(5) { emit("A record for $id") }
}
fun getPeople() = flow {
repeat(10) { emit(getRecords(it)) }
// repeat(10) { emit(getRecords(it).catch{ println("Caught in getPeople()")}) } // This works, but it prints /before/ any cnt lines...?
}
suspend fun testFlattenMerge() {
println ("Merge with flattenMerge()")
var cnt = 0
val flowOfFlows = getPeople()
flowOfFlows.catch{ println("Caught before flattenMerge")}
.flattenMerge()
.catch{ println("Caught after flattenMerge")}
.collect {
println("${cnt++}") // Without catching inside getPeople() this stops at 64
}
}
suspend fun testManualMerge() {
println("Merge manually")
var cnt = 0
repeat(10) {
getRecords(it).catch{ println("Caught in manual merge") }
.collect {
println("${cnt++}") // This goes up to 94, as expected
}
}
}
fun main() = runBlocking {
testFlattenMerge()
testManualMerge()
}
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()
}