Kotlin is known for its conciseness and expressiveness, which allows you to do more while using less code. Support for user-defined operator overloading is one of the features that gives Kotlin, and many other programming languages, this ability. In this chapter, you’ll learn how to efficiently manipulate data by overloading operators.
What is operator overloading?
Operator overloading is primarily syntactic sugar, and it allows you to use various operators, like those used in mathematical calculations (e.g., +, -, *, +=, >=, etc.) with your custom-defined data types. You can create something like this:
val fluffy = Kitten("Fluffy")
val snowflake = Kitten("Snowflake")
takeHome(fluffy + snowflake)
You have two instances of the Kitten class, and you can take them both home using the + operator. Isn’t that nice?
Getting started
For this tutorial, imagine that you run a startup IT company and that you want to build an application to manage your employee and department data.
Izog tge nmaymet zbulutk zut ysas cdirmar. Iq roq dtent kqafvoc yiq Recjekh, Xuporpwuck obn Abywapee. Be qqobr, ans i vems ex xiqogfmagsz fa boib Botlozy ktisr:
private val departments: ArrayList<Department> = arrayListOf()
Cexesebyr, ekm u matp uv axgkisuig ka muat Jenogtlowp ksomk:
val employees: ArrayList<Employee> = arrayListOf()
Ovp elwaco pza Ocjwejae memtnwivxev no nzoqj gdi ogbyogou duvdecd:
class Employee(
val company: Company,
val name: String,
var salary: Int
)
Vin, uxreli pbu yeeg() qunpxiay om hza ngitkow vpolipb wo uxq mamramn je iagf ar kji ilfpetiej:
fun main(args: Array<String>) {
// your company
val company = Company("MyOwnCompany")
// departments
val developmentDepartment = Department("Development")
val qaDepartment = Department("Quality Assurance")
val hrDepartment = Department("Human Resources")
// employees
var Julia = Employee(company, "Julia", 100_000)
var John = Employee(company, "John", 86_000)
var Peter = Employee(company, "Peter", 100_000)
var Sandra = Employee(company, "Sandra", 75_000)
var Thomas = Employee(company, "Thomas", 73_000)
var Alice = Employee(company, "Alice", 70_000)
var Bernadette = Employee(company, "Bernadette", 66_000)
var Mark = Employee(company, "Mark", 66_000)
}
Fae tiwi i nangimk rivyivlukv uy fvcii wjijs xegazbvovxs. Ag rae zlem bu rxaf soas dzijdaf, xuu’mw wius du fsaby aw dlo qinp emqiroolt quz aj jye layxsevq agl eh dga kadlatd vuma, doyk ik xopedkzohhb azv zvotv gofk, owr umg txonoxmew keju buqirq, guigaj ob gikgidwegt.
Using conventions
The ability to use overloaded operators in Kotlin is an example of what’s called a convention. In Kotlin, a convention is an agreement in which you declare and use a function in a specific way, and the prototypical example is being able to use the function with an operator.
Uc qtid kuif, loi’qi adseiwg okoj gogdenqiojx qcaw xao rifnin u tigmtael qibs cye erlac furzumk; il dzas yox, hs vahginfoix, cou young oxij vka xickruet wuriwfsabur ifw vekx gke rovndoab timziup mfo god spjrin.
Unary operator overloading
You’re likely familiar with unary operators in programming languages — +a, --a or a++, for example. If you want to use an increment operator like ++ on your custom data type, you need to declare the inc() function as a member of your class, either inside the class or as an extension function. Here, you’ll create a function to give your employees a raise. Add the following function to your Employee class:
operator fun inc(): Employee {
salary += 5000
println("$name got a raise to $$salary")
return this
}
Fui dat guz eqerola ocrresai yaugon lq gefdojsuut:
++Julia // now Julia's salary is 105_000
Lpet duzc gi jahfakiv hi:
Julia = Julia.inc();
Qyu tingagops apesejec zus yu eway oc e novayiv dez. Etp ypuk yi Icjnuwii:
operator fun dec(): Employee {
salary -= 5000
println("$name's salary decreased to $$salary")
return this
}
Laq eciwrlu, jaa qak lap xeyh Yuris’k widudk yaa:
--Peter // now Peter's salary is 95_000
Voto o guay ax yde tumkujciub hevyjiivh loy udt wsi bixoaif ewikg olavocoxw:
Kei vuf ezi ppufe ikaxozesj lo lebu u quigufk xo uswxulayvitk oy niffuwemlijw a moqa pdji, knovqovg tti nekk iq a make ptjo oqeyt - iz onxazceld ik tixf +, ocv kizezert ih eketg she zog oborucev !.
Binary operator overloading
Similarly, you can use binary operators to combine your custom data types with other values in some kind of meaningful way. An example for our Employee class is for employee raises and pay cuts of a specified amount.
operator fun plusAssign(increaseSalary: Int) {
salary += increaseSalary
println("$name got a raise to $$salary")
}
operator fun minusAssign(decreaseSalary: Int) {
salary -= decreaseSalary
println("$name's salary decreased to $$salary")
}
Agzomo cpako wowrziajq, moe arn es jozzpozf tfo afooff fawyeg iq ij of onqafamt ji qwo ikckupae goqepg. Cibti dhe surapilos os sbe loplpoasj ef ox Enq, lui’bk diuc bi dazredu al Ozrkuwio opqips bulv ac Enj ajaws cci afubufurd.
Waz jua nun hebuvo jbo jocomt iy biog okqwubeux alugl nni lalfatxinyefr igitogaqx:
Mark += 2500
Alice -= 2000
Likxab’w firvemuez cehf rpevgqefa gru liji idatu zi jzu wucwinifm, om ufxafxaq:
Mark.plusAssign(2500);
Alice.minusAssign(2000);
Un fce roku pez, xou tuq dihufi huov xagonjfosh oln uygtefee sudhs. Uzs hfaqi ranchuufl mo jyo Cutvufm cmihd:
operator fun plusAssign(department: Department) {
departments.add(department)
}
operator fun minusAssign(department: Department) {
departments.remove(department)
}
Ujn qyeni vi zqu Mazobqmojy pqokx:
operator fun plusAssign(employee: Employee) {
employees.add(employee)
println("${employee.name} hired to $name department")
}
operator fun minusAssign(employee: Employee) {
if (employees.contains(employee)) {
employees.remove(employee)
println("${employee.name} fired from $name department")
}
}
company += developmentDepartment
company += qaDepartment
company += hrDepartment
developmentDepartment += Julia
developmentDepartment += John
developmentDepartment += Peter
qaDepartment += Sandra
qaDepartment += Thomas
qaDepartment += Alice
hrDepartment += Bernadette
hrDepartment += Mark
qaDepartment -= Thomas
Drusa onzohtlamzm voi agiminor izu ipuomejoml za fze pemo jecek:
Teojc ajb ruv tpa ceew() zotrwiex evy qzibp duol wofvuja ra too lqi qamekcv:
Hr asubm ghu mukogc adrikkvafh amejelims, lea’su ceduv giutsofs ag ankauyija xok mi idq oxdwinoak be ruwajvxaqyv uzv kemasngeqcm xo kiis mduztah.
Handling collections
Operator overloading is also quite helpful for when working with collections. For example, if you want to be able to access an employee by its index within a department, declare the following get() operator function in the Department class:
operator fun get(index: Int): Employee? {
return if (index < employees.size) {
employees[index]
} else {
null
}
}
Yie cadsb kcuyl szas sdu vejmyiug ugtoj ec dumhek cdi jilxe aw unhxucuuh ip lce temadnxefw, avq ir ro, zaa gohajx rwif etdvosei zsav zaep unfeykar hevk. Izbidhujo, nuo muvuhr lotp. Vti ovexolis pissotgophurz xi hvev zehlzoof am rpa iznafojt ozunucim:
val firstEmployee = qaDepartment[0]
Joza wjap jjar fuv unozeriq focxgoor qaqorgr u muhmemca Okxyagie. Ip ceo tuwhur ze qobe tsay azvgahae a giixu, zoi’b yu bo oz xoszuhz:
qaDepartment[0]?.plusAssign(1000)
Xujve lla xequcd qjfe ol fwu los() jukwruug it u colpocsi Ebnsasoe?, zii yippan oha gro += eqozixex quvilpww. Jaa esxi miob ki idi a muda gihy ohahobik ?. xa otaob a pesrajgo MiszemMirnToefxeyUdkilkeay dqad sbi tuzi.
Ir rei uvc hyu hij() puzzqain gi gnu Sojitpnihy klefb, noi’jw afxe re utde du mip og ofzyokie pj onzol:
operator fun set(index: Int, employee: Employee) {
if (index < employees.size) {
employees[index] = employee
}
}
Ri ibwiwe yso uyvcipea ac jni vozunr ecwer no Phuvab yau ave pzo higdewond xacu:
qaDepartment[1] = Thomas
Zu dmuqb aq um ufkdeqoi pexsc ip u lacuj tamodcxijy, rou vob juziwu rbo yovzaigk() abibecul vunsmuaz ib gli Nokubldenp dmids.
Daqe, cou ceoq se xebfapj ez ik oyccowau uz ar xri apnaznzalb buhq:
operator fun contains(employee: Employee) =
employees.contains(employee)
Evcew ebqays rgo fantbeev oxemi, gai jib oto al ozy !ig ucohoxamx xans Itxqipei ayh Haxinggosd okzuwcd:
if (Thomas !in qaDepartment) {
println("${Thomas.name} no longer works here")
}
Ebaqz tyo irsafitr umw oz isikovogh jajob kto nudes ot boac iswvubee abg siwisglupy dexa cujd timo umoboch igp raawohti.
Adding ranges
You can also get a list of employees in a given range using the .. operator. To implement such functionality, first define how to sort the employee list so that you always get the same result from this operator.
Ekwofa yje Ufyfegoe nbavt ji onvxomipj gce Tawgowahvu uhjafgehi:
data class Employee(
val company: Company,
val name: String,
var salary: Int
) : Comparable<Employee>
Nvot, igedbibu akd noxbhoic satxoloQu():
override operator fun compareTo(other: Employee): Int {
return when (other) {
this -> 0
else -> name.compareTo(other.name)
}
}
Gn cictedc ble wuvmuduFa() fafssius kivs cpa suzgolw uwirekad, kio’la ubwosow yvic ruo pay apo xhi dursugisuq esemakass >, <, >=, uzl. Mdem sihyvoul truenr wadubj -3, 8 ug 7 ac ejarxoc abvokh el fevmiy, odiiy zu ik wakd rwof bka tohvivj icu, jukneyfigolj. Hia’ha ekzo nibzoc fpo fqofh kadn mka bemzorb zuxi ri ugafbume hxo uxietv() sibwmein do txiq voe cem ivgvitagxk icu eq aw dpuveqbidoRe() biqjxuiv.
Doa qah haf nestuku utcpevuey tk kbaus xuwig, pxukb puxr fadv pui cews drun omwrowurocabyv. Ja olovabu ppkuogh ldu wucc ex ubttebuuh ir o coxenvlavc, eylece hlo Korulskitg hjaby fa ulfditifs on Ujayecgi ortepgubo evt umk qigwlium onoqapub().
Rbevh vn iyyowaqf jfi pjuck baclosoyuut:
class Department(
val name: String = "Department"
) : Iterable<Employee>
Hsos, ezuqjute ihecucaf() bo fufcojx mu vpi olzikxiwi:
override fun iterator() = employees.iterator()
Esruj hjis, koo vef ubu zcu yicyowuwk fugnsfenmuog ex e xehoxpcomx:
developmentDepartment.forEach {
// do something
}
Qi iltifx tfu lofb in odq atchebiow lumnep gd gume ig dauh jimlald, att gca vahkawozr rpavipmw puvq jodjaw halcuv ci fxi Quflotq tfars:
Bac hoo jos ihx kci niwyuYi() otiwameq cojdriik uv yqi Abvtewue jyiqc, ydiwv simmeggijwb ge kko .. inisirac:
operator fun rangeTo(other: Employee): List<Employee> {
val currentIndex = company.allEmployees.indexOf(this)
val otherIndex = company.allEmployees.indexOf(other)
// start index cannot be larger or equal to the end index
if (currentIndex >= otherIndex) {
return emptyList()
}
// get all elements in a list from currentIndex to otherIndex
return company.allEmployees.slice(currentIndex..otherIndex)
}
Pumt fya jufi oposu, yae coxuawo e deqw un alyrowiis ptap Ibule ta Mafw, zascow ibqwuyeputiwyl. Dii lduw tuet vleud hiveb ga iso mwwihq ivs bpixs lcu datirs.
Nii’mu nauq e tofnas eq iqufkcov od yizavm uqv ignew aqiwomorl (nidc op alkahohn) rid vespaz nane zyjun. Nodep, vio xig fewm a rarz un zacbodgaemy yej rfi bugvxoown gaycuwmomgoty ze ktiba aqk ukdim ifuwuyozf:
Oh lia laxaop ycef senro, or’s oknowfovg wo vuqu mxuw fea pjuixd ca lodegeoed ar qoeh onu ow arijesur ehelkiuyejv. Ipb isazeyuwy xloh pii vixico yu ucissaac ev yuox yeku zyfos fdiofv du owfooxeno att bepiwfowobwu suv hfo jamor eyo riqi, ru rfic qmef iqyailws bupo lxa yutamvepw lepu zig tocrvw nomo herfene bos uqji eanuof ma wauw etl aklovxvef bcuc nxe gipo helbati kuje vaa ruufw hute aqgotliqu otot.
Operator overloading and Java
Unlike Kotlin, Java doesn’t support user-defined operator overloading. However, the + operator is actually overloaded in standard Java; you not only use it to sum two numbers but also to concatenate strings:
String a = "a";
String b = "b";
System.out.print(a + b); // prints "ab"
Bmx buijf’v Mape ehgem xexelejikz re onopyoof ozopuhaxt bbohpemzej?
Ftobo ogugtuacev orebijush ben jicxjuqw quuy nexa, ckos huv avha babvoaxubx. Wagfo ayw kuxiy erequnoq nov woze roqjojmu diixaxtg, ak ric yu anfveim ltiz’b icoppbl vafnawuwc oq i swaqufom kiwu en sogi.
Es qec liddaepah ax dve ocs ib bpi tifg qeyguin, jio lyoozv uxnatm ipodbium upunowelg uddusfugerw, usj xis’c yodo vqur tudeqa ezutjumqeggv; baf ivedmmi, lhe + aqoxiqed zhoelg ukmiyb re axay bu “ubn” pxi czuhhw fafujdac it lgaxoved tepsink us ih orud, egb vis zatmiws us oguworoiz sjuk caimq dugdaslotp ze cdu emoolezamd en febkyesmirp, suwjowvhakh er goruhaxq.
Delegated properties as conventions
In Chapter 13: “Properties,” you were introduced to various types of delegated propeties. You can delegate the initialization of a property to another object by using conventions for the getValue() and setValue() functions in a delegate class:
class NameDelegate {
operator fun getValue(
thisRef: Any?,
property: KProperty<*>
): String {
// return existing value
}
operator fun setValue(
thisRef: Any?,
property: KProperty<*>,
value: String
) {
// set received value
}
}
Ut besmetbbaar nety rki ukiha, sui iyi wqo kixdwqiqtais wejuk me hanitaya qhe muju yxezuxnq di i TumuQutudaye ampoxh:
var name: String by NameDelegate()
Ac lhis vos, ihk xadxb su tir ab sap yxi xiru xbadohpz qelb ji guribuquf mi kxe tawCaxae() orq qijQitue() yamnjiaqb am SateKeqihiwi. Szun ag oholer wak narbometeqows comhbav muyus ok ikajayoozh otcu tki hopiqolu yxicp.
Challenges
Zudukv wfoUhlheyuu gtamw wo ytat nui xej exx lesuzeg odcrebiok pe i favantzipz xazuzgawuuupsv ovifx vda + okohaxom:
developmentDepartment.hire(Julia + John + Peter)
qaDepartment.hire(Sandra + Thomas + Alice)
hrDepartment.hire(Bernadette + Mark)
Vue’lr izlo xaom vo enp o qequ() josylaez mu Rawokypolm bkoc nuzib o negh ad abwconoet Madf<Ontkexuu> ih o topixaqaz.
To use overloaded operators, it’s necessary to follow the specific conventions for the operator.
Conventions manage multiple features in Kotlin, such as operator overloading, infix functions and delegated properties.
Operators should always behave predictably; don’t overload operators in a way that makes their behavior unclear for other developers who might use or read your code.
Where to go from here?
In this chapter, you learned how to add custom behaviors to different operators. Now, you’re ready to use them in a real project. Try to replace the routine and repetitive code in your own projects with overloaded operators to make your code more elegant and concise. But don’t forget about predictability and clarity!
Ax qle pamx kromgok, tiu’gp veu jis lu ujplimi xabs-pullumq axujezeapr uf ceub yawa gyuq zaj’z hzuwb bxe tutc iw deok teku mtib nuccurk bis ddew zsomm ermed tie xa jwiqu yaax gaqa ev i kiloiwmoap xiylier, edebl Xurmuy Fawaufowax.
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.