Scala: Can't catch exception thrown inside a closure - exception

Disclaimer: absolute novice in Scala :(
I have the following defined:
def tryAndReport(body: Unit) : Unit = {
try {
body
} catch {
case e: MySpecificException => doSomethingUseful
}
}
I call it like this:
tryAndReport{
someCodeThatThrowsMySpecificException()
}
While the call to someCodeThatThrowsMySpecificException happens just fine, the exception is not being caught in tryAndReport.
Why?
Thank you!

Try changing body from Unit to => Unit. The way its defined now, it considers body a block of code to evaluate to Unit. Using call-by-name, it will be executed in the try as defined and should be caught.

The body in your tryAndReport method is not a closure or block, it's a value (of type Unit).
I don't recommend using a by-name argument, but rather an explicit function.
def tryAndReport(block: () => Unit): Unit = {
try { block() }
catch { case e: MSE => dSU }
}

Related

In Kotlin exception blocks, how does one implement an 'else' (success) block?

In Python, I would do this:
try:
some_func()
except Exception:
handle_error()
else:
print("some_func was successful")
do_something_else() # exceptions not handled here, deliberately
finally:
print("this will be printed in any case")
I find this very elegant to read; the else block will only be reached if no exception was thrown.
How does one do this in Kotlin? Am I supposed to declare a local variable and check that below the block?
try {
some_func()
// do_something_else() cannot be put here, because I don't want exceptions
// to be handled the same as for the statement above.
} catch (e: Exception) {
handle_error()
} finally {
// reached in any case
}
// how to handle 'else' elegantly?
I found Kotlin docs | Migrating from Python | Exceptions, but this does not cover the else block functionality as found in Python.
Another way to use runCatching is to use the Result's extension functions
runCatching {
someFunc()
}.onFailure { error ->
handleError(error)
}.onSuccess { someFuncReturnValue ->
handleSuccess(someFuncReturnValue)
}.getOrDefault(defaultValue)
.also { finalValue ->
doFinalStuff(finalValue)
}
Take a look at the docs for Result: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-result/index.html
If you do not care about the default value, for example, you want just to hide the loading you could use this:
runCatching {
show_loading(true) //show loading indicator
some_func() //this could throw an exception
}.onFailure {
handle_error(it.message)
}.getOrNull().run {
show_loading(false) //hide loading indicator regardless error or success
}

Return string from Firebase addOnSuccessListener [duplicate]

I'm building an app for a friend and I use Firestore. What I want is to display a list of favorite places but for some reason, the list is always empty.
I cannot get the data from Firestore. This is my code:
fun getListOfPlaces() : List<String> {
val places = ArrayList<String>()
placesRef.get().addOnCompleteListener { task ->
if (task.isSuccessful) {
for (document in task.result) {
val name = document.data["name"].toString()
places.add(name)
}
}
}
return list;
}
If I try to print, let's say the size of the list in onCreate function, the size is always 0.
Log.d("TAG", getListOfPlaces().size().toString()); // Is 0 !!!
I can confirm Firebase is successfully installed.
What am I missing?
This is a classic issue with asynchronous web APIs. You cannot return something now, that hasn't been loaded yet. With other words, you cannot simply return the places list as a result of a method because it will always be empty due the asynchronous behavior of the onComplete function. Depending on your connection speed and the state, it may take from a few hundred milliseconds to a few seconds before that data is available.
But not only Cloud Firestore loads data asynchronously, almost all of modern other web APIs do, since it may take some time to get the data. But let's take an quick example, by placing a few log statements in the code, to see more clearly what I'm talking about.
fun getListOfPlaces() : List<String> {
Log.d("TAG", "Before attaching the listener!");
val places = ArrayList<String>()
placesRef.get().addOnCompleteListener { task ->
if (task.isSuccessful) {
Log.d("TAG", "Inside onComplete function!");
for (document in task.result) {
val name = document.data["name"].toString()
places.add(name)
}
}
}
Log.d("TAG", "After attaching the listener!");
return list;
}
If we run this code will, the output in your logcat will be:
Before attaching the listener!
After attaching the listener!
Inside onComplete function!
This is probably not what you expected, but it explains precisely why your places list is empty when returning it.
The initial response for most developers is to try and "fix" this asynchronous behavior, which I personally recommend against it. Here is an excelent article written by Doug Stevenson that I'll highly recommend you to read.
A quick solve for this problem would be to use the places list only inside the onComplete function:
fun readData() {
placesRef.get().addOnCompleteListener { task ->
if (task.isSuccessful) {
val list = ArrayList<String>()
for (document in task.result) {
val name = document.data["name"].toString()
list.add(name)
}
//Do what you need to do with your list
}
}
}
If you want to use the list outside, there is another approach. You need to create your own callback to wait for Firestore to return you the data. To achieve this, first you need to create an interface like this:
interface MyCallback {
fun onCallback(value: List<String>)
}
Then you need to create a function that is actually getting the data from the database. This method should look like this:
fun readData(myCallback : MyCallback) {
placesRef.get().addOnCompleteListener { task ->
if (task.isSuccessful) {
val list = ArrayList<String>()
for (document in task.result) {
val name = document.data["name"].toString()
list.add(name)
}
myCallback.onCallback(list)
}
}
}
See, we don't have any return type anymore. In the end just simply call readData() function in your onCreate function and pass an instance of the MyCallback interface as an argument like this:
readData(object: MyCallback {
override fun onCallback(value: List<String>) {
Log.d("TAG", list.size.toString())
}
})
If you are using Kotlin, please check the other answer.
Nowadays, Kotlin provides a simpler way to achieve the same result as in the case of using a callback. This answer is going to explain how to use Kotlin Coroutines. In order to make it work, we need to add the following dependency in our build.gradle file:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.2.1"
This library that we use is called Module kotlinx-coroutines-play-services and is used for the exact same purpose. As we already know, there is no way we can return a list of objects as a result of a method because get() returns immediately, while the callback from the Task it returns will be called sometime later. That's the reason why we should wait until the data is available.
When calling "get()" on the Task object that is returned, we can attach a listener so we can get the result of our query. What we need to do now is to convert this into something that is working with Kotlin Coroutines. For that, we need to create a suspend function that looks like this:
private suspend fun getListOfPlaces(): List<DocumentSnapshot> {
val snapshot = placesRef.get().await()
return snapshot.documents
}
As you can see, we have now an extension function called await() that will interrupt the Coroutine until the data from the database is available and then return it. Now we can simply call it from another suspend method like in the following lines of code:
private suspend fun getDataFromFirestore() {
try {
val listOfPlaces = getListOfPlaces()
} catch (e: Exception) {
Log.d(TAG, e.getMessage()) //Don't ignore potential errors!
}
}
The reason for having a empty list got perfectly answered by Alex Mamo above.
I just like to present the same thing without needing to add an extra interface.
In Kotlin you could just implement it like so:
fun readData(myCallback: (List<String>) -> Unit) {
placesRef.get().addOnCompleteListener { task ->
if (task.isSuccessful) {
val list = ArrayList<String>()
for (document in task.result) {
val name = document.data["name"].toString()
list.add(name)
}
myCallback(list)
}
}
}
and then use it like so:
readData() {
Log.d("TAG", it.size.toString())
})

