I need to collect only the first value from two emitted by flow.
I have a function that returns flow:
fun myFlow = flow {
try {
emit(localDataSource.fetchData())
} catch(e: Exception) {
// just skip this error
}
emit(remoteDataSource.fetchData(1000, 0))
}
In one special case I need only first emitted value, doesn't matter is it from local cache or remote source.
I tried this one:
fun getRandomFavoriteItem() = myFlow.first().filter { it.score > 7 }.randomOrNull()
But first() invocation always throws
java.lang.IllegalStateException: Flow exception transparency is violated:
Previous 'emit' call has thrown exception kotlinx.coroutines.flow.internal.AbortFlowException: Flow was aborted, no more elements needed, but then emission attempt of value.
What I've tried:
single() -
java.lang.IllegalArgumentException: Flow has more than one element
take(1).first() -
java.lang.IllegalStateException: Flow exception transparency is violated:
Previous 'emit' call has thrown exception kotlinx.coroutines.flow.internal.AbortFlowException: Flow was aborted, no more elements needed, but then emission attempt of value
Catch error but it doesn't stop here:
myFlow.catch { e ->
if (e !is IllegalArgumentException) {
throw e
}
}.first().filter { it.score > 7 }.randomOrNull()
My questions are:
What is the point of usage first() if it doesn't work in case of more than 1 emitted values? If I would know that my flow produces only one value I could just use any other terminal operator.
How to avoid those errors and how to collect only first value without adding repeated code?
This isn't an error in first(). It's an error in your flow. You are not permitted to swallow all exceptions in a Flow in the way you have.
Some varying approaches may differ in whether they detect that error, but what you must fix is how you "just skip" all exceptions. Consider catching only the specific exceptions you're concerned about, or at least making sure to catch and rethrow CancellationException or its subclasses.
Lous Wasserman already found the problem, here some more details.
As mentioned in the error message you're also catching the AbortFlowException.
java.lang.IllegalStateException: Flow exception transparency is
violated: Previous 'emit' call has thrown exception
kotlinx.coroutines.flow.internal.AbortFlowException: Flow was aborted,
no more elements needed, but then emission attempt of value.
You're bascically catching an exception which interferes with the way flows work. The problem is not about the first function.
Since AbortFlowException is internal you cannot access it, but you can access its superclass CancellationException. You need to modify your catch block like this:
try {
emit(localDataSource.fetchData())
} catch (e: Exception) {
if(e is CancellationException) {
throw e
}
}
Now first will work in the way you expect it to.
Edit:
A better solution would be to handle the exception within fetchData (you might return null in case one was thrown). This way you don't get in the way of the flow mechanics.
If that is not possible, you could create a wrapper function which takes care of the exception handling.
Related
I have Java background and recently I started to learn Kotlin. Right now I'm reading a book "Programming Kotlin" and I reached a snippet with try-catch expression. It caused me to write some simple function to compare the way it works in Java and in Kotlin.
First function:
fun tryExpExplicit(throwing: Boolean): Int {
return try {
if (throwing) {
throw RuntimeException()
}
return 1
} catch (e: Exception) {
return 2
} finally {
return 3
}
}
works as I expected and always returns 3.
Unexpectedly, when I use implicit return, the behaviour is different
fun tryExpImplicit(throwing: Boolean): Int {
return try {
if (throwing) {
throw RuntimeException()
}
1
} catch (e: Exception) {
2
} finally {
3
}
}
and 3 is never returned.
Why do these two functions work differently?
The difference from behaviour in Java is because try ... catch ... finally is an expression in Kotlin, not a statement.
The way that a "try-expression" is evaluated, is defined as follows in the specification:
The try-expression evaluation evaluates its body; if any statement in the try body throws an exception (of type E), this exception, rather than being immediately propagated up the call stack, is checked for a matching catch block. If a catch block of this try-expression has an exception parameter of type T:>E , this catch block is evaluated immediately after the exception is thrown and the exception itself is passed inside the catch block as the corresponding parameter. [...]
If there is a finally block, it is evaluated after the evaluation of all previous try-expression blocks
The value of the try-expression is the same as the value of the last expression of the try body (if no exception was thrown) or the value of the last expression of the matching catch block (if an exception was thrown and matched). All other situations mean that an exception is going to be propagated up the call stack, and the value of the try-expression is undefined.
Note: as described, the finally block (if present) is always executed, but has no effect on the value of the try-expression.
So when throwing is true, the try, catch, and finally blocks are all executed, but the value of the try-expression is the value of the last expression in the catch block. This explains the behaviour in both the "explicit" and "implicit" cases.
In the "explicit return" case, return 2 is executed, but the method can't return there - the finally block still has to run! Then return 3 is executed, and now the method returns. Notably, the outer return is never executed. You can delete the outer return and start the method with try { ... and get the same result.
In the "implicit return" case, 2 is evaluated, and no side effects happen since it is just a literal. Then the finally block runs, and 3 is evaluated, and again no side effects happen since it is just a literal. Now we have finished evaluating the whole try-expression, and according to the spec, the value of the expression should be what we evaluated in the catch block - i.e. 2. And now we execute return 2.
Side note: return ... are also expressions, and they have the type of Nothing (the subtype of every type), because they never evaluate to anything. This is why you are able to write return 1 etc as the last line in the try-expression blocks in the "explicit return" case. In that case, the try-expression actually has a type of Nothing, and the outer return is actually returning Nothing. This is fine, because Nothing is a subtype of Int.
In the documentation here, it says that when try/catch is used an expression:
The contents of the finally block don't affect the result of the expression.
In the first example, even though your finally block is not affecting the expression's evaluation, it's short-circuiting the expression entirely by returning directly.
In the second example, finally isn't doing anything, and since it doesn't affect the expression, the value is ignored.
In practice, you'd never write code to return from a try and/or catch block if you're returning from a finally block, since it would be useless code.
You can do something like this in kotlin
fun tryExpExplicit(throwing: Boolean): Int {
return runCatching {
// do something
}.onFailure { exception ->
// similar to catch
}.onSuccess { data ->
// similar to finally
}.getOrThrow() // get the data or throw
}
internally runCatching is using try/catch blocks similar in java. you can think of
runCatching as try
.onFailure as catch
.onSuccess as finally
you could also use it's extension to return the value
.getOrThrow if you want to return the data or throw an exception
.getOrNull if you want to return the data or null if it throw an exception
.getOrElse if you want to return the data or else if it throw an exception
.getOrDefault if you want to return the data or default if it throw an exception
I am experimenting a bit with flows in kotlin and asked myself a question: Will my flows be cancelled if one of the operations within the flow throws an exception even If I use .catch?
If not, how can I cancel my flow when an exception occurs even while using .catch?
Example
fun testFlow() = flow {
emit("Test")
emit(Exception("Error"))
emit("Test2") // This should not be emitted
}.catch { e -> println("Showing UI $e") }
Another Example
fun testFlow2() = flow {
emit("Test")
throw Exception("Error")
emit("Test2") // This should not be emitted
}.catch { e -> println("Showing UI $e") }
If the execution of the Flow throws an Exception, it will cancel and complete the Flow during collection. The collect() function call will throw the Exception if the Flow.catch operator was not used.
If you emit an Exception like in your example, it's just another object in the Flow. Since you have not specified the Flow's type, it's implicitly choosing a type that's common between String and Exception. I think you have a Flow<Serializable> since that's a common supertype of both. If you had specified Flow<String>, it would not allow you to emit an Exception.
As per kotest docs: https://github.com/kotest/kotest/blob/master/doc/nondeterministic.md
You can tell eventually to ignore specific exceptions and any others will immediately fail the test.
I want to pass multiple exceptions to eventually that I know would be thrown by my block so that I can explicitly skip them.
Right now I only see a way to pass one, how do I pass more than one exception to eventually to skip it in case the block throws those exceptions?
You may use superclass for all your exceptions like
eventually(200.milliseconds, exceptionClass = RuntimeException::class) {
throw IllegalStateException()
}
or wrap exceptions
eventually(200.milliseconds, exceptionClass = IllegalStateException::class) {
runCatching { throw UnknownError() }
.onFailure { throw IllegalStateException(it) }
}
In 4.4.3 there are no features with collection of Exception
I'm still learning Kotlin and I just learned about the "use" and how it is a replacement for a try, catch and finally block.
However I am curious if it is possible to customize it's exception handling for example:
var connection: Connection? = null
try {
connection = dataSource.connection
connection.prepareStatement(query).execute()
} catch (e: SQLException) {
logger.log("Specific error for that query")
e.printStackTrace()
} finally {
if (connection != null && !connection.isClosed) {
connection.close()
}
}
That code is my current one, I have a specific error I would like to display on the catch, would that be possible using use?
This is my current use code:
dataSource.connection.use { connection ->
connection.prepareStatement(query).execute()
}
As commented by #Tenfour04, and from the documentation
[use] Executes the given block function on this resource and then closes it down correctly whether an exception is thrown or not.
In particular it is implemented like this:
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)
}
}
That piece of code should look familiar if you're a Java developer, but basically it executes block passing this (i.e. the receiver object) as an argument to your block of code. At the end it closes the AutoCloseable resource. If at any point an exception is thrown (either inside block or while closing the resource), that exception is thrown back to the caller, i.e. your code.
As an edge case you could have 2 exceptions, one when executing block and one when closing the resource. This is handled by closeFinally (whose source is available in the same file linked above) and the exception thrown while closing the resource is added as a suppressed exception to the one thrown from block – that's because only up to 1 exception can be thrown by a method, so they had to choose which one to throw. The same actually applies to the try-with-resources statement in Java.
In below code I want to neutralize the throw and continue the method - Can it be done ?
public class TestChild extends TestParent{
private String s;
public void doit(String arg) throws Exception {
if(arg == null) {
Exception e = new Exception("exception");
throw e;
}
s=arg;
}
}
The net result should be that, in case of the exception triggered (arg == null)
throw e is replaced by Log(e)
s=arg is executed
Thanks
PS : I can 'swallow' the exception or replace it with another exception but in all cases the method does not continue, all my interventions take place when the harm is done (ie the exception has been thrown)
I strongly doubt that general solution exists. But for your particular code and requirements 1 and 2:
privileged public aspect SkipNullBlockAspect {
public pointcut needSkip(TestChild t1, String a1): execution(void TestChild.doit(String))
&& this(t1) && args(a1) ;
void around(TestChild t1, String a1): needSkip(t1, a1){
if(a1==null) //if argument is null - doing hack.
{
a1=""; //alter argument to skip if block.
proceed(t1, a1);
t1.s=null;
a1=null; //restore argument
System.out.println("Little hack.");
}
else
proceed(t1, a1);
}
}
I think that generally what you want makes no sense most cases because if an application throws an exception it has a reason to do so, and that reason almost always includes the intention not to continue with the normal control flow of the method where the exception was thrown due to possible subsequent errors caused by bogus data. For example, what if you could neutralise the throw in your code and the next lines of code would do something like this:
if(arg == null)
throw new Exception("exception");
// We magically neutralise the exception and are here with arg == null
arg.someMethod(); // NullPointerException
double x = 11.0 / Integer.parseInt(arg); // NumberFormatException
anotherMethod(arg); // might throw exception if arg == null
Do you get my point? You take incalculable risks by continuing control flow here, assuming you can at all. Now what are the alternatives?
Let us assume you know exactly that a value of null does not do any harm here. Then why not just catch the exception with an after() throwing advice?
Or if null is harmful and you know about it, why not intercept method execution and overwrite the parameter so as to avoid the exception to begin with?
Speculatively assuming that the method content is a black box to you and you are trying to do some hacky things here, you can use an around() advice and from there call proceed() multiple times with different argument values (e.g. some authentication token or password) until the called method does not throw an exception anymore.
As you see, there are many ways to solve your practical problem depending on what exactly the problem is and what you want to achieve.
Having said all this, now let us return to your initial technical question of not catching, but actually neutralising an exception, i.e. somehow avoiding its being thrown at all. Because the AspectJ language does not contain technical means to do what you want (thank God!), you can look at other tools which can manipulate Java class files in a more low-level fashion. I have never used them productively, but I am pretty sure that you can do what you want using BCEL or Javassist.