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.
But! Let’s warm up with another debugging challenge!
throw
and finally
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.
throw
throw
First, you may have wondered: what happens if you throw an exception inside a catch
block?
Let’s try it:
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
:
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:
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:
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
!
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!
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:
Next let’s look at a few common exception handling patterns.
assert
?assert
?We’ve used assert
in the past, particularly when writing test cases.
It’s fine for that, but there’s something about Kotlin assert
ions 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
.
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
:
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:
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:
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:
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:
Create and complete the implementation of the Pentagon
class.
Your class should be public, inherit from the Shape
class, and provide the following methods:
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.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.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.
Need more practice? Head over to the practice page.