Kotlin: catch extension

Because Kotlin doesn't support multiple catch like java does, I want to create extension to partially solve the problem.
fun <T: Throwable> (() -> Unit).catch(vararg exceptions: KClass<T>, catchBlock: (Throwable) -> Unit) {
try {
this()
} catch (e: Throwable) {
if (e::class in exceptions) catchBlock(e) else throw e
}
}
That can be called like this:
{
throw NotImplementedException.exception()
}.catch(NotImplementedException::class) {
//handle it
}
But the problem is that if to pass several arguments with different types it doesn't work (Type inference failed):
{
throw IndexOutOfBoundsException()
}.catch(NotImplementedException::class, IndexOutOfBoundsException::class) {
}
So how can I change signature of the extension to catch several exceptions of different types?
Let's look at the types of the two arugments you're trying to pass to your function:
val kclass1: KClass<NotImplementedException> = NotImplementedException::class
val kclass2: KClass<IndexOutOfBoundsException> = IndexOutOfBoundsException::class
While they are both KClass instances, their type parameters are different - NotImplementedException and IndexOutOfBoundsException. This means that no generic T type parameter can be found for the function that would fit both of these types exactly.
Just for demonstration and explanation purposes, you could help type inference by casting both of your types to KClass<Throwable> (or KClass<Exception>, or KClass<RuntimeException, you get the idea) yourself, that way it could figure out the generic type:
{
throw IndexOutOfBoundsException()
}.catch(NotImplementedException::class as KClass<Throwable>, IndexOutOfBoundsException::class as KClass<Throwable>) {
println("Caught something: $it")
}
But the real solution is to use the out keyword to specify use-site variance for the type parameter of the KClass instances:
fun <T : Throwable> (() -> Unit).catch(vararg exceptions: KClass<out T>, catchBlock: (Throwable) -> Unit) {
try {
this()
} catch (e: Throwable) {
if (e::class in exceptions) catchBlock(e) else throw e
}
}
This way the compiler will find a type for T that's both a subtype of Throwable as specified, and is a supertype of all argument's KClass type parameters - this will be RuntimeException in this case, which you can find out by opening intention actions on the catch call (Alt + Enter on Windows, ⌥↩ on macOS) and choosing Add explicit type arguments. This will produce the following:
{
throw IndexOutOfBoundsException()
}.catch<RuntimeException>(NotImplementedException::class, IndexOutOfBoundsException::class) {
println("Caught something: $it")
}

