Hi there fellas, I'm currently experimenting on ko...
# getting-started
c
Hi there fellas, I'm currently experimenting on kotlins standard lib functions, especially on use(). Unfortunately I'm still not sure how it works in case of an Exception being thrown. I have this function that writes a File to my apps storage:
Copy code
fun writeSoundToSharedStorage(context: Context, sound: ISound): Boolean {

        var inputStream: InputStream? = null
        var outputStream: OutputStream? = null
        val targetFile = getTargetFileFromSoundName(sound.soundName()) ?: return false

        try {

            inputStream = context.assets.open(getAssetPathFromSound(sound))
            outputStream = FileOutputStream(targetFile)
            inputStream.copyTo(outputStream)
        } catch (ex: IOException) {
            Log.e(LOG_TAG, "Failed to save file: ${ex.message}")
            return false
        } finally {
            try {
                inputStream?.close()
                outputStream?.close()
            } catch (ex: IOException) {
                ex.printStackTrace()
            }
        }
        return true
    }
What I'm thinking about is to migrate the try-catch-finally-block to a use()-block. But from the docs of use() I'm not sure if exceptions are handled or not. I took a look into the implementation and for me it looks like an exception is thrown, so my guess would be that my app would crash as long as I don't handle it somehow. Also my function depends on returning false if something went wrong. It would be nice if someone could bring some more clarity to me 🙂
d
All
use
does is ensure that any thrown exceptions do not prevent your resource (
Closeable
) from being disposed. It works basically the same as the
try-with-resources
construct in Java.
So, any exceptions still need to be handled by you, but
use
will ensure that
close
is called in all cases.
m
@CodeRed what @diesieben07 said. And to review your posted code, it doesn’t have enough try/catch statements as you have 2 Streams and only one try/catch pair. If there’s an issue processing outputStream, and for some reason an issue occurred closing the inputStream, the outputStream
close
would not get called. try-with-resource/use really helps clean up the code BUT you still need to ensure you have enough of them.
c
@Mike Oh yeah, thanks, I really forgot that one 😄 @diesieben07 Ok thanks for making it clear to me. But how do I handle the exceptions? How I understand the use() implementation it just throws an exception but doesn't seem to return it to the caller, or maybe I have a wrong understanding of the throw-expression. But if I'm right another try-catch block around the use()-block might not help, right?
m
Do you have experience with Java? Kotlin doesn’t fundamentally change exception handling EXCEPT all exceptions are treated like unchecked exceptions. Ultimately, if none of your code explicitly handles an exception, it ‘bubbles up’ to the JVM. The JVM will then catch, and report it.
d
You handle the exceptions that happen inside
use
like any other: You put
try
around it 🙂
c
Hm ok. I'm just a little bit confused cause in Java you have to declare that a method throws an exception to be able to catch and handle it in a method further up the call stack. But the use() implementation does not declare any exceptions to be thrown.
Copy code
@InlineOnly
@RequireKotlin("1.2", versionKind = RequireKotlinVersionKind.COMPILER_VERSION, message = "Requires newer compiler version to be inlined correctly.")
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
                }
        }
    }
}
OOOOOOOH wait... I think I know why. use() is an inline function so it will throw the exception in the same scope as the try-catch-block, right?
r
You should throw exception up in that I case, I think it is cleaner that way. As it is an exception if you fail to write
don't return Boolean with if it succeeded or not
m
In Java, you only have to add
throws
for checked exceptions, not all exceptions. Kotlin treats ALL exceptions as unchecked exceptions, so there’s no need to explicitly define that an exception is thrown. Having said that, IF your code is called from Java, and you want the Java compiler to ‘do the right thing’, you can add the
@Throws
annotation.
c
Ah ok. Didn't know that. Thank all of you very much 🙂