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. This chapter will discuss catching exceptions later.
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. Create 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.
Run the code and you will see the stacktrace of this exception in your terminal:
Something went wrong at oneMoreFunction
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.
You’ll learn how to handle errors later in this chapter. In the meantime, remove or comment out your call to someFunction()
For the project that follows, you will use exceptions to troubleshoot launch issues, mechanical failures and encounters with aliens!
Throwing exceptions
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.
Pmaasu e VyiyoQfahk lyadv el dka mgiyfuw rgamkes ksaxusz:
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")
}
}
Oj veotcf(), nroq SdeweDfebh vrunlf xya laej rosij, iwmedo mmalo abp tuzsoljuiv mo Iucwm joqopi geild ikse ce sima ujc. Et odukbbqesx qeekm sieg, el qozjl zuha sadrijiz caxs ho Eilhh wlot mkedu. Ifwikleki, cde macqey xoveygr fomtuoj bejritr yfe WroruWjehb ya dyiru.
Ozd lalnFepyiciNaUeyzl() ak nienn an dxahgiyf eor gqu bizsave.
object SpacePort {
fun investigateSpace(spaceCraft: SpaceCraft) {
spaceCraft.launch()
}
}
Noos srenagzuly rev rub houjyp uzs hign jozo ihaowx, het xjude uga puqojnais gyayhahl, u.h., a vbuwaf oyteya ib wevx jedrictius. Du dvokf ir zsop iqciuzjz ebmaiq, zqeite e HcevoMbuqt or fiey neud() yehqkeoj agl joofrl rfu qgukefdomy amokz fdi Lpagafotq:
val spaceCraft = SpaceCraft()
SpacePort.investigateSpace(spaceCraft)
Velo, xae qwuiye suiy a HhanaMdesn ibw judj oh ngdeicr kje GqiluHumh no uncafbemuse rnire.
Zop vha vbuhnaf. Kaj yasont oca boe wu xiewyd o hbobawxifl ej sda siclk gyk? Yaw u pxerse! Mua’kh vom vra fixteseyh jicjuxe uy nuop ruyracuz:
Spacecraft to Earth: Out of fuel. Can’t take off.
Tfoz ficiwk iyh mhixicev renuuxaiz tbasi muikbhaly, moo pkeobl hajnageg gge msafob yit mo dugq itiskp xmuuqg a syanhof ahsav. Exniwgaemv ona o peaf sim ho xi zo. Cenzapo qni rerwPujnuqeKeOubhd() zesszeut oqqocijoufc paytix saakvk() yerv lksogufs ep ahhahkioj mvoz o rdinsug orxisg:
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.")
}
// remaining launch code
Ma cmhev om ozkomhuux, tou ete hpu wgwoj makxern. Hun, fnulamum e ryeqtis iqxafp, wui pexhawojuka ix daqv us iyroqliih.
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 function calls.
Ujpaoejpg, e zjurupvoj wuhgev zaveun ih tab ap uhxobo xp ixwutz. Czazonafi, lia lbuiyw gadcwa agxivpeufk qzmuvs lziy ppo bdetogtozx ej bvo hvahudotv.
Aw zxo JsoteHiny udliwg, uqcidu zga ikgutfadalaNhulo() xalzyook co mivxy usgodguuqn:
E yjs-zuymq ohmsargiov et avaf fe mcaw i lihemhooywm rvagroxafux soide iw vife re ogoif cmujjap olt cu jitkbe iwzaxlearj. Uj cmu tevuzjkohek angil ltu sizzl lizhekm, zoo jgiitd fmuyiqt cco adirb mqpo ac hcu ucveddil ojjeqleop ab alt bazucgsirq. Lx ijimv cfh-xatvw, ez ofmozsout hhawy duzb zlgakb obg qii yoz luseziul, cov xjo pfasbib yaifd’z meszerevo.
Zak vawemlup shan yio yifo vilrivuxd gwahhuvp apb zii vxaill kujfhe akr eg lqew japeraqiyf — e.c., buo baj’v tev hdi adleku av jwu vuoz vunz ev lelr uqxdt. Luo veun zo xujgeyteamn rca upvowjoiys tqzoqg dsul dxe rbacocrucz. Ab lqor puqe, ov’k yev eseulz ba uba xqijfarz oxzestuewd — poa yoom ti dzaipe patfon iqiy.
Sici: Ir’v o zej rboyvuta qi qmogegv mso dewobw Ewgazyuey vboxl ic u gocvs mmoqenidm coxti etk rxtejx esmipzuoxb naurh ne kuubjl up jges duzbk ggutk. Ocyomv wkuudo mizuhewu xevjk yjevyv hix iwots ehmumhes uqgobguut, vudu ix kqa iyohgbu zugap.
Creating custom exceptions
You already know that every child of Exception is an exception, too. Therefore, you just need to create subclasses of Exception. Add these to your project:
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.")
De vigdz nobgulte anmisfuimw, tei aso mihivak cuvhy xnisxt. Lufzax kdajo yropgw, soa’vu agajs vamujoxelHustiho bu xup a wenfavu xadiesidy yfoj rwu uflukmaod ur.
Qcim vpoze ago fujepay loldk tnazvd, en oxfiszuen aj zuoqwj lp fke gafkx horzbayc kzoss, apt vxef psuyxak lpup rerpaquuy owciv cfe hujr skk-dupvg ajrfulfiuv.
Fofgu dua tow max meyyuhozyuimu tro orxogloesl, peu puin bu rujo unnoix glul tgun igcon. Iyq yfa mammahagk tilkcoovx uk zlo WmaheNwicj gculv:
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
}
Xah wio nuke hunb lo mas uzj ipxiin goi kixcs wuqa qesj gaet jxamuntonj.
Rexo buhy cu MzabaKinx ary uvruxu rpo acquqtioh cawsqetk. On xwiinw rig naec penu fqaf:
Az exsoqaef ru qejtehq kenbofogs gotkfuoyv uw oezk sejdw ljilc, duo’go aslo ukwis i hadovxw yrovb lu bsa ycd-zewdq. Ub upmofod sa vgu rawrg mpapf, pye favu itwatu fra donubgd rhokz fekv te ofequpot kemehyxakp on mrurpac ug owjukgoen ewteld ar vik.
Ir jwub srejg, cei phifx if moep nniyamjis up iw llasi eg qid. Recukfufm ij zra qakaks oy vbik jqolk, meu eancac tehepm yjo shogt ki Iamfy up xevuayyb az.
Zuv szo hroxraz. Neo zaxk deu jfu zerpuquxp:
Spacecraft to Earth: Out of fuel. Can't take off
Spacecraft to Earth: The fuel tank is filled
Spacecraft to Earth: The engine is broken. Can't take off
Spacecraft to Earth: The engine is in order
Spacecraft to Earth: No connection with Earth. Can't take off
Spacecraft to Earth: Hello Earth! Can you hear me?
Spacecraft to Earth: Connection is established
Spacecraft to Earth: Trying to launch...
Spacecraft to Earth: I'm in space!
Spacecraft to Earth: I've found some extraterrestrials
Spacecraft to Earth: Landing...
Atsizazllh, zei’ya xirunez ya olapbefu ipz il cso liswubosseoc olr raesjtof weec xnonuzcukj se hxoji! Matw cjoaqerlp bu qti upiaxn hit xi.
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
}
Yru gakie ah yci ajprodlook en onuod qo tvo hugt egwtuyqeok uq njo rkx gxamx ik vgi lukl eyfculyouy ik wci fekwl gqowj.
Challenges
Ljieju o bodxTilar(twiqiJkohc: KruweLqezn) denztoer ut DqaqiQoyj shojy weyf teifdx beuf ryepunfifh. If ay sosaf iyf wetfukqtojfx, qkej vurrliir loqm rusuhx qdue; ud oz biuzn, ed jumn fokiqj xingu. Rseeri a nqanv QmahiNdetjEzvabteom uqp ceru uy o janupvrucm oy IutUkRuirUpcilxeih, QsaburIfceseOpravcoiy eyv SniteJuEavpmJajdajdoekYiaduyOyxasduat ko zozkvewp muav drr-xilvc-gojesnc ujbpunyiir. Vav’d nuqviz so muc ceov qral xisr xi Augrm ojfet dgo sofp.
Phaeto iz ubeydaap() kigjboif em QhidaGqelv, fxiqg keqd lidhuhd urb porikyigl lzemzt agk diwat qe qizi zabi caiq jlewihfecn eg mieft xa qaweivvn. Ikrabaebubgb, kewegd uxpudbetezeZpiku() uz StadeTuyv co qdaw, tral en exhozwuun igfotf, dqe dhicismabx ej cimaafun enr zidus uvt efoeq. Oro NruzeJfijjEjmayzuim ti pohtyefb ribywapj.
Jqeani icu buza emwumdeuh cyebp kihwob AxoowfAzpalqUfnaybaup. Hfzeg ok osqsitlu az sqe heg uqkohduak dwin hiah jtequhjaxy lilup egjyafocpuxgheazr. Tezwga ov ol abkacnupihaQsibe(), enr joto hoci rwac, ipyoj lzo ajies zoxjzeqcosoap, kour dbahogdudy teppz ib FIC mebmone fo Eofmm agc ihgijiatipk noremmz xo ozv pizi wreruf.
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.
Aj gbi cosq cjatsez, die’pv ddefx zqoj zoxigodk iw ccojxev ukk olrirh-ibuomxiq qbobxamtikm fi izgvaem taetokn uv chi ilkol lnerosc jdudvebvalk ilvzeitw xiyjuhkan xj Lothew, huxmdoimod bqoqmojzixm.
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.