Can I avoid redundantly casting a Throwable when using catching(...).either?

I'm using util.control.Exception.catching to convert internal exceptions into an exception type specific to my library:
import util.control.Exception._
abstract class MyException extends Exception
case class ErrorOccurredDuringFoo(e : Exception) extends MyException
def foo : Foo = {
catching(classOf[Exception]) either { fooInternals } match {
case Left(e) => throw ErrorOccurredDuringFoo(e)
case Right(v) => v
}
}
Unfortunately, this doesn't work. Applying the Catch returned by either doesn't return Either[Exception,Foo], it returns Either[Throwable,Foo]. But I've already told catching I want it to catch only subtypes of Exception, not all Throwables, and internally it's already matched an Exception.
Am I using this correctly? Is there no way I can convince catching to return the exception it catches as an instance of the class of exceptions I asked it to catch? Is my best bet to just add a redundant asInstanceOf[Exception]? I'd rather not if I can avoid it, as the catching instance could logically be created elsewhere, and I'd like to get a compile error if I one day change it to catching[Throwable] without changing ErrorOccurredDuringFoo, not a runtime error when the cast to Exception fails.
Catch isn't parameterised on Throwable, only on the result type. The only way to downcast the Throwable type is with the mkCatcher method:
val c = catching[Foo](
mkCatcher(
(t: Throwable) => t.getClass == classOf[MyException],
(e: MyException) => throw new ErrorOccurredDuringFoo(e)))
c(fooInternals)
But, Catch takes a Catcher[T] – which is really just an alias for a PartialFunction[Throwable, T].
As a case statement is a PartialFunction we can use pattern matching:
val c: Catcher[Foo] = {
case e: MyException => throw new ErrorOccurredDuringFoo(e)
}
catching(c)(fooInternals)
You could write it like this:
def foo : Foo = {
catching(classOf[Exception]) either { fooInternals } match {
case Left(e: Exception) => throw ErrorOccurredDuringFoo(e)
case Right(v) => v
}
}
It is interesting that it doesn't complain about missing cases.

How to test for additional properties of expected Exceptions using ScalaTest

I'm using ScalaTest for testing some Scala code.
I currently testing for expected exceptions with code like this
import org.scalatest._
import org.scalatest.matchers.ShouldMatchers
class ImageComparisonTest extends FeatureSpec with ShouldMatchers{
feature("A test can throw an exception") {
scenario("when an exception is throw this is expected"){
evaluating { throw new Exception("message") } should produce [Exception]
}
}
}
But I would like to add additional check on the exception, e.g. I would like to check that the exceptions message contains a certain String.
Is there a 'clean' way to do this? Or do I have to use a try catch block?
I found a solution
val exception = intercept[SomeException]{ ... code that throws SomeException ... }
// you can add more assertions based on exception here
You can do the same sort of thing with the evaluating ... should produce syntax, because like intercept, it returns the caught exception:
val exception =
evaluating { throw new Exception("message") } should produce [Exception]
Then inspect the exception.
If you need to further inspect an expected exception, you can capture it using this syntax:
val thrown = the [SomeException] thrownBy { /* Code that throws SomeException */ }
This expression returns the caught exception so that you can inspect it further:
thrown.getMessage should equal ("Some message")
you can also capture and inspect an expected exception in one statement, like this:
the [SomeException] thrownBy {
// Code that throws SomeException
} should have message "Some message"