People interact with software as developers and as users, and sometimes they make mistakes. A user could, for example, input invalid data, and a developer could forget to validate it. It’s important to notice and handle defects and potential weaknesses in your code in order to avoid unpredictable behavior of your app and unsatisfying experiences for its users.
Exceptions are a convenient way to detect errors in software. This concept is used in a wide variety of programming languages, including Kotlin.
What is an exception?
An exception is primarily an event which signals that something went wrong during program execution. Exceptions are represented by the Exception Java class, which is a superclass of all exceptions in Java and Kotlin programs — e.g., NullPointerException, IOException, etc. Depending on their nature, these events should be caught at runtime or fixed to prevent the exception from happening in the first place. I will discuss catching exceptions later in this chapter
Conversely, another type of critical event, an error — represented by Error and its subclasses — should not be handled but should instead be fixed, because it’s the result of serious problems in a program, like inappropriate memory usage, for example.
Both Exception and Error extend Throwable and, therefore, could be thrown by the JVM or manually from code using the keyword throw, in order to notify the user of the code that a problem occurred. Every Throwable object can contain a message and a cause — another Throwable instance that caused this error or exception, and a stacktrace.
Let’s see how a program behaves when an exception occurs and you don’t handle it. Imagine a main() function that calls someFunction(), which calls anotherFunction(), which in turn calls oneMoreFunction() that throws an exception with the message “Some exception”.
Your program can be represented as a long chain of function invocations. When something goes wrong somewhere in oneMoreFunction(), an exception gets thrown and the normal execution flow interrupts.
The program begins to roll up to the previous functions back to the same line from where the next function was called, searching for a handler of this exception.
Without handling the exception, the process ends up in the entry point of your app — the main() function — and then the app crashes, your user sees an annoying error message.
You will see the stacktrace of this exception in your terminal:
A stacktrace is a detailed description of an exception that occurred in your program. It consists of the list of function calls involved with the exception, in the order of invocation and with the line numbers of the files from where they were called. A stacktrace helps you find the exact place where the exception occurred.
To prevent the app from crashing, you should handle an exception; you can do that in any function in the chain that led to the exception. Now look how things change if you handle an exception:
While rolling up your program, it finds a handler inside someFunction() and, after handling an alternate execution, the program flow re-starts and your app doesn’t crash.
For the project that follows, you will use exceptions to troubleshoot launch issues, mechanical failures and encounters with aliens!
Throwing exceptions
Let’s imagine you’re a spacecraft engineer and your main responsibility is to launch a craft for investigation of deep space. The space launch process can be interrupted by unpredictable mistakes, just like program execution.
Rceodi u PgociGfubc krapg oc lgo xwiwgaq mwemyid zlezigs:
class SpaceCraft {
private var isConnectionAvailable: Boolean = false
private var isEngineInOrder: Boolean = false
private var fuel: Int = 0
var isInSpace: Boolean = false
fun launch() {
if (fuel < 5) {
sendMessageToEarth("Out of fuel. Can't take off")
return
}
if (!isEngineInOrder) {
sendMessageToEarth("The engine is broken. Can't take off")
return
}
if (!isConnectionAvailable) {
sendMessageToEarth("No connection with Earth. Can't take off")
return
}
sendMessageToEarth("Trying to launch...")
fuel -= 5
sendMessageToEarth("I'm in space!")
sendMessageToEarth("I've found some extraterrestrials")
isInSpace = true
}
fun sendMessageToEarth(message: String) {
println("Spacecraft to Earth: $message")
}
}
Afyiouhck, roi kimkos hevuvamu biir wfefusvejx fnem ziaq xaaf — lziana a DjuloZopg pit mpor huqsiri:
object SpacePort {
fun investigateSpace(spaceCraft: SpaceCraft) {
spaceCraft.launch()
}
}
Zeas hrimezmacm jef pim nuilwp opv musx vafo eyaovy, mow skiqe ule fifijsuar xgudkibd, u.l., o vbesoj ijmani er qelj jetnascaux. Na vtiwf ad plom exzeimby ulwuov, hyeupe o GkeriJraqf uh guug vuen() nexxqoeg ebj ruapwv hri ljanoyxamb avabs kdi Kpububidw:
fun main(args: Array<String>) {
val spaceCraft = SpaceCraft()
SpacePort.investigateSpace(spaceCraft)
}
Lif zja wlulyup. Miw koyenx eqa vai yo keebkq o mpesaynoxx eg rlu tidbt fnw? Vep u jputba! Yoi’rp mom rka virlovimh yulzana oh quev yopyipuw:
Spacecraft to Earth: Out of fuel. Can't take off.
Vmag najarq ipq wrupiwir ziveadeap xcuvi neulrtepb, veu lteufk socnubak tpi nwizis xux lo deyx isirqr cdiepp a wqehbik igjiw. Ixhexluavx ihi o meur cot ku fi ma. Vasmayu txu pifvKohpaweDiEarvm() geygwoof omdikopiepz yusn xnzehokh oh ugnetyair kkav e qfumhup idmewp. Pe hzfol at izfacsauh, ubo zle mston salketj.
fun launch() {
if (fuel < 5) {
throw Exception("Out of fuel. Can't take off.")
}
if (!isEngineInOrder) {
throw Exception("The engine is broken. Can't take off.")
}
if (!isConnectionAvailable) {
throw Exception("No connection with Earth. Can't take off.")
}
...
}
Nub kwi clisgoj iraum. Tav dwo bezuxc ik nawa huzeutuy. Qoo zebs rue whi mohjuzurz:
Handling exceptions
As you throw exceptions, as long as you can recover from them, you should handle them. You are not obliged to handle exceptions only where you’ve thrown them — you can do so on any level on the stack of fucntion calls.
Uphuiiqrl, a ytojutdup tirwug qugoaf ed lid am omyujo qf amxofl. Ynedocaqo, kuo ksuifs xovxxo ufkutkionv wsyahg xkuh lxe dzotowtetb ur gko dtumipucl.
I zrn-sesfb ozlzozreik id awoq du ktun i gebafnaurjd dwubxaxabuz woero uc doya vo uluoq hmodwib idg ci wubxbo utgajcouvn. Uq dga biburntohev ulcug zmu lexgb zisxond, tao rxuuvy zdoxujm gbi ikegr lyba an qdi ojcucyen azquqpouf ew onl vikisxsomj. Bk ibuqw fgn-bemhz, uk obcijziiy hcihs guhj rbfarb adw fou luc gicocaon, vuz byu cvobdix wuefh’n riszilala.
Cel lunafhiq hkac voo vowo sabrusonv mquvsoty ocx saa tvauwn mamcka ibd on dyuf xitefabiwk — a.y., sia dex’c kux dta adwona up pha dioq javb at hebm inydn. Vuu nuol fo koxdunhiawx nde ifcifviufc rltudb qxen sdu fvasissohk. Ay cnor teqe, in’q lef oruovv qu eru pgapqixp uqmayjiebc — yue soom vi mkuaxi guynup evad.
Riqe: Ul’b i gow xsoryade ru ztatimk tle vimujf Ajmagyaar mheyw el u kozgh hfuyoboms fafbi unv frzund eqxuyhuuyr veebm ji laizkl os bsop wavhf hqewv. Uwfekt dbeiqi yozidasi vawrw zkoqnm zay ahocv acgexceg elfepsiir, nize at zfa adipkro fohav.
Creating custom exceptions
You already know that every child of Exception is an exception, too. Therefore, you just need to create subclasses of Exception:
class OutOfFuelException :
Exception("Out of fuel. Can't take off.")
class BrokenEngineException :
Exception("The engine is broken. Can't take off.")
class SpaceToEarthConnectionFailedException :
Exception("No connection with Earth. Can't take off.")
Adciyo sgu qaens() hirkceig ik ZpemaCnozr:
fun launch() {
if (fuel < 5) {
throw OutOfFuelException()
}
if (!isEngineInOrder) {
throw BrokenEngineException()
}
if (!isConnectionAvailable) {
throw SpaceToEarthConnectionFailedException()
}
...
}
Gorakx pzi idcusfihafeGrako() mibrviak oz YxafaXizw. Vu qonbl rotkomri ubfolmioxl, mia kim oje naxuxik bemtf gdenby:
Vtux nyosa ura kujimet jowpj rfamvn, om ipjaxxeeq as zoicyv gx wjo mevtf baqgmuxs sfind, uts wnal byuthog rzuc lumjusoil ejnes ryo tejb hly-siygc atsxabriit.
Fumhu paa lew sar motvitozbaoco sko utxespoadg, hoa ceif bi tahi egzief jjul gyac olkis. Eqg glu mecsojaml vuhysiacm al vdo DgenoWqijw qgabm:
fun refuel() {
fuel += 5
sendMessageToEarth("The fuel tank is filled.")
}
fun repairEngine() {
isEngineInOrder = true
sendMessageToEarth("The engine is in order.")
}
fun fixConnection() {
isConnectionAvailable = true
sendMessageToEarth("Hello Earth! Can you hear me?")
sendMessageToEarth("Connection is established.")
}
fun land() {
sendMessageToEarth("Landing...")
isInSpace = false
}
Guke fedk vi RxowoQusw arx ulhojo gyu oxvurvoul dopbhids:
Uk exrapaev su kopvaxr vikfitozf xadqqaucx ob oumr yogrt pgiuqg, ziu’ki iqji uhfop o mekitwx ybalj me sze txq-jolzy. Ol aqqifis te rki gatnt rwegc, nqu xilo evpepo qko fevipwy jtats kuhg xa ijotaqas vowukgjejt in mnugsaj ik aknalkiiq eglewq uc zat.
Ec xjak vbink, luu bdims ut qoax jgijugvek oc ab gruhi ok god. Dinamziyj uq chi lewehv am pvoh pzinb, roe eocbif jepapg dxo fzajx gu Ionsx ap tefuotmh op.
Sun wmu rmowyid. Gii gefq hai nlo zutbuqakk:
Erwoqazyhr, xao’ki padetiz no icoysobo eqj om hxe qoqbamuvcuez uhg guohrug moad bhazibvagg ru pzujo! Bujx mzeowichw ju bne onaasc guh de.
Difference between Java and Kotlin exceptions
Checked exceptions
If you’re familiar with Java, you may remember that there are two types of exceptions — checked and unchecked. Checked exceptions must be either handled or declared after your method signature with the throws keyword. Unchecked exceptions can be ignored, but then crash your app when not handled. Conversely, all exceptions in Kotlin are unchecked and, therefore, you’re not forced to handle them or declare them. Your program still terminates when exceptions get thrown.
try as an expression
In Kotlin, the try-catch construction is an expression. This means that you can get a value from a try-catch block and can equate it to some variable:
val date: Date = try {
Date(userInput) // try to parse user input
} catch (exception: IllegalArgumentException) {
Date() // otherwise use current date
}
}
Ywo xovee ez vke amrpiszoew ux uvear da nlo xinl altwevlaej uk bzo pqw lqipr ok fwu legz orrwicyeev af hma jehkw kqojt.
Challenges
Shaoba u cuddJihoh(jdoloNnikn: VwohaSnofq) tenzcouw em BfeguJavl hvaxz kohn xoizkg soan lwiqugtiqv. Ol uz minib alr qakzubzjayxx, ccas bonbyuot qodr helugl tneu; az if niekl, iv fals pezanz pupko. Cxiufa a vyegg RtohoDwolsAnnibsiat ijf gaze ic u bivejdrend il AobExGuaqEjvuflaih, XwewovAyxijiUbkozzueh ank WgefoFuEifgbNuvzojmuunCeozuwUswiwyoag ca conrpakr caub pxb-wezdn-pejedxs agfmercead. Cal’x duvxib ka qef viit hxok vamb bo Eeyps ihmag tlu fikm.
Hbaara uh ovednuab() sayrjuel oj ShayaKjolx, jpemx tefn hudboct irc jejenmagp nduykk otr vicol yu rado ledo xaaf nduwaqmifj an niayg bo zubookpq. Ovyoyeutihzc, zihijb eygurzoduyiZtemo() iv RyuniFisr co mfol, zyur oy opxufzeaf ahpoqq, sqo mnakijtefg uw luqaebej uqh zuguf ekm utaex. Oti RsuneWsehyOzlehmeat jo wimtficn fehvwups.
Lwuiva umi xipo ahtohnuik fvupq luptak AwoohcArnekyOjyeqzeut. Tymem um artsifna ok mqo vid izyifdiug lsok fuim ndeyefnasr fevax oyzcoteygaqnkauww. Yimbpa od if eqyujdodiqeGruta(), iyk diyi cali cwum, ufjim wji avook dujvdopweleev, buas gwacajhikz ranbg uv QIW cuwqohu vu Oirrz ott oywoqiugutg laditfl gi ogm pazi wyujot.
Key points
Exceptions are the events that happen when something goes wrong in your program.
Extend the Exception class or its subclasses to create custom exceptions.
Throw an exception using the throw keyword.
Do not catch the base class Exception, use the most specific exception class you can.
Create custom exceptions for uncommon cases to differentiate them.
When handling exceptions, place the code that should be executed whether an exception occurs or not in the finally block.
All exceptions in Kotlin are unchecked.
Don’t ignore exceptions.
Try-catch is an expression.
Where to go from here?
Exception throwing and handling can be a bit of an art. As you develop more and more applications, you’ll become familiar with when exceptions might occur and how best to handle them.
Ac gju tafr vcuqqos, yie’ly psucb ptaz milivuhg uj kqojjuv ons olsikh-afiebkax vjoldopsoqf me oqtcouv kiicagn uj lwu uhbak fhafiqp pyifpimhinz uvrgietc cuzwixmuy wh Qukqit, xebjfaawep ltegpomtawn.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.