Kotlinlearncs.online LogoJava

    ← Prev

    Index

    Next →

    Kotlin
    Java
    • Using Interfaces : 39

    • Working with Exceptions : 38

    • Throwing Exceptions : 37

    • Catching Exceptions : 36

    • References and Polymorphism : 35

    • References : 34

    • Data Modeling 2 : 33

    • Equality and Object Copying : 32

    • Polymorphism : 31

    • Inheritance : 30

    • Data Modeling 1 : 29

    • Companion Objects : 28

    • Encapsulation : 27

    • Constructors : 26

    • Objects, Continued : 25

    • Introduction to Objects : 24

    • Compilation and Immutability : 23

    • Practice with Collections : 22

    • Maps and Sets : 21

    • Lists and Type Parameters : 20

    • Imports and Libraries : 19

    • Multidimensional Arrays : 18

    • Practice with Strings : 17

    • null : 16

    • Algorithms and Strings : 15

    • Strings : 14

    • Functions and Algorithms : 13

    • Practice with Functions : 12

    • More About Functions : 11

    • Errors and Debugging : 10

    • Functions : 9

    • Practice with Loops and Algorithms : 8

    • Algorithms I : 7

    • Loops : 6

    • Arrays : 5

    • Compound Conditionals : 4

    • Conditional Expressions and Statements : 3

    • Operations on Variables : 2

    • Variables and Types : 1

    • Hello, world! : 0

    Working with Exceptions

    class MyException(message: String) : Exception(message)
    val youAreAlone = false
    if (youAreAlone) {
    throw MyException("You are not alone!")
    }

    In this lesson we’ll focus on how to use Kotlin’s exception handling mechanisms—both try-catch and throw. We’ll also introduce a few new wrinkles to round out our understanding of this particular system. Our focus is on real-word patterns for working with and handling errors.

    Warm Up Debugging Challenge
    Warm Up Debugging Challenge

    But! Let’s warm up with another debugging challenge!

    Re-throw and finally
    Re-throw and finally

    Before we go on, let’s look at a few further wrinkles in Kotlin exception handling that we haven’t touched on yet.

    Re-throw
    Re-throw

    First, you may have wondered: what happens if you throw an exception inside a catch block? Let’s try it:

    try {
    throw Exception("Try")
    } catch (e: Exception) {
    throw Exception("Catch")
    }

    You’ll notice that the second exception is thrown out of the catch block. Of course, we can rewrap the entire try-catch in another try-catch:

    try {
    try {
    throw Exception("Try")
    } catch (e: Exception) {
    throw Exception("Catch")
    }
    } catch (e: Exception) {
    println("Caught it! $e")
    }

    Note that exceptions thrown inside a catch block cannot be caught by the same try-catch in which they were thrown. So this doesn’t work:

    try {
    check(false) { "Try" }
    } catch (e: IllegalStateException) {
    check(false) { "Nope" }
    } catch (e: IllegalArgumentException) {
    println("Won't get here")
    }

    finally
    finally

    try-catch blocks can include an additional component: a finally block. The finally block is always executed, regardless of whether an exception was thrown or not. Let’s see how that works:

    import kotlin.random.Random
    try {
    if (Random.nextBoolean()) {
    throw Exception()
    }
    println("Try")
    } catch (e: Exception) {
    println("Catch")
    } finally {
    println("Finally")
    }
    println("Done")

    If you run this a few times, you’ll notice that regardless of whether we complete the try block successfully or enter the catch, the finally block is always executed.

    One of the cool things about finally is that it is always executed. Even if the try includes a return!

    fun example(): Int {
    try {
    return 0
    } finally {
    println("Done")
    }
    }
    println(example())

    This feature of finally makes it useful when a method needs to do some kind of cleanup before exiting. We haven’t run into this scenario yet, but you will sometimes. And when you do, finally will be there for you!

    Practice: PingPonger Class

    Created By: Geoffrey Challen
    / Version: 2020.11.0

    Create a public class PingPonger. A PingPonger is in one of two states: ping or pong. You should provide a constructor accepting a String argument that sets the initial state. If the passed String is "ping", the initial state is ping. If the passed String is "pong", the initial state is pong. If the passed String is not ping or pong, you should throw an IllegalArgumentException.

    Once the PingPonger has been created, the user should call pong if the state is ping and ping if the state is pong. ping takes no arguments and returns true. pong takes no arguments and returns false. If ping or pong is called out of order, you should throw an IllegalStateException.

    Here's an example:

    Exception Handling Patterns
    Exception Handling Patterns

    Next let’s look at a few common exception handling patterns.

    Why Not assert?
    Why Not assert?

    We’ve used assert in the past, particularly when writing test cases.

    It’s fine for that, but there’s something about Kotlin assertions you need to know: they are not on by default!

    assert is enabled in all of our playgrounds. However, in other Kotlin environments—such as on Android—it may be hard or impossible to enable assertions. While they can be useful during testing, the right thing to do in most cases is to throw an Exception using require or check, rather than rely on assert.

    Input Validation
    Input Validation

    However, we can replace many of the places where we had previously used assert with Kotlin’s convenient require and check methods. When checking parameters to a method, use require:

    fun doIt(it: String?) {
    require(it != null) { "That's not it!" }
    }
    doIt("Good!")
    doIt(null)

    State Validation
    State Validation

    When designing our Kotlin classes, there are times when we want to enforce patterns of usage that span multiple methods. To make that more concrete, let’s look at an example together:

    // State Validation Example

    Batching Failures
    Batching Failures

    Sometimes our code needs to take a series of steps to complete some action. If any of those steps fail, the entire operation fails. This can be a good place to use a try-catch to avoid having to do a lot of error checking after each step. Let’s look at an example of this:

    import com.fasterxml.jackson.databind.ObjectMapper
    import com.fasterxml.jackson.module.kotlin.registerKotlinModule
    val mapper = ObjectMapper()
    val json = """{
    "you": {
    "are": {
    "not": {
    "alone": true
    }
    }
    }
    }"""
    val node = mapper.readTree(json)

    Rethrowing Exceptions
    Rethrowing Exceptions

    Sometimes when an error occurs we just want to log that it happened, but then let it continue to propagate. We can do this by rethrowing the error out of the catch block:

    try {
    throw Exception("Had a problem")
    } catch (e: Exception) {
    println("Had a problem: $e")
    throw e
    }

    Designing Your Own Exceptions
    Designing Your Own Exceptions

    Once you start writing your own libraries and sharing code with others, it can be helpful to design your own custom exceptions. One reason to do this is that it allows users of your code to better understand what went wrong. And to do more accurate error handling by establishing multiple catch blocks that deal with different kinds of exceptions.

    Happily, extending Exception could not be easier:

    class MyNewException : Exception()

    Homework: Pentagon Shape

    Created By: Geoffrey Challen
    / Version: 2021.10.0

    Create and complete the implementation of the Pentagon class. Your class should be public, inherit from the Shape class, and provide the following methods:

    1. Primary constructor that takes a Double parameter. Creates a new Pentagon with the passed side length. If the passed length is less than or equal to 0, throw an IllegalArgumentException. You should call the Shape constructor and pass it the String "pentagon" to identify the type of this shape.
    2. Public instance method area that takes no arguments and returns a double. Return the area of this shape, given only the side length, which you can find here: https://www.cuemath.com/measurement/area-of-pentagon/. You will need to use sqrt or Kotlin's built-in sqrt to solve this problem.
    3. Override fun equals(other: Any?): Boolean. Return true if other is a Pentagon with the same side length, and false otherwise. Note that other may be null or not a Pentagon.

    Finally, note that your class should not expose any of its internal state publicly.

    More Practice

    Need more practice? Head over to the practice page.