516

Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

  • Upload
    others

  • View
    23

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava
Page 2: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

AndroidDevelopmentwithKotlin

LearnAndroidapplicationdevelopmentwiththeextensivefeaturesofKotlin

Page 3: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

MarcinMoskalaIgorWojda

BIRMINGHAM-MUMBAI

Page 4: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

AndroidDevelopmentwithKotlinCopyright©2017PacktPublishing

Allrightsreserved.Nopartofthisbookmaybereproduced,storedinaretrievalsystem,ortransmittedinanyformorbyanymeans,withoutthepriorwrittenpermissionofthepublisher,exceptinthecaseofbriefquotationsembeddedincriticalarticlesorreviews.

Everyefforthasbeenmadeinthepreparationofthisbooktoensuretheaccuracyoftheinformationpresented.However,theinformationcontainedinthisbookissoldwithoutwarranty,eitherexpressorimplied.Neithertheauthors,norPacktPublishing,anditsdealersanddistributorswillbeheldliableforanydamagescausedorallegedtobecauseddirectlyorindirectlybythisbook.

PacktPublishinghasendeavoredtoprovidetrademarkinformationaboutallofthecompaniesandproductsmentionedinthisbookbytheappropriateuseofcapitals.However,PacktPublishingcannotguaranteetheaccuracyofthisinformation.

Firstpublished:August2017

Productionreference:1280817

PublishedbyPacktPublishingLtd.LiveryPlace35LiveryStreetBirminghamB32PB,UK.

Page 5: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ISBN978-1-78712-368-7

www.packtpub.com

Page 6: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Credits

Authors

MarcinMoskala

IgorWojda

CopyEditor

SafisEditing

Reviewers

MikhailGlukhikh

StepanGoncharov

ProjectCoordinator

VaidehiSawant

CommissioningEditor

AaronLazar

Proofreader

SafisEditing

Page 7: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

AcquisitionEditor

ChaitanyaNair

Indexer

FrancyPuthiry

ContentDevelopmentEditor

RohitKumarSingh

Graphics

AbhinashSahu

TechnicalEditor

PavanRamchandani

ProductionCoordinator

NileshMohite

Page 8: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

AbouttheAuthorsMarcinMoskalaisanexperiencedAndroiddeveloperwhoisalwayslookingforwaystoimprove.HehasbeenpassionateaboutKotlinsinceitsearlybetarelease.HewritesarticlesforTradepressandspeaksatprogrammingconferences.

Marcinisquiteactiveintheprogrammingandopensourcecommunityandisalsopassionateaboutcognitiveanddatascience.Youcanvisithiswebsite(marcinmoskala.com),orfollowhimonGitHub(MarcinMoskala)andonTwitter(@marcinmoskala).

Iwouldliketothankmyco-workersinGamekit,Docplanner,andApreel.Iespeciallywanttothankmysupervisors,whowerenotonlysupportive,butwhoarealsoconstantsourceofknowledgeandinspiration:MateuszMikulski,KrzyysztofWolniak,BartekWilczynskiandRafalTrzeciak.IwouldliketothankMarekKaminski,GlebSmirnov,JacekJablonski,andMaciejGorskiforthecorrections,andDariuszBacinskiandJamesShvartsforreviewingthecodeofexampleapplication.AlsoIwouldliketothankmyfamilyandmygirlfriend,MajaMarkiewiczforhersupport,help,makinganenvironmentthatissupportingpassionandself-realization.

IgorWojdaisanexperiencedengineerwithover11yearsofexperienceinsoftwaredevelopment.HisadventurewithAndroidstartedafewyearsago,andheiscurrentlyworkingasaseniorAndroiddeveloperinthehealthcareindustry.IgorhasbeendeeplyinterestedinKotlindevelopmentlongbeforethe1.0versionwasofficiallyreleased,andheisanactivememberoftheKotlincommunity.Heenjoyssharinghispassionforcodingwithdevelopers.

Tolearnmoreabouthim,youcanvisitonMedium(@igorwojda)andfollowhimonTwitter(@igorwojda).

Page 9: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

IwouldalsoliketothankamazingteamatBabylon,whoarenotonlyprofessionalsbutalsotheinspiringandveryhelpfulpeople,especiallyMikolajLeszczynski,SakisKaliakoudas,SimonAttard,BalachandarKolathurMani,SergioCarabantes,JoaoAlves,TomasNavickas,MarioSanoguera,SebastienRouif.Iofferthankstoallthereviewers,especiallytechnicalreviewerStepanGoncharov,MikhailGlukhikhandmycolleagueswholivedusfeedbackonthedrafts,especiallyMichałJankowski.Ialsothankfultomyfamilyforalloftheirloveandsupport.I'dliketothankmyparentsforallowingmetofollowmyambitionsthroughoutmychildhoodandforalltheeducation.ThanksalsogotoJetBrainsforcreatingthisawesomelanguageandtotheKotlincommunityforsharingtheknowledge,beinghelpful,openandinspiring.Thisbookcouldnotbewrittenwithoutyou!Iofferspecialthankstomyfriends,especiallyKonradHamela,MarcinSobolski,MaciejGierasimiuk,RafalCupial,MichalMazurandEdytaSkibafortheirfriendship,inspirationandcontinuoussupport.Ivalueyouradviceimmensely.

Page 10: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

AbouttheReviewersMikhailGlukhikhhasgraduatedfromIoffePhysicalTechnicalSchoolin1995andfromSaintPetersburgStatePolytechnicalUniversityin2001withmasterdegreeininformationaltechnologies.During2001-2004,hewasPhDstudentinthesameuniversity,andthenhedefendedPhDthesisin2007.ThetitleofhisthesisisSynthesismethoddevelopmentofspecial-purposeinformationalandcontrolsystemswithstructuralredundancy.

MikhailworkedinKodeksSoftwareDevelopmentCenterduring1999-2000,andinEfremovResearchInstituteofElectrophysicalApparatusduring2001-2002.Since2002,heisaleaddeveloperinDigitekLabsatcomputersystemandsoftwareengineeringdepartment.Hewasaseniorlecturerofthedepartmentfrom2004to2007,from2007heisanassociateprofessor.In2013hehadone-yearstayinClausthalUniversityofTechnologyasaninvitedresearcher.In2014,heworkedatSPbofficeofIntelcorporation,sinceMarch2015,heparticipatesinKotlinlanguagedevelopmentatJetBrainscompany.

MikhailisoneofDigitekAegisdefectdetectiontoolauthors,alsoheisoneofDigitekRAtoolauthors.NowadaysprimaryR&Dareasincludecodeanalysis,codeverification,coderefactoringandcodereliabilityestimationmethods.Beforehehadalsointerestsinfault-tolerantsystemdesignandanalysisandalsoinhigh-productivedigitalsignalprocessingcomplexesdeveloping.

StepanGoncharoviscurrentlyworkingatGrabastheengineeringleadoftheDriverAndroidapp.HeisanorganizerofKotlinUserGroupSingaporewhohasdevelopedappsandgamesforAndroidsince2008.HeisaKotlinandRxJavaaddict,andobsessedwithelegantandfunctionalstylecode.Heismainlyfocusedonmobileappsarchitecture.

Stepanismakingadifferencebyspendingmoreandmoretimecontributingtoopen-sourceprojects.HeisthereviewerofLearningRxJava,byThomasNield,

Page 11: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

publishedbyPackt.

Page 12: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

www.PacktPub.comForsupportfilesanddownloadsrelatedtoyourbook,pleasevisitwww.PacktPub.com.

DidyouknowthatPacktofferseBookversionsofeverybookpublished,withPDFandePubfilesavailable?YoucanupgradetotheeBookversionatwww.PacktPub.comandasaprintbookcustomer,youareentitledtoadiscountontheeBookcopy.

[email protected].

Atwww.PacktPub.com,youcanalsoreadacollectionoffreetechnicalarticles,signupforarangeoffreenewslettersandreceiveexclusivediscountsandoffersonPacktbooksandeBooks.

https://www.packtpub.com/mapt

Getthemostin-demandsoftwareskillswithMapt.MaptgivesyoufullaccesstoallPacktbooksandvideocourses,aswellasindustry-leadingtoolstohelpyouplanyourpersonaldevelopmentandadvanceyourcareer.

Page 13: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Whysubscribe?FullysearchableacrosseverybookpublishedbyPacktCopyandpaste,print,andbookmarkcontentOndemandandaccessibleviaawebbrowser

Page 14: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

CustomerFeedbackThanksforpurchasingthisPacktbook.AtPackt,qualityisattheheartofoureditorialprocess.Tohelpusimprove,pleaseleaveusanhonestreviewonthisbook'sAmazonpageathttps://www.amazon.com/dp/1787123685.

Ifyou'dliketojoinourteamofregularreviewers,youcane-mailusatcustomerreviews@packtpub.com.WeawardourregularreviewerswithfreeeBooksandvideosinexchangefortheirvaluablefeedback.Helpusberelentlessinimprovingourproducts!

Page 15: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

TableofContentsPreface

WhatthisbookcoversWhatyouneedforthisbookWhothisbookisforConventionsReaderfeedbackCustomersupport

DownloadingtheexamplecodeErrataPiracyQuestions

1. BeginningYourKotlinAdventureSayhellotoKotlinAwesomeKotlinexamplesDealingwithKotlincode

KotlinPlaygroundAndroidStudio

ConfiguringKotlinfortheprojectUsingKotlininanewAndroidprojectJavatoKotlinconverter(J2K)AlternativewaystorunKotlincode

KotlinunderthehoodTheKotlinstandardlibrary

MorereasonstouseKotlinSummary

2. LayingaFoundationVariablesTypeinferenceStrictnullsafety

SafecallElvisoperatorNotnullassertionLet

NullabilityandJavaCasts

Safe/unsafecastoperator

Page 16: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

SmartcastsTypesmartcastsNon-nullablesmartcast

PrimitivedatatypesNumbersCharArraysTheBooleantype

CompositedatatypesStrings

StringtemplatesRangesCollections

StatementsversusexpressionsControlflow

TheifstatementThewhenexpressionLoops

TheforloopThewhileloopOtheriterationsBreakandcontinue

ExceptionsThetry...catchblock

Compile-timeconstantsDelegatesSummary

3. PlayingwithFunctionsBasicfunctiondeclarationandusages

ParametersReturningfunctions

VarargparameterSingle-expressionfunctionsTail-recursivefunctionsDifferentwaysofcallingafunction

DefaultargumentsvaluesNamedargumentssyntax

Top-levelfunctionsTop-levelfunctionsunderthehood

Page 17: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

LocalfunctionsNothingreturntypeSummary

4. ClassesandObjectsClasses

ClassdeclarationProperties

Read-writeversusread-onlypropertyPropertyaccesssyntaxbetweenKotlinandJava

IncrementanddecrementoperatorsCustomgetters/setters

ThegetterversuspropertydefaultvalueLate-initializedpropertiesAnnotatingpropertiesInlineproperties

ConstructorsPropertyversusconstructorparameterConstructorwithdefaultarguments

PatternsInheritance

TheJvmOverloadsannotationInterfacesDataclasses

TheequalsandhashCodemethodThetoStringmethodThecopymethodDestructivedeclarations

OperatoroverloadingObjectdeclarationObjectexpressionCompanionobjects

CompanionobjectinstantiationEnumclassesInfixcallsfornamedmethodsVisibilitymodifiers

InternalmodifierandJavabytecodeSealedclassesNestedclassesImportaliasesSummary

Page 18: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

5. FunctionsasFirst-ClassCitizensFunctiontype

Whatisfunctiontypeunderthehood?AnonymousfunctionsLambdaexpressions

ImplicitnameofasingleparameterHigher-orderfunctions

ProvidingoperationstofunctionsObserver(Listener)patternCallbackafterathreadedoperation

CombinationofnamedargumentsandlambdaexpressionsLastlambdainargumentconvention

NamedcodesurroundingProcessingdatastructuresusingLINQstyle

JavaSAMsupportinKotlinNamedKotlinfunctiontypes

NamedparametersinfunctiontypeTypealias

UnderscoreforunusedvariablesDestructuringinlambdaexpressionsInlinefunctions

ThenoinlinemodifierNon-localreturnsLabeledreturninlambdaexpressionsCrossinlinemodifierInlineproperties

FunctionReferencesSummary

6. GenericsAreYourFriendsGenerics

TheneedforgenericsTypeparametersversustypearguments

GenericconstraintsNullability

VarianceVariancemodifiersUse-sitevarianceversusdeclaration-sitevarianceCollectionvarianceVarianceproducer/consumerlimitationInvariantconstructor

Page 19: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

TypeerasureReifiedtypeparameters

ThestartActivitymethodStar-projectionsTypeparameternamingconventionsSummary

7. ExtensionFunctionsandPropertiesExtensionfunctions

ExtensionfunctionsunderthehoodNomethodoverridingAccesstoreceiverelementsExtensionsareresolvedstatically

CompanionobjectextensionsOperatoroverloadingusingextensionfunctionsWhereshouldtop-levelextensionfunctionsbeused?

ExtensionpropertiesWhereshouldextensionpropertiesbeused?

MemberextensionfunctionsandpropertiesTypeofreceiversMemberextensionfunctionsandpropertiesunderthehood

GenericextensionfunctionsCollectionprocessing

KotlincollectiontypehierarchyThemap,filter,flatMapfunctionsTheforEachandonEachfunctionsThewithIndexandindexedvariantsThesum,count,min,max,andsortedfunctionsOtherstreamprocessingfunctionsExamplesofstreamcollectionprocessingSequence

FunctionliteralswithreceiverKotlinstandardlibraryfunctions

TheletfunctionUsingtheapplyfunctionforinitializationThealsofunctionTherunandwithfunctionThetofunction

Domain-specificlanguageAnko

Summary

Page 20: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

8. DelegatesClassdelegation

DelegationpatternDecoratorpattern

PropertydelegationWhataredelegatedproperties?Predefineddelegates

ThelazyfunctionThenotNullfunctionTheobservabledelegateThevetoabledelegatePropertydelegationtoMaptype

CustomdelegatesViewbingingPreferencebindingProvidingadelegate

Summary9. MakingYourMarvelGalleryApplication

MarvelGalleryHowtousethischapterMakeanemptyprojectCharactergallery

ViewimplementationNetworkdefinitionBusinesslogicimplementationPuttingitalltogether

CharactersearchCharacterprofiledisplay

Summary

Page 21: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

PrefaceNowadays,theAndroidapplicationdevelopmentprocessisquiteextensive.Overthelastfewyears,wehaveseenhowvarioustoolshaveevolvedtomakeourliveseasier.However,onecoreelementofAndroidapplicationdevelopmentprocesshasn’tchangedmuchovertime,Java.TheAndroidplatformadaptstonewerversionsofJava,buttobeabletousethem,weneedtowaitforaverylongtimeuntilnewAndroiddevicesreachpropermarketpropagation.Also,developingapplicationsinJavacomeswithitsownsetofchallengessinceJavaisanoldlanguagewithmanydesignissuesthatcan’tbesimplyresolvedduetobackwardcompatibilityconstraints.

Kotlin,ontheotherhand,isanewbutstablelanguagethatcanrunonallAndroiddevicesandsolvemanyissuesthatJavacannot.ItbringslotsofprovenprogrammingconceptstotheAndroiddevelopmenttable.Itisagreatlanguagethatmakesadeveloper'slifemucheasierandallowstoproducemoresecure,expressive,andconcisecode.

Thisbookisaneasy-to-follow,practicalguidethatwillhelpyoutospeedupandimprovetheAndroiddevelopmentprocessusingKotlin.WewillpresentmanyshortcutsandimprovementsoverJavaandnewwaysofsolvingcommonproblems.Bytheendofthisbook,youwillbefamiliarwithKotlinfeaturesandtools,andyouwillbeabletodevelopanAndroidapplicationentirelyinKotlin.

Page 22: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

WhatthisbookcoversChapter1,BeginningYourKotlinAdventure,discussesKotlinlanguage,itsfeaturesandreasonstouseit.We'llintroducereadertotheKotlinplatformandshowhowKotlinfitsintoAndroiddevelopmentprocess.

Chapter2,LayingaFoundation,islargelydevotedtothebuildingblocksoftheKotlin.Itpresentsvariousconstructs,datatypes,andfeaturesthatmakeKotlinanenjoyablelanguagetoworkwith.

Chapter3,PlayingwithFunctions,explainsvariouswaystodefineandcallafunction.Wewillalsodiscussfunctionmodifiersandlookatpossiblelocationswherefunctioncanbedefined.

Chapter4,ClassesandObjects,discussestheKotlinfeaturesrelatedtoobject-orientedprogramming.Youwilllearnaboutdifferenttypesofclass.Wewillalsoseefeaturesthatimprovereadability:propertiesoperatoroverloadingandinfixcalls.

Chapter5,FunctionsasFirst-ClassCitizens,coversKotlinsupportforfunctionalprogrammingandfunctionsasfirst-classcitizens.Wewilltakeacloserlookatlambdas,higherorderfunctions,andfunctiontypes.

Chapter6,GenericsAreYourFriends,exploresthesubjectsofgenericclasses,interfaces,andfunctions.WewilltakeacloserlookattheKotlingenerictypesystem.

Chapter7,ExtensionFunctionsandProperties,demonstrateshowtoaddnewbehaviortoanexistingclasswithoutusinginheritance.Wewillalsodiscusssimplerwaystodealwithcollectionsandstreamprocessing.

Chapter8,Delegates,showshowKotlinsimplifiesclassdelegationduetobuilt-inlanguagesupport.Wewillseehowtouseitbothbyusingbuilt-inpropertydelegatesandbydefiningcustomones.

Chapter9,MakingYourMarvelGalleryApplication,utilizesmostofthefeatures

Page 23: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

discussedinthebookanduseittobuildafullyfunctionalAndroidapplicationinKotlin.

Page 24: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

WhatyouneedforthisbookTotestandusethecodepresentedinthisbook,youneedonlyAndroidStudioinstalled.Chapter1,BeginningyourKotlinAdventure,explainshowanewprojectcanbestartedandhowtheexamplespresentedherecanbechecked.Italsodescribeshowmostofthecodepresentedherecanbetestedwithoutanyprograminstalled.

Page 25: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

WhothisbookisforTousethisbook,youshouldtobefamiliarwithtwoareas:

YouneedtoknowJavaandobject-orientedprogrammingconcepts,includingobjects,classes,constructors,interfaces,methods,getters,setters,andgenerictypes.So,ifthisareadoesnotringabell,itwillbedifficulttofullyunderstandtherestofthisbook.StartinsteadwithanintroductoryJavabookandreturntothisbookafterward.Thoughnotmandatory,understandingtheAndroidplatformismuchdesirablebecauseitwillhelpyoutounderstandthepresentedexamplesinmoredetail,andyou’llhavedeeperunderstandingtheproblemsthataresolvedbyKotlin.IfyouareanAndroiddeveloperwith6-12monthsofexperienceoryouhavecreatedfewAndroidapplications,you’llbefine.Ontheotherhand,ifyoufeelcomfortablewithOOPconceptsbutyourknowledgeofAndroidplatformislimited,youwillprobablystillbeOKformostofthebook.

Beingopen-mindedandeagertolearnnewtechnologieswillbeveryhelpful.Ifsomethingmakesyoucuriousorcatchesyourattention,feelfreetotestitandplaywithitwhileyouarereadingthisbook

Page 26: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ConventionsInthisbook,youwillfindanumberoftextstylesthatdistinguishbetweendifferentkindsofinformation.Herearesomeexamplesofthesestylesandanexplanationoftheirmeaning.

Codewordsintext,databasetablenames,foldernames,filenames,fileextensions,pathnames,dummyURLs,userinput,andTwitterhandlesareshownasfollows:"Let'slookattherangedatatype,whichallowstodefineend-inclusiveranges."

Ablockofcodeissetasfollows:

valcapitol="England"to"London"

println(capitol.first)//Prints:England

println(capitol.second)//Prints:London

Whenwewishtodrawyourattentiontoaparticularpartofacodeblock,therelevantlinesoritemsaresetinbold:

ext.kotlin_version='1.1.3'

repositories{

maven{url'https://maven.google.com'}

jcenter()

}

Anycommand-lineinputoroutputiswrittenasfollows:

sdkinstallkotlin

Newtermsandimportantwordsareshowninbold.Wordsthatyouseeonthescreen,forexample,inmenusordialogboxes,appearinthetextlikethis:"Setname,package,andlocationforthenewproject.RemembertotickIncludeKotlinsupportoption."

Warningsorimportantnotesappearlikethis.

Tipsandtricksappearlikethis.

Page 27: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava
Page 28: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ReaderfeedbackFeedbackfromourreadersisalwayswelcome.Letusknowwhatyouthinkaboutthisbook-whatyoulikedordisliked.Readerfeedbackisimportantforusasithelpsusdeveloptitlesthatyouwillreallygetthemostoutof.

Tosendusgeneralfeedback,[email protected],andmentionthebook'stitleinthesubjectofyourmessage.

Ifthereisatopicthatyouhaveexpertiseinandyouareinterestedineitherwritingorcontributingtoabook,seeourauthorguideatwww.packtpub.com/authors.

Page 29: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

CustomersupportNowthatyouaretheproudownerofaPacktbook,wehaveanumberofthingstohelpyoutogetthemostfromyourpurchase.

Page 30: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

DownloadingtheexamplecodeYoucandownloadtheexamplecodefilesforthisbookfromyouraccountathttp://www.packtpub.com.Ifyoupurchasedthisbookelsewhere,youcanvisithttp://www.packtpub.com/supportandregistertohavethefilese-maileddirectlytoyou.Youcandownloadthecodefilesbyfollowingthesesteps:

1. Loginorregistertoourwebsiteusingyoure-mailaddressandpassword.2. HoverthemousepointerontheSUPPORTtabatthetop.3. ClickonCodeDownloads&Errata.4. EnterthenameofthebookintheSearchbox.5. Selectthebookforwhichyou'relookingtodownloadthecodefiles.6. Choosefromthedrop-downmenuwhereyoupurchasedthisbookfrom.7. ClickonCodeDownload.

Oncethefileisdownloaded,pleasemakesurethatyouunziporextractthefolderusingthelatestversionof:

WinRAR/7-ZipforWindowsZipeg/iZip/UnRarXforMac7-Zip/PeaZipforLinux

ThecodebundleforthebookisalsohostedonGitHubathttps://github.com/PacktPublishing/Android-Development-with-Kotlin.Wealsohaveothercodebundlesfromourrichcatalogofbooksandvideosavailableathttps://github.com/PacktPublishing/.Checkthemout!

Page 31: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ErrataAlthoughwehavetakeneverycaretoensuretheaccuracyofourcontent,mistakesdohappen.Ifyoufindamistakeinoneofourbooks-maybeamistakeinthetextorthecode-wewouldbegratefulifyoucouldreportthistous.Bydoingso,youcansaveotherreadersfromfrustrationandhelpusimprovesubsequentversionsofthisbook.Ifyoufindanyerrata,pleasereportthembyvisitinghttp://www.packtpub.com/submit-errata,selectingyourbook,clickingontheErrataSubmissionFormlink,andenteringthedetailsofyourerrata.Onceyourerrataareverified,yoursubmissionwillbeacceptedandtheerratawillbeuploadedtoourwebsiteoraddedtoanylistofexistingerrataundertheErratasectionofthattitle.

Toviewthepreviouslysubmittederrata,gotohttps://www.packtpub.com/books/content/supportandenterthenameofthebookinthesearchfield.TherequiredinformationwillappearundertheErratasection.

Page 32: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

PiracyPiracyofcopyrightedmaterialontheInternetisanongoingproblemacrossallmedia.AtPackt,wetaketheprotectionofourcopyrightandlicensesveryseriously.IfyoucomeacrossanyillegalcopiesofourworksinanyformontheInternet,pleaseprovideuswiththelocationaddressorwebsitenameimmediatelysothatwecanpursuearemedy.

Pleasecontactusatcopyright@packtpub.comwithalinktothesuspectedpiratedmaterial.

Weappreciateyourhelpinprotectingourauthorsandourabilitytobringyouvaluablecontent.

Page 33: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

QuestionsIfyouhaveaproblemwithanyaspectofthisbook,[email protected],andwewilldoourbesttoaddresstheproblem.

Page 34: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

BeginningYourKotlinAdventureKotlinisgreatlanguagethatmakesAndroiddevelopmenteasier,faster,andmuchmorepleasant.Inthischapter,wewilldiscusswhatKotlinreallyisandlookatmanyKotlinexamplesthatwillhelpusbuildevenbetterAndroidapplications.WelcometotheamazingjourneyofKotlin,thatwillchangethewayyouthinkaboutwritingcodeandsolvingcommonprogrammingproblems.

Inthischapter,wewillcoverthefollowingtopics:

FirststepswithKotlinPracticalKotlinexamplesCreatingnewKotlinprojectinAndroidStudioMigratingexistingJavaprojecttoKotlinTheKotlinstandardlibrary(stdlib)WhyKotlinisagoodchoicetolearn

Page 35: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

SayhellotoKotlinKotlinisamodern,staticallytyped,Android-compatiblelanguagethatfixesmanyJavaproblems,suchasnullpointerexceptionsorexcessivecodeverbosity.KotlinisalanguageinspiredbySwift,Scala,Groovy,C#,andmanyotherlanguages.KotlinwasdesignedbyJetBrainsprofessionals,basedonanalysisofbothdevelopersexperiences,bestusageguidelines(mostimportantarecleancodeandeffectiveJava),anddataaboutthislanguage'susage.Deepanalysisofotherprogramminglanguageshasbeendone.Kotlintrieshardtonotrepeatthemistakesfromotherlanguagesandtakeadvantageoftheirmostusefulfeatures.WhenworkingwithKotlin,wecanreallyfeelthatthisisamatureandwell-designedlanguage.

Kotlintakesapplicationdevelopmenttoawholenewlevelbyimprovingcodequalityandsafetyandboostingdeveloperperformance.OfficialKotlinsupportfortheAndroidplatformwasannouncedbyGooglein2017,buttheKotlinlanguagehasbeenhereforsometime.IthasaveryactivecommunityandKotlinadoptionontheAndroidplatformisalreadygrowingquickly.WecandescribeKotlinasasafe,expressive,concise,versatile,andtool-friendlylanguagethathasgreatinteroperabilitywithJavaandJavaScript.Let'sdiscussthesefeatures:

Safety:Kotlinofferssafetyfeaturesintermsofnullabilityandimmutability.Kotlinisstaticallytyped,sothetypeofeveryexpressionisknownatcompiletime.Thecompilercanverifythatwhateverpropertyormethodthatwearetryingtoaccessoraparticularclassinstanceactuallyexists.ThisshouldbefamiliarfromJavawhichisalsostaticallytyped,butunlikeJava,Kotlintypesystemismuchmorestrict(safe).Wehavetoexplicitlytellthecompilerwhetherthegivenvariablecanstorenullvalues.ThisallowsmakingtheprogramfailatcompiletimeinsteadofthrowingaNullPointerExceptionatruntime:

Page 36: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Easydebugging:Bugscanbedetectedmuchfasterduringthedevelopmentphaseinsteadofcrashingtheapplicationafteritisreleasedandthusdamagingtheuserexperience.Kotlinoffersaconvenientwaytoworkwithimmutabledata.Forexample,itcandistinguishmutable(read-write)andimmutable(read-only)collectionsbyprovidingconvenientinterfaces(underthehoodcollectionsarestillmutable).Conciseness:MostoftheJavaverbositywaseliminated.Weneedlesscodetoachievecommontasksandthustheamountofboilerplatecodeisgreatlyreduced,evencomparingKotlintoJava8.Asaresult,thecodeisalsoeasiertoreadandunderstand(expressive).Interoperability:KotlinisdesignedtoseamlesslyworksidebysidewithJava(cross-languageproject).TheexistingecosystemofJavalibrariesandframeworksworkswithKotlinwithoutanyperformancepenalties.ManyJavalibrarieshaveevenKotlin-specificversionsthatallowmoreidiomaticusagewithKotlin.KotlinclassescanalsobedirectlyinstantiatedandtransparentlyreferencedfromJavacodewithoutanyspecialsemanticsandviceversa.ThisallowsustoincorporateKotlinintoexistingAndroidprojectsanduseKotlineasilytogetherwithJava(ifwewantto).Versatility:Wecantargetmanyplatforms,includingmobileapplications(Android),server-sideapplications(backend),desktopapplications,frontendcoderunninginthebrowser,andevenbuildsystems(Gradle).

Anyprogramminglanguageisonlyasgoodasitstoolsupport.KotlinhasoutstandingsupportformodernIDEssuchasAndroidStudio,IntelliJIdea,andEclipse.Commontaskslikecodeassistanceorrefactoringarehandledproperly.TheKotlinteamworkshardtomaketheKotlinpluginbetterwitheverysinglerelease.Mostofthebugsarequicklyfixedandmanyofthefeaturesrequestedbythecommunityareimplemented.

Kotlinbugtracker:https://youtrack.jetbrains.com/issues/KTKotlinslackchannel:http://slack.kotlinlang.org/

AndroidapplicationdevelopmentbecomesmuchmoreefficientandpleasantwithKotlin.KotliniscompatiblewithJDK6,soapplicationscreatedinKotlinrunsafelyevenonoldAndroiddevicesthatprecedeAndroid4.

Kotlinaimstobringyouthebestofbothworldsbycombiningconceptsand

Page 37: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

elementsfrombothproceduralandfunctionalprogramming.Itfollowsmanyguidelinesaredescribedinthebook,EffectiveJava,2ndEdition,byJoshuaBlochwhichisconsideredmustreadabookforeveryJavadeveloper.

Ontopofthat,Kotlinisopensourced,sowecancheckouttheprojectandbeactivelyinvolvedinanyaspectoftheKotlinprojectsuchasKotlinplugins,compilers,documentationsorKotlinlanguageitself.

Page 38: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

AwesomeKotlinexamplesKotlinisreallyeasytolearnforAndroiddevelopersbecausethesyntaxissimilartoJavaandKotlinoftenfeelslikenaturalJavaevolution.Atthebeginning,adeveloperusuallywritesKotlincodehavinginmindhabitsfromJava,butafterawhile,itisveryeasytomovetomoreidiomaticKotlinsolutions.Let'slookatsomecoolKotlinfeatures,andseewhereKotlinmayprovidebenefitsbysolvingcommonprogrammingtasksinaneasier,moreconcise,andmoreflexibleway.Wehavetriedtokeepexamplessimpleandself-explanatory,buttheyutilizecontentfromvariouspartsofthisbook,soit'sfineiftheyarenotfullyunderstoodatthispoint.ThegoalofthissectionistofocusonthepossibilitiesandpresentwhatcanbeachievedbyusingKotlin.Thissectiondoesnotnecessarilyneedtofullydescribehowtoachieveit.Let'sstartwithavariabledeclaration:

varname="Igor"//InferredtypeisString

name="Marcin"

NoticethatKotlindoesnotrequiresemicolons.Youcanstillusethem,buttheyareoptional.Wealsodon'tneedtospecifyavariabletypebecauseit'sinferredfromthecontext.Eachtimethecompilercanfigureoutthetypefromthecontextwedon'thavetoexplicitlyspecifyit.Kotlinisastronglytypedlanguage,soeachvariablehasanadequatetype:

varname="Igor"

name=2//Error,becausenametypeisString

ThevariablehasaninferredStringtype,soassigningadifferentvalue(integer)willresultincompilationerror.Now,let'sseehowKotlinimprovesthewaytoaddmultiplestringsusingstringtemplates:

valname="Marcin"

println("Mynameis$name")//Prints:MynameisMarcin

Weneednomorejoiningstringsusingthe+character.InKotlin,wecaneasilyincorporatesinglevariableorevenwholeexpressionintostringliterals:

valname="Igor"

println("Mynameis${name.toUpperCase()}")

//Prints:MynameisIGOR

Page 39: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

InJavaanyvariablecanstorenullvalues.InKotlinstrictnullsafetyforcesustoexplicitlymarkeachvariablethatcanstorenullablevalues:

vara:String="abc"

a=null//compilationerror

varb:String?="abc"

b=null//Itiscorrect

Addingaquestionmarktoadatatype(stringversusstring?),wesaythatvariablecanbenullable(canstorenullreferences).Ifwedon'tmarkvariableasnullable,wewillnotbeabletoassignanullablereferencetoit.Kotlinalsoallowstodealwithnullablevariablesinproperways.Wecanusesafecalloperatortosafelycallmethodsonpotentiallynullablevariables:

savedInstanceState?.doSomething

ThemethoddoSomethingwillbeinvokedonlyifsavedInstanceStatehasanon-nullvalue,otherwisethemethodcallwillbeignored.ThisisKotlin'ssafewaytoavoidnullpointerexceptionsthataresocommoninJava.

Kotlinalsohasseveralnewdatatypes.Let'slookattheRangedatatypethatallowsustodefineendinclusiveranges:

for(iin1..10){

print(i)

}//12345678910

KotlinintroducesthePairdatatypethat,combinedwithinfixnotation,allowsustoholdacommonpairofvalues:

valcapitol="England"to"London"

println(capitol.first)//Prints:England

println(capitol.second)//Prints:London

Wecandeconstructitintoseparatevariablesusingdestructivedeclarations:

val(country,city)=capitol

println(country)//Prints:England

println(city)//Prints:London

Wecaneveniteratethroughalistofpairs:

valcapitols=listOf("England"to"London","Poland"to"Warsaw")

for((country,city)incapitols){

println("Capitolof$countryis$city")

}

Page 40: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

//Prints:

//CapitolofEnglandisLondon

//CapitolofPolandisWarsaw

Alternatively,wecanusetheforEachfunction:

valcapitols=listOf("England"to"London","Poland"to"Warsaw")

capitols.forEach{(country,city)->

println("Capitolof$countryis$city")

}

NotethatKotlindistinguishesbetweenmutableandimmutablecollectionsbyprovidingasetofinterfacesandhelpermethods(ListversusMutableList,SetversusSetversusMutableSet,MapversusMutableMap,andsoon):

vallist=listOf(1,2,3,4,5,6)//InferredtypeisList

valmutableList=mutableListOf(1,2,3,4,5,6)

//InferredtypeisMutableList

Immutablecollectionmeansthatthecollectionstatecan'tchangeafterinitialization(wecan'tadd/removeitems).Mutablecollection(quiteobviously)meansthatthestatecanchange.

Withlambdaexpressions,wecanusetheAndroidframeworkbuildinaveryconciseway:

view.setOnClickListener{

println("Click")

}

Kotlinstandardlibrary(stdlib)containsmanyfunctionsthatallowustoperformoperationsoncollectionsinsimpleandconciseway.Wecaneasilyperformstreamprocessingonlists:

valtext=capitols.map{(country,_)->country.toUpperCase()}

.onEach{println(it)}

.filter{it.startsWith("P")}

.joinToString(prefix="CountriesprefixP:")

//Prints:ENGLANDPOLAND

println(text)//Prints:CountriesprefixP:POLAND

.joinToString(prefix="CountriesprefixP:")

Noticethatwedon'thavetopassparameterstoalambda.Wecanalsodefineourownlambdasthatwillallowustowritecodeincompletelynewway.ThislambdawillallowustorunaparticularpieceofcodeonlyinAndroidMarshmallowornewer.

Page 41: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

inlinefunsupportsMarshmallow(code:()->Unit){

if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.M)

code()

}

//usage

supportsMarshmallow{

println("ThiscodewillonlyrunonAndroidNougatandnewer")

}

WecanmakeasynchronousrequestseasilyanddisplayresponsesonthemainthreadusingthedoAsyncfunction:

doAsync{

varresult=runLongTask()//runsonbackgroundthread

uiThread{

toast(result)//runonmainthread

}

}

Smartcastsallowustowritecodewithoutperformingredundantcasting:

if(xisString){

print(x.length)//xisautomaticallycastedtoString

}

x.length//error,xisnotcastedtoaStringoutsideifblock

if(x!isString)

return

x.length//xisautomaticallycastedtoString

TheKotlincompilerknowsthatthevariablexisofthetypeStringafterperformingacheck,soitwillautomaticallycastittotheStringtype,allowingittocallallmethodsandaccessallpropertiesoftheStringclasswithoutanyexplicitcasts.

Sometimes,wehaveasimplefunctionthatreturnsthevalueofasingleexpression.Inthiscase,wecanuseafunctionwithanexpressionbodytoshortenthesyntax:

funsum(a:Int,b:Int)=a+b

println(sum(2+4))//Prints:6

Usingdefaultargumentsyntax,wecandefinethedefaultvalueforeachfunctionargumentandcallitinvariousways:

funprintMessage(product:String,amount:Int=0,

name:String="Anonymous"){

Page 42: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

println("$namehas$amount$product")

}

printMessage("oranges")//Prints:Anonymoushas0oranges

printMessage("oranges",10)//Prints:Anonymoushas10oranges

printMessage("oranges",10,"Johny")

//Prints:Johnyhas10oranges

Theonlylimitationisthatweneedtosupplyallargumentswithoutdefaultvalues.Wecanalsousenamedargumentsyntaxtospecifyfunctionarguments:

printMessage("oranges",name="Bill")

Thisalsoincreasesreadabilitywheninvokingthefunctionwithmultipleparametersinthefunctioncall.

Thedataclassesgiveaveryeasywaytodefineandoperateonclassesfromthedatamodel.Todefineaproperdataclass,wewillusethedatamodifierbeforetheclassname:

dataclassBall(varsize:Int,valcolor:String)

valball=Ball(12,"Red")

println(ball)//Prints:Ball(size=12,color=Red)

Noticethatwehaveareallynice,humanreadablestringrepresentationoftheclassinstanceandwedonotneedthenewkeywordtoinstantiatetheclass.Wecanalsoeasilycreateacustomcopyoftheclass:

valball=Ball(12,"Red")

println(ball)//prints:Ball(size=12,color=Red)

valsmallBall=ball.copy(size=3)

println(smallBall)//prints:Ball(size=3,color=Red)

smallBall.size++

println(smallBall)//prints:Ball(size=4,color=Red)

println(ball)//prints:Ball(size=12,color=Red)

Theprecedingconstructsmakeworkingwithimmutableobjectsveryeasyandconvenient.

OneofthebestfeaturesinKotlinareextensions.Theyallowustoaddnewbehavior(amethodorproperty)toanexistingclasswithoutchangingitsimplementation.Sometimeswhenyouworkwithalibraryorframework,youwouldliketohaveextramethodorpropertyforcertainclass.Extensionsareagreatwaytoaddthosemissingmembers.ExtensionsreducecodeverbosityandremovetheneedtouseutilityfunctionsknownfromJava(forexample,the

Page 43: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

StringUtilsclass).Wecaneasilydefineextensionsforcustomclasses,third-partylibraries,orevenAndroidframeworkclasses.Firstofall,ImageViewdoesnothavetheabilitytoloadimagesfromnetwork,sowecanaddtheloadImageextensionmethodtoloadimagesusingthePicassolibrary(animageloadinglibraryforAndroid):

funImageView.loadUrl(url:String){

Picasso.with(context).load(url).into(this)

}

\\usage

imageView.loadUrl("www.test.com\\image1.png")

WecanalsoaddasimplemethoddisplayingtoaststotheActivityclass:

funContext.toast(text:String){

Toast.makeText(this,text,Toast.LENGTH_SHORT).show()

}

//usage(insideActivityclass)

toast("Hello")

Therearemanyplaceswhereusageofextensionswillmakeourcodesimplerandmoreconcise.UsingKotlin,wecanfullytakeadvantageoflambdastosimplifyKotlincodeevenmore.

InterfacesinKotlincanhavedefaultimplementationsaslongastheydon'tholdanystate:

interfaceBasicData{

valemail:String

valname:String

get()=email.substringBefore("@")

}

InAndroid,therearemanyapplicationswherewewanttodelayobjectinitializationuntilitisneeded(used).Tosolvethisproblem,wecanusedelegates:

valretrofitbylazy{

Retrofit.Builder()

.baseUrl("https://www.github.com")

.addConverterFactory(MoshiConverterFactory.create())

.build()

}

Retrofit(apopularAndroidnetworkingframework)propertyinitializationwillbedelayeduntilthevalueisaccessedforthefirsttime.Lazyinitializationmay

Page 44: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

resultinfasterAndroidapplicationstartuptimesinceloadingisdeferredtowhenthevariableisaccessed.Thisisagreatwaytoinitializemultipleobjectsinsideaclass,especiallywhennotallofthemarealwaysneeded(forcertainclassusagescenario,wemayneedonlyspecificobjects)orwhennoteveryoneofthemisneededinstantlyafterclasscreation.

AllthepresentedexamplesareonlyaglimpseofwhatcanbeaccomplishedwithKotlin.WewilllearnhowtoutilizethepowerofKotlinthroughoutthisbook.

Page 45: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

DealingwithKotlincodeTherearemultiplewaysofmanagingandrunningKotlincode.WewillmainlyfocusonAndroidStudioandKotlinPlayground.

Page 46: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

KotlinPlaygroundThefastestwaytotryKotlincodewithouttheneedtoinstallanysoftwareisKotlinPlayground(https://try.kotlinlang.org).WecanrunKotlincodethereusingJavaScriptorJVMKotlinimplementationsandeasilyswitchbetweendifferentKotlinversions.AllthecodeexamplesfromthebookthatdoesnotrequiretheAndroidframeworkdependenciesandcanbeexecutedusingKotlinPlayground.

ThemainfunctionistheentrypointofeveryKotlinapplication.Thisfunctioniscalledwhenanyapplicationstarts,sowemustplacecodefromthebook

Page 47: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

examplesinthebodyofthismethod.WecanplacecodedirectlyorjustplaceacalltoanotherfunctioncontainingmoreKotlincode:

funmain(args:Array<String>){

println("Hello,world!")

}

AndroidApplicationshavemultipleentrypoints.mainfunctioniscalledimplicitlybytheAndroidframework,sowecan'tuseittorunKotlincodeonAndroidplatform.

Page 48: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

AndroidStudioAllAndroidStudio'sexistingtoolsworkwithKotlincode.Wecaneasilyusedebugging,lintchecks,havepropercodeassistance,refactoringandmore.MostofthethingsworkthesamewayasforJava,sothebiggestnoticeablechangeistheKotlinlanguagesyntax.AllweneedtodoistoconfigureKotlinintheproject.

Androidapplicationshavemultipleentrypoints(differentintentscanstartdifferentcomponentsintheapplication)andrequireAndroidframeworkdependencies.Torunbookexamples,weneedtoextendtheActivityclassandplacecodethere.

Page 49: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ConfiguringKotlinfortheprojectStartingfromAndroidStudio3.0,fulltoolingsupportforKotlinwasadded.InstallationoftheKotlinpluginisnotrequiredandKotlinisintegratedevendeeperintotheAndroiddevelopmentprocess.

TouseKotlinwithAndroidStudio2.x,wemustmanuallyinstalltheKotlinplugin.Toinstallit,weneedtogotoAndroidStudio|File|Settings|Plugins|InstallJetBrainsplugin...|KotlinandpresstheInstallbutton:

TobeabletouseKotlin,weneedtoconfigureKotlininourproject.ForexistingJavaprojects,weneedtoruntheConfigureKotlininprojectaction(theshortcutinWindowsisCtrl+Shift+A,andinmacOS,itiscommand+shift+A)orusethecorrespondingTools|Kotlin|ConfigureKotlininProjectmenuitem:

Page 50: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Then,selectAndroidwithGradle:

Finally,weneedtoselecttherequiredmodulesandtheproperKotlinversion:

TheprecedingconfigurationscenarioalsoappliestoallexistingAndroidprojectsthatwereinitiallycreatedinJava.StartingfromAndroidStudio3.0,wecanalsochecktheIncludeKotlinsupportcheckboxwhilecreatinganewproject:

Page 51: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Inbothscenarios,theConfigureKotlininprojectcommandupdatestherootbuild.gradlefileandthebuild.gradlefilescorrespondingtothemodule(s)byaddingKotlindependencies.ItalsoaddstheKotlinplugintotheAndroidmodule.DuringthetimeofwritingthisbookreleaseversionofAndroidStudio3isnotyetavailable,butwecanreviewbuildscriptfrompre-releaseversion:

//build.gradlefileinprojectrootfolder

buildscript{

ext.kotlin_version='1.1'

repositories{

google()

jcenter()

}

dependencies{

classpath'com.android.tools.build:gradle:3.0.0-alpha9'

classpath"org.jetbrains.kotlin:kotlin-gradle-

plugin:$kotlin_version"

}

}

...

//build.gradlefileintheselectedmodules

applyplugin:'com.android.application'

applyplugin:'kotlin-android'

applyplugin:'kotlin-android-extensions'

...

dependencies{

...

implementation'com.android.support.constraint:constraint-

layout:1.0.2'

}

...

PriortoAndroidPluginforGradle3.x(deliveredwithAndroid

Page 52: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Studio3.0)compiledependencyconfigurationwasusedinsteadofimplementation.

ToupdatetheKotlinversion(letussayinthefuture),weneedtochangethevalueofthekotlin_versionvariableinthebuild.gradlefile(projectrootfolder).ChangesinGradlefilesmeanthattheprojectmustbesynchronized,soGradlecanupdateitsconfigurationanddownloadalltherequireddependencies:

Page 53: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

UsingKotlininanewAndroidprojectForthenewKotlinprojectscreatedinAndroidStudio3.x,themainactivitywillbealreadydefinedinKotlin,sothatwecanstartwritingKotlincoderightaway:

AddinganewKotlinfileissimilartoaddingaJavafile.Simplyright-clickonapackageandselectnew|KotlinFile/Class:

ThereasonwhytheIDEsaysKotlinFile/ClassandnotsimplyKotlinclass,analogouslytoJavaclassisthatwecanhavemoremembersdefinedinsideasinglefile.WewilldiscussthisinmoredetailinChapter2,LayingaFoundation.

NoticethatKotlinsourcefilescanbelocatedinsidethejavasourcefolder.WecancreateanewsourcefolderforKotlin,butitisnotrequired:

Page 54: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

RunninganddebuggingaprojectisexactlythesameasinJavaanddoesnotrequireanyadditionalstepsbesidesconfiguringKotlinintheproject:

StartingfromAndroidStudio3.0,variousAndroidtemplateswillalsoallowustoselectalanguage.ThisisthenewConfigureActivitywizard:

Page 55: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

JavatoKotlinconverter(J2K)MigrationofexistingJavaprojectsisalsoquiteeasy,becausewecanuseJavaandKotlinsidebysideinthesameproject.TherearealsowaystoconvertexistingJavacodeintoKotlincodebyusingtheJavatoKotlinconverter(J2K).

ThefirstwayistoconvertwholeJavafilesintoKotlinfilesusingtheconvertJavaFiletoKotlincommand(keyboardshortcutinWindowsisAlt+Shift+Ctrl+KandinmacOS:option+shift+command+K),andthisworksverywell.ThesecondwayistopasteJavacodeintoanexistingKotlinfileandthecodewillalsobeconverted(adialogwindowwillappearwithaconversionproposition).ThismaybeveryhelpfulwhenlearningKotlin.

Ifwedon'tknowhowtowriteaparticularpieceofcodeinKotlin,wecanwriteitinJava,thensimplycopytotheclipboardandthenpasteitintotheKotlinfile.ConvertedcodewillnotbethemostidiomaticversionofKotlin,butitwillwork.TheIDEwilldisplayvariousintentionstoconvertthecodeevenmoreandimproveitsquality.Beforeconversion,weneedtomakesurethatJavacodeisvalid,becauseconversiontoolsareverysensitiveandtheprocesswillfailevenifasinglesemicolonismissing.TheJ2KconvertercombinedwithJavainteroperabilityallowsKotlinbeintroducedgraduallyintotheexistingproject(forexample,toconvertasingleclassatatime).

Page 56: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

AlternativewaystorunKotlincodeAndroidStudiooffersanalternativewayofrunningKotlincodewithouttheneedtorunAndroidapplication.ThisisusefulwhenyouwanttoquicklytestsomeKotlincodeseparatelyfromthelongAndroidcompilationanddeploymentprocess.

ThewaytorunKotlincodeistousebuildKotlinReadEvalPrintLoop(REPL).REPLisasimplelanguageshellthatreadssingleuserinput,evaluatesit,andprintstheresult:

REPLlookslikethecommand-line,butitwillprovideuswithalltherequiredcodehintsandwillgiveusaccesstovariousstructuresdefinedinsidetheproject(classes,interfaces,top-levelfunctions,andsoon):

Page 57: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ThebiggestadvantageofREPLisitsspeed.WecantestKotlincodereallyquickly.

Page 58: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

KotlinunderthehoodWewillfocusmainlyonAndroid,butkeepinmindthatKotlincanbecompiledtomultipleplatforms.KotlincodecanbecompiledtoJavabytecodeandthentoDalvikbytecode.HereissimplifiedversionoftheKotlinbuildprocessfortheAndroidplatform:

Afilewitha.javaextensioncontainsJavacodeAfilewitha.ktextensioncontainsKotlincodeAfilewitha.classextensioncontainsJavabytecodeAfilewitha.dexextensioncontainsDalvikbytecodeAfilewitha.apkextensioncontainstheAndroidManifestfile,resources,and.dexfile

ForpureKotlinprojects,onlytheKotlincompilerwillbeused,butKotlinalsosupportscross-languageprojects,wherewecanuseKotlintogetherwithJavainthesameAndroidproject.Insuchcases,bothcompilersareusedtocompiletheAndroidapplicationandtheresultwillbemergedattheclasslevel.

Page 59: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

TheKotlinstandardlibraryKotlinstandardlibrary(stdlib)isaverysmalllibrarythatisdistributedtogetherwithKotlin.ItisrequiredtorunapplicationswritteninKotlinanditisaddedautomaticallytoourapplicationduringthebuildprocess.

InKotlin1.1,kotlin-runtimewasrequiredtorunapplicationswritteninKotlin.Infact,inKotlin1.1thereweretwoartifacts(kotlin-runtimeandkotlin-stdlib)thatsharedalotofKotlinpackages.Toreducetheamountofconfusionboththeartifactswillbemergedintosingleartifact(kotlin-stdlib)inintheupcoming1.2versionofKotlin.StartingfromKotlin1.2,kotlin-stdlibisrequiredtorunapplicationswritteninKotlin.

TheKotlinstandardlibraryprovidesessentialelementsrequiredforeverydayworkwithKotlin.Theseinclude:

Datatypeslikearrays,collections,lists,ranges,andsoonExtensionsHigher-orderfunctionsVariousutilitiesforworkingwithstringsandcharsequencesExtensionsforJDKclassesmakingitconvenienttoworkwithfiles,IO,andthreading

Page 60: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

MorereasonstouseKotlinKotlinhasstrongcommercialsupportfromJetBrains,acompanythatdeliversverypopularIDEsformanypopularprogramminglanguages(AndroidStudioisbasedonJetBrainsIntelliJIDEA).JetBrainswantedtoimprovethequalityoftheircodeandteamperformance,sotheyneededthelanguagethatwillsolvealltheJavaissuesandprovideseamlessinteroperabilitywithJava.NoneoftheotherJVMlanguagesmeetthoserequirements,soJetBrainsfinallydecidedtocreatetheirownlanguageandstartedtheKotlinproject.Nowadays,Kotlinisusedintheirflagshipproducts.SomeuseKotlintogetherwithJavawhileothersarepureKotlinproducts.

Kotlinisquiteamaturelanguage.Infact,itsdevelopmentstartedmanyyearsbeforeGoogleannouncedofficialAndroidsupport(firstcommitdatesbackto2010-11-08):

TheinitialnameofthelanguagewasJet.Atsomepoint,theJetBrainsteamdecidedtorenameittoKotlin.ThenamecomesfromKotlinIsland,nearSt.PetersburganditsanalogytoJavawhichwasalsonamedaftertheIndonesianisland.

Aftertheversion1.0releasein2016,moreandmorecompaniesstartedtosupporttheKotlinproject.GradleaddedsupportofKotlinintobuildingscripts,Square,thebiggestcreatorofAndroidlibrariespostedthattheystronglysupportKotlinandfinally,Googleannouncedit'sofficialKotlinsupportfortheAndroidplatform.ThismeansthateverytoolthatwillbereleasedbytheAndroidteamwillbecompatiblenotonlywithJavabutalsowithKotlin.GoogleandJetBrainshavebegunapartnershiptocreateanonprofitfoundationforKotlin,responsibleforfuturelanguagemaintenanceanddevelopment.Allofthiswillgreatly

Page 61: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

increasethenumberofcompaniesthatwilluseKotlinintheirprojects.

KotlinisalsosimilartoApple'sSwiftprogramminglanguage.Infact,suchistheresemblance,thatsomearticlesfocusondifferences,notsimilarities.LearningKotlinwillbeveryhelpfulfordeveloperseagertodevelopapplicationsforAndroidandiOS.TherearealsoplanstoportKotlintoiOS(Kotlin/Native),somaybewedon'thavetolearnSwiftafterall.FullstackdevelopmentisalsopossibleinKotlin,sowecandevelopserver-sideapplicationsandfrontendclientssharingthesamedatamodelwithmobileclients.

Page 62: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

SummaryWe'vediscussedhowtheKotlinlanguagefitsintoAndroiddevelopmentandhowwecanincorporateKotlinintonewandexistingprojects.WehaveseenusefulexampleswhereKotlinsimplifiedthecodeandmadeitmuchsafer.Therearestillmanyinterestingthingstodiscover.

Inthenextchapter,wewilllearnaboutKotlinbuildingblocksandlayafoundationtodevelopAndroidapplicationsusingKotlin.

Page 63: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

LayingaFoundationThischapterislargelydevotedtothefundamentalbuildingblocksthatarecoreelementsoftheKotlinprogramminglanguage.Eachonemayseeminsignificantbyitself,butcombinedtogether,theycreatereallypowerfullanguageconstructs.WewilldiscusstheKotlintypesystemthatintroducesstrictnullsafetyandsmartcasts.AlsowewillseeafewnewoperatorsintheJVMworld,andmanyimprovementscomparedtoJava.Wewillalsopresentnewwaystohandleapplicationflowsanddealwithequalityinaunifiedway.

Inthischapter,wewillcoverthefollowingtopics:

Variables,values,andconstantsTypeinferenceStrictnullsafetySmartcastsKotlindatatypesControlstructuresExceptionshandling

Page 64: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

VariablesInKotlin,wehavetwotypesofvariables:varorval.Thefirstone,var,isamutablereference(read-write)thatcanbeupdatedafterinitialization.ThevarkeywordisusedtodefineavariableinKotlin.Itisequivalenttoanormal(non-final)Javavariable.Ifourvariableneedstochangeatsometime,weshoulddeclareitusingthevarkeyword.Let'slookatanexampleofavariabledeclaration:

funmain(args:Array<String>){

varfruit:String="orange"//1

fruit="banana"//2

}

1. Createfruitvariableandinitializeitwithvariableorangevalue2. Reinitializefruitvariablewithwithbananavalue

Thesecondtypeofvariableisaread-onlyreference.Thistypeofvariablecannotbereassignedafterinitialization.

Thevalkeywordcancontainacustomgetter,sotechnicallyitcanreturndifferentobjectsoneachaccess.Inotherwords,wecan'tguaranteethatthereferencetotheunderlyingobjectisimmutable:

valrandom:Int

get()=Random().nextInt()

CustomgetterswillbediscussedinmoredetailinChapter4,ClassesandObjects.

ThevalkeywordisequivalentofaJavavariablewiththefinalmodifier.Usingimmutablevariablesisuseful,becauseitmakessurethatthevariablewillneverbeupdatedbymistake.Theconceptofimmutabilityisalsohelpfulforworkingwithmultiplethreadswithoutworryingaboutproperdatasynchronization.Todeclareimmutablevariables,wewillusethevalkeyword:

funmain(args:Array<String>){

Page 65: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

valfruit:String="orange"//1

a="banana"//2Error

}

1. Createfruitvariableandinitializeitwithstringorangevalue2. Compilerwillthrowanerror,becausefruitvariablewasalreadyinitialized

Kotlinalsoallowsustodefinevariablesandfunctionsatthelevelofthefile.WewilldiscussitfurtherinChapter3,PlayingwithFunctions.

Noticethatthetypeofthevariablereference(var,val)relatestothereferenceitself,notthepropertiesofthereferencedobject.Thismeansthatwhenusingaread-onlyreference(val),wewillnotbeabletochangethereferencethatispointingtoaparticularobjectinstance(wewillnotbeabletoreassignvariablevalues),butwewillstillbeabletomodifypropertiesofreferencedobjects.Let'sseeitinactionusinganarray:

vallist=mutableListOf("a","b","c")//1

list=mutableListOf("d","e")//2Error

list.remove("a")//3

1. Initializemutablelist2. Compilerwillthrowanerror,becausevaluereferencecannotbechanged

(reassigned)3. Compilerwillallowtomodifycontentofthelist

Thekeywordvalcannotguaranteethattheunderlyingobjectisimmutable.

Ifwereallywanttomakesurethattheobjectwillnotbemodified,wemustuseimmutablereferenceandanimmutableobject.Fortunately,Kotlin'sstandardlibrarycontainsanimmutableequivalentofanycollectioninterface(ListversusMutableList,MapversusMutableMap,andsoon)andthesameistrueforhelperfunctionsthatareusedtocreateinstanceofparticularcollection:

Variable/valuedefinition Referencecanchange Objectstatecanchange

val=listOf(1,2,3)

Page 66: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

No No

val=mutableListOf(1,2,3) No Yes

var=listOf(1,2,3) Yes No

var=mutableListOf(1,2,3) Yes Yes

Page 67: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

TypeinferenceAswesawinpreviousexamples,unlikeJava,theKotlintypeisdefinedaftervariablename:

vartitle:String

Atfirstglance,thismaylookstrangetoJavadevelopers,butthisconstructisabuildingblockofaveryimportantfeatureofKotlincalledtypeinference.Typeinferencemeansthatthecompilercaninfertypefromcontext(thevalueofanexpressionassignedtoavariable).Whenvariabledeclarationandinitializationisperformedtogether(singleline),wecanomitthetypedeclaration.Let'slookatthefollowingvariabledefinition:

vartitle:String="Kotlin"

ThetypeofthetitlevariableisString,butdowereallyneedanimplicittypedeclarationtodeterminevariabletype?Ontherightsideoftheexpression,wehaveastringKotlinandweareassigningittoavariabletitledefinedontheleft-handsideoftheexpression.

WespecifiedavariabletypeasString,butitwasobvious,becausethisisthesametypeasthetypeofassignedexpression(Kotlin).Fortunately,thisfactisalsoobviousfortheKotlincompiler,sowecanomittypewhendeclaringavariable,becausethecompilerwilltrytodeterminethebesttypeforthevariablefromthecurrentcontext:

vartitle="Kotlin"

Keepinmind,thattypedeclarationisomitted,butthetypeofvariableisstillimplicitlysettoString,becauseKotlinisastronglytypedlanguage.That'swhybothoftheprecedingdeclarationsarethesame,andKotlincompilerwillstillbeabletoproperlyvalidateallfutureusagesofvariable.Hereisanexample:

vartitle="Kotlin"

title=12//1,Error

1. InferredtypewasStringandwearetryingtoassignInt

Page 68: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

IfwewanttoassigntheInt(value12)tothetitlevariablethenweneedtospecifytitletypetoonethatisaStringandIntcommontype.Theclosestone,upinthetypehierarchyisAny:

vartitle:Any="Kotlin"

title=12

AnyisanequivalentoftheJavaobjecttype.ItistherootoftheKotlintypehierarchy.AllclassesinKotlinexplicitlyinheritfromtypeAny,evenprimitivetypessuchasStringorInt

Anydefinesthreemethods:equals,toString,andhashCode.Kotlinstandardlibrarycontainsafewextensionsforthistype.WewilldiscussextensionsinChapter7,ExtensionFunctionsandProperties.

Aswecansee,typeinferenceisnotlimitedtoprimitivevalues.Let'slookatinferringtypesdirectlyfromfunctions:

vartotal=sum(10,20)

Intheprecedingexample,theinferredtypewillbethesameastypereturnedbythefunction.WemayguessthatitwillbeInt,butitmayalsobeaDouble,Float,orsomeothertype.Ifit'snotobviousfromthecontextwhattypewillbeinferredwecanuseplacecarrotonthevariablenameandruntheAndroidStudioexpressiontypecommand(forWindows,itisShift+Ctrl+P,andformacOS,itisarrowkey+control+P).Thiswilldisplaythevariabletypeinthetooltip,asfollows:

Typeinferenceworksalsoforgenerictypes:

varpersons=listOf(personInstance1,personInstance2)

//Inferredtype:List<Person>()

Page 69: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

AssumingthatwepassonlyinstancesofthePersonclass,theinferredtypewillbeList<Person>.ThelistOfmethodisahelperfunctiondefinedintheKotlinstandardlibrarythatallowustocreatecollection.WewilldiscussthissubjectinChapter7,ExtensionFunctionsandProperties.Let'slookatmoreadvancedexamplesthatusestheKotlinstandardlibrarytypecalledPair,whichcontainsapaircomposedoftwovalues:

varpair="Everest"to8848//Inferredtype:Pair<String,Int>

Intheprecedingexample,apairinstanceiscreatedusingtheinfixfunction,whichwillbediscussedinChapter4,ClassesandObjects,butfornowallweneedtoknowisthatthosetwodeclarationsreturnthesametypeofPairobject:

varpair="Everest"to8848

//Createpairusingtoinfixmethod

varpair2=Pair("Everest",8848)

//CreatePairusingconstructor

Typeinferenceworksalsoformorecomplexscenariossuchasinferringtypefrominferredtype.Let'susetheKotlinstandardlibrary'smapOffunctionandinfixthetomethodofthePairclasstodefinemap.Thefirstiteminthepairwillbeusedtoinferthemapkeytype;thesecondwillbeusedtoinferthevaluetype:

varmap=mapOf("MountEverest"to8848,"K2"to4017)

//Inferredtype:Map<String,Int>

GenerictypeofMap<String,Int>isinferredfromtypeofPair<String,Int>,whichisinferredfromtypeofparameterspassedtoPairconstructor.Wemaywonderwhathappensifinferredtypeofpairsusedtocreatemapdiffers?ThefirstpairisPair<String,Int>andsecondisPair<String,String>:

varmap=mapOf("MountEverest"to8848,"K2"to"4017")

//Inferredtype:Map<String,Any>

Intheprecedingscenario,Kotlincompilerwilltrytoinfercommontypeforallpairs.FirstparameterinbothpairsisString(MountEverest,K2),sonaturallyStringwillbeinferredhere.Secondparameterofeachpairdiffers(Intforfirstpair,Stringforsecondpair),soKotlinneedstofindtheclosestcommontype.TheAnytypeischosen,becausethisistheclosestcommontypeinupstreamtypehierarchy:

Page 70: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Aswecansee,typeinferencedoesagreatjobinmostcases,butwecanstillchoosetoexplicitlydefineadatatypeifwewant,forexample,wewantdifferentvariabletypes:

varage:Int=18

Whendealingwithintegers,theInttypeisalwaysadefaultchoice,butwecanstillexplicitlydefinedifferenttypes,forexample,Short,tosavesomepreciousAndroidmemory:

varage:Short=18

Ontheotherhand,ifweneedtostorelargervalues,wecandefinethetypeoftheagevariableasLong.Wecanuseexplicittypedeclarationaspreviously,oruseliteralconstant:

varage:Long=18//Explicitlydefinevariabletype

varage=18L

//Useliteralconstanttospecifyvaluetype

Thosetwodeclarationsareequal,andallofthemwillcreatevariableoftypeLong.

Fornow,weknowthattherearemorecasesincodewheretypedeclarationcanbeomittedtomakecodesyntaxmoreconcise.TherearehoweversomesituationswheretheKotlincompilerwillnotbeabletoinfertypeduetolackofinformationincontext.Forexample,simpledeclarationwithoutassignmentwillmaketypeinferenceimpossible:

valtitle//Error

Intheprecedingexample,thevariablewillbeinitializedlater,sothereisnowaytodetermineitstype.That'swhytypemustbeexplicitlyspecified.Thegeneralruleisthatiftypeofexpressionisknownforthecompiler,thentypecanbeinferred.Otherwise,itmustbeexplicitlyspecified.KotlinplugininAndroidStudiodoesagreatjobbecauseitknowsexactlywheretypecannotbeinferredandthenitishighlightingerror.Thisallowsustodisplaypropererrormessages

Page 71: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

instantlybyIDEwhenwritingthecodewithouttheneedtocompleteapplication.

Page 72: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

StrictnullsafetyAccordingtoAgileSoftwareAssessment(http://p3.snf.ch/Project-144126)research,missingnullcheckisthemostfrequentpatternofbugsinJavasystems.ThebiggestsourceoferrorsinJavaisNullPointerExceptions.It'ssobig,thatspeakingataconferencein2009,SirTonyHoareapologizedforinventingthenullreference,callingitabillion-dollarmistake(https://en.wikipedia.org/wiki/Tony_Hoare).

ToavoidNullPointerException,weneedtowritedefensivecodethatchecksifanobjectisnullbeforeusingit.Manymodernprogramminglanguages,includingKotlin,madestepstoconvertruntimeerrorsintocompiletimeerrorstoimproveprogramminglanguagesafeness.OneofthewaytodoitinKotlinisbyaddingnullabilitysafenessmechanismstolanguagetypesystems.ThisispossiblebecauseKotlintypesystemdistinguishesbetweenreferencesthatcanholdnull(nullablereferences)andthosethatcannot(non-nullablereferences).ThissinglefeatureofKotlinallowsustodetectmanyerrorsrelatedtoNullPointerExceptionatveryearlystagesofdevelopment.CompilertogetherwithIDEwillpreventmanyNullPointerException.Inmanycasescompilationwillfailinsteadofapplicationfailingatruntime.

StrictnullsafetyispartofKotlintypesystem.Bydefault,regulartypescannotbenull(can'tstorenullreferences),unlesstheyareexplicitlyallowed.Tostorenullreferences,wemustmarkvariableasnullable(allowittostorenullreferences)byaddingquestionmarksuffixtovariabletypedeclaration.Hereisanexample:

valage:Int=null//1,Error

valname:String?=null//2

1. Compilerwillthrowerror,becausethistypedoesnotallownull.2. Compilerwillallownullassignment,becausetypeismarkedasnullable

usingquestionmarksuffix.

Wearenotallowedtocallmethodonapotentiallynullableobject,unlessanullitycheckisperformedbeforeacall:

valname:String?=null

Page 73: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

//...

name.toUpperCase()//error,thisreferencemaybenull

Wewilllearnhowtodealwiththeprobleminthenextsection.Everynon-nullabletypeinKotlinhasitsnullabletypeequivalent:InthasInt?,StringhasString?andsoon.ThesameruleappliesforallclassesintheAndroidframework(ViewhasView?),third-partylibraries(OkHttpClienthasOkHttpClient?),andallcustomclassesdefinedbydevelopers(MyCustomClasshasMyCustomClass?).Thismeansthateverynongenericclasscanbeusedtodefinetwokindsoftypes,nullableandnon-nullable.Anon-nullabletypeisalsoasubtypeofitsnullableequivalent.Forexample,Vehicle,aswellasbeingasubtypeofVehicle?,isalsoasubtypeofAny:

TheNothingtypeisanemptytype(uninhabitedtype),whichcan'thaveaninstance.WewilldiscussitinmoredetailsinChapter3,PlayingwithFunctions.Thistypehierarchyisthereasonwhywecanassignnon-nullobject(Vehicle)intoavariabletypedasnullable(Vehicle?),butwecannotassignanullableobject(Vehicle?)intoanon-nullvariable(Vehicle):

varnullableVehicle:Vehicle?

varvehicle:Vehicle

nullableVehicle=vehicle//1

vehicle=nullableVehicle//2,Error

1. Assignmentpossible2. ErrorbecausenullableVehiclemaybeanull

Page 74: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Wewilldiscusswaysofdealingwithnullabletypesinfollowingsections.Nowlet'sgetbacktotypedefinitions.Whendefininggenerictypes,therearemultiplepossibilitiesofdefiningnullability,solet'sexaminevariouscollectiontypesbycomparingdifferentdeclarationsforgenericArrayListcontainingitemsoftypeInt.Hereisatablethatispresentsthekeydifferences:

Typedeclaration Listitselfcanbenull Elementcanbenull

ArrayList<Int> No No

ArrayList<Int>? Yes No

ArrayList<Int?> No Yes

ArrayList<Int?>? Yes Yes

It'simportanttounderstanddifferentwaystospecifynulltypedeclarations,becauseKotlincompilerenforcesittoavoidNullPointerExceptions.Thismeansthatcompilerenforcesnullitycheckbeforeaccessinganyreferencethatpotentiallycanbenull.Nowlet'sexaminecommonAndroid/JavaerrorintheActivityclass'onCreatemethod:

//Java

@Override

publicvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

savedInstanceState.getBoolean("locked");

}

InJava,thiscodewillcompilefineandaccessingnullobjectswillresultinapplicationcrashatruntimethrowingNullPointerException.Nowlet'sexaminetheKotlinversionofthesamemethod:

overridefunonCreate(savedInstanceState:Bundle?){//1

Page 75: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

super.onCreate(savedInstanceState)

savedInstanceState.getBoolean("key")//2Error

}

1. savedInstanceStatedefinedasnullableBundle?2. Compilerwillthrowerror

ThesavedInstanceStatetypeisaplatformtypethatcanbeinterpretedbyKotlinasnullableornon-nullable.Wewilldiscussplatformtypesinthefollowingsections,butfornowwewilldefinesavedInstanceStateasnullabletype.Wearedoingso,becauseweknowthatnullwillbepassedwhenActivityiscreatedforthefirsttime.InstanceofBundlewillonlybepassedwhenanActivityisrecreatedusingsavedinstancestate:

WewilldiscussfunctionsinChapter3,PlayingwithFunctions,butfornow,wecanalreadyseethatthesyntaxfordeclaringfunctionsinKotlinisquitesimilartoJava.

ThemostobviouswaytofixtheprecedingerrorinKotlinistocheckfornullityexactlythesamewayasinJava:

overridefunonCreate(savedInstanceState:Bundle?){

super.onCreate(savedInstanceState)

}

overridefunonCreate(savedInstanceState:Bundle?){

super.onCreate(savedInstanceState)

vallocked:Boolean

if(savedInstanceState!=null)

locked=savedInstanceState.getBoolean("locked")

else

locked=false

}

Theprecedingconstructpresentssomeboilerplatecode,becausenull-checkingisaprettycommonoperationinJavadevelopment(especiallyintheAndroidframework,wheremostelementsarenullable).Fortunately,Kotlinallowsafewsimplersolutionstodealwithnullablevariables.Thefirstoneisthesafecalloperator.

Page 76: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

SafecallThesafecalloperatorissimplyaquestionmarkfollowedbyadot.It'simportanttounderstandthatsafecastoperatorwillalwaysreturnavalue.Iftheleft-handsideoftheoperatorisnull,thenitwillreturnnull,otherwiseitwillreturntheresultoftheright-handsideexpression:

overridefunonCreate(savedInstanceState:Bundle?){

super.onCreate(savedInstanceState)

vallocked:Boolean?=savedInstanceState?.getBoolean("locked")

}

IfsavedInstanceStateisnull,thennullwillbereturned,otherwisetheresultofevaluatingasavedInstanceState?.getBoolean("locked")expressionwillbereturned.Keepinmind,thatanullablereferencecallmayalwaysreturnsnullable,sotheresultofthewholeexpressionisnullableBoolean?.Ifwewanttomakesurewewillgetnon-nullableBoolean,wecancombinethesafecalloperatorcombinedwiththeelvisoperator,discussedinthenextsection.

Multiplecallsofthesavecalloperatorcanbechainedtogethertoavoidanestedifexpressionorcomplexconditionslikethis:

//Javaidiomatic-multiplechecks

valquiz:Quiz=Quiz()

//...

valcorrect:Boolean?

if(quiz.currentQuestion!=null){

if(quiz.currentQuestion.answer!=null){

//dosomething

}

}

//Kotlinidiomatic-multiplecallsofsavecalloperator

valquiz:Quiz=Quiz()

//...

valcorrect=quiz.currentQuestion?.answer?.correct

//InferredtypeBoolean?

Theprecedingchainworkslikethis--correctwillbeaccessedonlyiftheanswervalueisnotnullandanswerisaccessedonlyifthecurrentQuestionvalueisnotnull.Asaresult,theexpressionwillreturnthevaluereturnedbycorrectpropertyornullifanyobjectinthesafecallchainisnull.

Page 77: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ElvisoperatorTheelvisoperatorisrepresentedbyaquestionmarkfollowedbyacolon(?:)andhassuchsyntax:

firstoperand?:secondoperand

Theelvisoperatorworksasfollows:iffirstoperandisnotnull,thenthisoperandwillbereturned,otherwisesecondoperandwillbereturned.Theelvisoperatorallowsustowriteveryconcisecode.

Wecanapplytheelvisoperatortoourexampletoretrievethevariablelocked,whichwillbealwaysnon-nullable:

overridefunonCreate(savedInstanceState:Bundle?){

super.onCreate(savedInstanceState)

vallocked:Boolean=savedInstanceState?.

getBoolean("locked")?:false

}

Intheprecedingexample,theelvisoperatorwillreturnvalueofsavedInstanceState?.getBoolean("locked")expressionifsavedInstanceStateisnotnull,otherwiseitwillreturnfalse.Thiswaywecanmakesurethatthelockedvariable.Thankstoelvisoperatorwecandefinedefaultvalue.Alsonotethattheright-handsideexpressionisevaluatedonlyiftheleft-handsideisnull.Itisthenprovidingdefaultvaluethatwillbeusedwhentheexpressionisnullable.Gettingbacktoourquizexamplefromtheprevioussection,wecaneasilymodifythecodetoalwaysreturnanon-nullablevalue:

valcorrect=quiz.currentQuestion?.answer?.correct?:false

Astheresult,theexpressionwillreturnthevaluereturnedbythecorrectpropertyorfalseifanyobjectinthesafecallchainisnull.Thismeansthatthevaluewillalwaysbereturned,sonon-nullableBooleantypeisinferred.

TheoperatornamecomesfromthefamousAmericansinger-songwriterElvisPresley,becausehishairstyleissimilartoaquestionmark.

Page 78: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava
Page 79: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

NotnullassertionAnothertooltodealwithnullityisthenot-nullassertionoperator.Itisrepresentedbyadoubleexclamationmark(!!).Thisoperatorexplicitlycastsnullablevariablestonon-nullablevariables.Hereisausageexample:

vary:String?="foo"

varsize:Int=y!!.length

Normally,wewouldnotbeabletoassignavaluefromanullablepropertylengthtoanon-nullablevariablesize.However,asadeveloper,wecanassurethecompilerthatthisnullablevariablewillhaveavaluehere.Ifweareright,ourapplicationwillworkcorrectly,butifwearewrong,andthevariablehasanullvalue,theapplicationwillthrowNullPointerException.Let'sexamineouractivitymethodonCreate():

overridefunonCreate(savedInstanceState:Bundle?){

super.onCreate(savedInstanceState)

vallocked:Boolean=savedInstanceState!!.getBoolean("locked")

}

Theprecedingcodewillcompile,butwillthiscodeworkcorrectly?Aswesaidbefore,whenrestoringanactivityinstance,savedInstanceStatewillbepassedtotheonCreatemethod,sothiscodewillworkwithoutexceptions.However,whencreatinganactivityinstance,thesavedInstanceStatewillbenull(thereisnopreviousinstancetorestore),soNullPointerExceptionwillbethrownatruntime.ThisbehaviorissimilartoJava,butthemaindifferenceisthatinJavaaccessingpotentiallynullableobjectswithoutanullitycheckisthedefaultbehavior,whileinKotlinwehavetoforceit;otherwise,wewillgetacompilationerror.

Thereareonlyfewcorrectpossibleapplicationsforusageofthisoperator,sowhenyouuseitorseeitincode,thinkaboutitaspotentialdangerorwarning.Itissuggestedthatnot-nullassertionshouldbeusedrarely,andinmostcasesshouldbereplacedwithsafecallorsmartcast.

Combatingnon-nullassertionsarticlepresentsfewusefulexampleswherenon-nullassertionoperatorisreplacedwithother,safeKotlinconstructsathttp://bit.ly/2xg5JXt.

Page 80: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Actuallyinthiscasethereisnopointofusingnot-nullassertionoperatorbecausewecansolveourprobleminsaferwayusinglet.

Page 81: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

LetAnothertooltodealwithnullablevariablesislet.Thisisactuallynottheoperator,northelanguagespecialconstruct.ItisafunctiondefinedintheKotlinstandardlibrary.Let'sseethesyntaxofletcombinedwiththesafecalloperator:

overridefunonCreate(savedInstanceState:Bundle?){

super.onCreate(savedInstanceState)

savedInstanceState?.let{

println(it.getBoolean("isLocked"))//1

}

}

1. savedInstanceStateinsideletcanbeaccessedusingvariablenamedit.

Asmentionedbefore,theright-handsideexpressionofthesafecalloperatorwillbeonlybeevaluatediftheleft-handsideisnotnull.Inthiscase,theright-handsideisaletfunctionthattakesanotherfunction(lambda)asaparameter.CodedefinedintheblockafterletwillbeexecutedifsavedInstanceStateisnotnull.WewilllearnmoreaboutitandhowtodefinesuchfunctionslaterinChapter7,ExtensionFunctionsandProperties.

Page 82: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

NullabilityandJavaWeknowthatKotlinrequirestoexplicitlydefinereferencesthatcanholdnullvalues.Javaontheotherhand,ismuchmorelenientaboutnullability,sowemaywonderhowKotlinhandlestypescomingfromJava(basicallythewholeAndroidSDKandlibrarieswritteninJava).Wheneverpossible,Kotlincompilerwilldeterminetypenullabilityfromthecodeandrepresenttypesasactualnullableornon-nullabletypesusingnullabilityannotations.

TheKotlincompilersupportsseveralflavorsofnullabilityannotations,including:

Android(com.android.annotationsandandroid.support.annotations)JetBrains(@[email protected])JSR-305(Javax.annotation)

WecanfindthefulllistintheKotlincompilersourcecode(https://github.com/JetBrains/kotlin/blob/master/core/descriptor.loader.Java/src/org/jetbrains/kotlin/load/Java/JvmAnnotationNames.kt)

WehaveseenthispreviouslyinActivity'sonCreatemethod,wherethesavedInstanceStatetypewasexplicitlysettothenullabletypeBundle?:

overridefunonCreate(savedInstanceState:Bundle?){

...

}

Thereare,however,manysituationswhereitisnotpossibletodeterminevariablenullability.AllvariablescomingfromJavacanbenullexceptonesannotatedasnon-nullable..Wecouldtreatallofthemasnullableandcheckbeforeeachaccess,butthiswouldbeimpractical.Asasolutionforthisproblem,Kotlinintroducedtheconceptofplatformtypes.ThosearetypescomingfromJavatypeswithrelaxednullchecks,meaningthateachplatformtypemaybenullornot.

Althoughwecannotdeclareplatformtypesbyourselves,thisspecialsyntax

Page 83: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

existsbecausethecompilerandAndroidStudioneedtodisplaythemsometimes.Wecanspotplatformtypesinexceptionmessagesorthemethodparameterslist.Platformtypesyntaxisjustasingleexclamationmarksuffixinavariabletypedeclaration:

View!//Viewdefinedasplatformtype

Wecouldtreateachplatformtypeasnullable,buttypenullabilityusuallydependsoncontext,sosometimeswecantreatthemasnon-nullablevariables.Thispseudocodeshowsthepossiblemeaningofplatformtype:

T!=TorT?

It'sourresponsibilityasdeveloperstodecidehowtotreatsuchtype,asnullableornon-nullable.Let'sconsidertheusageofthefindViewByIdmethod:

valtextView=findViewById(R.id.textView)

WhatwillthefindViewByIdmethodactuallyreturn?WhatistheinferredtypeofthetextViewvariable?Nullabletype(TestView)ornotnullable(TextView?)?Bydefault,theKotlincompilerknowsnothingaboutthenullabilityofthevaluereturnedbythefindViewByIdmethod.ThisiswhyinferredtypeforTextViewhasplatformtypeView!.

Thisisthekindofdeveloperresponsibilitythatwearetalkingabout.We,asdevelopers,mustdecide,becauseonlyweknowifthelayoutwillhavetextViewdefinedinallconfigurations(portrait,landscape,andsoon)oronlyinsomeofthem.IfwedefineproperviewinsidecurrentlayoutfindViewByIdmethodwillreturnreferencetothisview,andotherwiseitwillreturnnull:

valtextView=findViewById(R.id.textView)asTextView//1

valtextView=findViewById(R.id.textView)asTextView?//2

1. AssumingthattextViewispresentineverylayoutforeachconfiguration,sotextViewcanbedefinedasnon-nullable

2. AssumingthattextViewisnotpresentinalllayoutconfigurations(forexample,presentonlyinlandscape),textViewmustbedefinedasnullable,otherwisetheapplicationwillthrowaNullPointerExceptionwhentryingtoassignnulltoanon-nullablevariable(whenlayoutwithouttextViewisloaded)

Page 84: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

CastsThecastingconceptissupportedbymanyprogramminglanguages.Basically,castingisawaytoconvertanobjectofoneparticulartypeintoanothertype.InJava,weneedtocastanobjectexplicitlybeforeaccessingitsmembersorcastitandstoreitinthevariableofthecastedtype.Kotlinsimplifiesconceptofcastingandmovesittothenextlevelbyintroducingsmartcasts.

InKotlin,wecanperformafewtypesofcasts:

Castobjectstodifferenttypesexplicitly(safecastoperator)Castobjectstodifferenttypesornullabletypestonon-nullabletypesimplicitly(smartcastmechanism)

Page 85: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Safe/unsafecastoperatorInstronglytypedlanguages,suchasJavaorKotlin,weneedtoconvertvaluesfromonetypetoanotherexplicitlyusingthecastoperator.Atypicalcastingoperationistakinganobjectofoneparticulartypeandturningitintoanotherobjecttypethatisitssupertype(upcasting),subtype(downcasting),orinterface.Let'sstartwithasmallremainderofcastingthatcouldbeperformedinJava:

Fragmentfragment=newProductFragment();

ProductFragmentproductFragment=(ProductFragment)fragment;

Intheprecedingexample,thereisaninstanceofProductFragmentthatisassignedtoavariablestoringFragmentdatatype.TobeabletostorethisdataintotheproductFragmentvariablethatcanstoreonlytheProductFragmentdatatype,soweneedtoperformanexplicitcast.UnlikeJava,Kotlinhasaspecialaskeywordrepresentingtheunsafecastoperatortohandlecasting:

valfragment:Fragment=ProductFragment()

valproductFragment:ProductFragment=fragmentasProductFragment

TheProductFragmentvariableisasubtypeofFragment,sotheprecedingexamplewillworkfine.TheproblemisthatcastingtoanincompatibletypewillthrowtheexceptionClassCastException.That'swhytheasoperatoriscalledanunsafecastoperator:

valfragment:String="ProductFragment"

valproductFragment:ProductFragment=fragmentas

ProductFragment

\\Exception:ClassCastException

Tofixthisproblem,wecanusethesafecastoperatoras?.Itissometimescalledthenullablecastoperator.Thisoperatortriestocastavaluetothespecifiedtype,andreturnsnullifthevaluecannotbecasted.Hereisanexample:

valfragment:String="ProductFragment"

valproductFragment:ProductFragment?=fragmentas?

ProductFragment

Notice,thatusageofthesafecastoperatorrequiresustodefinethenamevariableasnullable(ProductFragment?insteadofProductFragment).Asanalternative,wecanusetheunsafecastoperatorandnullabletypeProductFragment?,sowecansee

Page 86: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

exactlythetypethatwearecastingto:

valfragment:String="ProductFragment"

valproductFragment:ProductFragment?=fragmentas

ProductFragment?

IfwewouldliketohaveaproductFragmentvariablethatisnon-nullable,thenwewouldhavetoassignadefaultvalueusingtheelvisoperator:

valfragment:String="ProductFragment"

valproductFragment:ProductFragment?=fragmentas?

ProductFragment?:ProductFragment()

Now,thefragmentas?ProductFragmentexpressionwillbeevaluatedwithoutasingleerror.Ifthisexpressionreturnsanon-nullablevalue(thecastcanbeperformed),thenthisvaluewillbeassignedtotheproductFragmentvariable,otherwiseadefaultvalue(thenewinstanceofProductFragment)willbeassignedtotheproductFragmentvariable.Hereisacomparisonbetweenthesetwooperators:

Unsafecast(as):ThrowsClassCastExceptionwhencastingisimpossibleSafecast(as?):Returnsnullwhencastingimpossible

Now,whenweunderstandthedifferencebetweensafecastandunsafecastoperators,wecansafelyretrieveafragmentfromthefragmentmanager:

varproductFragment:ProductFragment?=supportFragmentManager

.findFragmentById(R.id.fragment_product)as?ProductFragment

Thesafecastandunsafecastoperatorsareusedforcastingcomplexobjects.Whenworkingwithprimitivetypes,wecansimplyuseoneoftheKotlinstandardlibraryconversionmethods.MostoftheobjectsfromtheKotlinstandardlibraryhavestandardmethodsusedtosimplifycommoncastingtoothertypes.Theconventionisthatthiskindoffunctionshaveprefixto,andthenameoftheclassthatwewanttocastto.Inthelineinthisexample,theInttypeiscastedtotheStringtypeusingthetoStringmethod:

valname:String

valage:Int=12

name=age.toString();//ConvertsInttoString

Wewilldiscussprimitivetypesandtheirconversionsintheprimitivedatatypessection.

Page 87: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

SmartcastsSmartcastingconvertsvariableofonetypetoanothertype,butasopposedtosafecasting,itisdoneimplicitly(wedon'tneedtousetheasoras?castoperator).SmartcastsworkonlywhentheKotlincompilerisabsolutelysurethatthevariablewillnotbechangedaftercheck.Thismakesthemperfectlysafeformultithreadedapplications.Generallysmartcastsareavailableforallimmutablereferences(val)andforlocalmutablereferences(var).Wehavetwokindsofsmartcasts:

TypesmartcastthatcastobjectsofonetypetoanobjectofanothertypeNullitysmartcastthatcastnullablereferencestonon-nullable

Page 88: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

TypesmartcastsLet'srepresenttheAnimalandFishclassfromtheprevioussection:

Let'sassumewewanttocalltheisHungrymethodandwewanttocheckiftheanimalisaninstanceofFish.InJavawewouldhavetodosomethinglikethis:

\\Java

if(animalinstanceofFish){

Fishfish=(Fish)animal;

fish.isHungry();

//or

((Fish)animal).isHungry();

}

Theproblemwiththiscodeisitsredundancy.WehavetocheckifanimalinstanceisFishandthenwehavetoexplicitlycastanimaltoFishafterthischeck.Wouldn'titbeniceifcompilercouldhandlethisforus?ItturnsoutthattheKotlincompilerisreallysmartwhenitcomestocasts,soitwillhandleallthoseredundantcastsforus,usingthesmartcastsmechanism.Hereisanexampleofsmartcasting:

if(animalisFish){

animal.isHungry()

}

SmartcastinAndroidStudio

AndroidStudiowilldisplaypropererrorsifsmartcastingisnotpossible,sowewillknowexactlyifwecanuseit.AndroidStudiomarksvariableswithgreenbackgroundwhenweaccessamemberthatrequiredacast.

Page 89: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

InKotlin,wedon'thavetoexplicitlycastananimalinstancetoaFish,becauseafterthetypecheck,Kotlincompilerwillbeabletohandlecastsimplicitly.Nowinsidetheifblock,thevariableanimaliscastedtoFish.TheresultisthenexactlythesameasinpreviousJavaexample(theJavainstanceoftheoperatoriscalledisinKotlin).ThisiswhywecansafelycalltheisHungrymethodwithoutanyexplicitcasting.Notice,thatinthiscase,thescopeofthissmartcastislimitedbytheifblock:

if(animalisFish){

animal.isHungry()//1

}

animal.isHungry()//2,Error

1. InthiscontextanimalinstanceisFish,sowecancallisHungrymethod.2. InthiscontextanimalinstanceisstillAnimal,sowecan'tcallisHungry

method.

Thereare,however,othercaseswherethesmartcastscopeislargerthanasingleblock,aslikeinthefollowingexample:

valfish:Fish?=//...

if(animal!isFish)//1

return

animal.isHungry()//1

1. Fromthispoint,animalwillbeimplicitlyconvertedtonon-nullableFish

Intheprecedingexample,thewholemethodwouldreturnfromfunctionifanimalisnotFish,sothecompilerknowsthatanimalmustbeaFishacrosstherestofthecodeblock.KotlinandJavaconditionalexpressionsareevaluatedlazily.

Itmeansthatinexpressioncondition1()&&condition2(),methodcondition2willbecalledonlywhencondition1returnstrue.Thisiswhywecanuseasmartcastedtypeintheright-handsideoftheconditionalexpression:

if(animalisFish&&animal.isHungry()){

println("Fishishungry")

}

Page 90: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

NoticethatiftheanimalwasnotaFish,thesecondpartoftheconditionalexpressionwouldnotbeevaluatedatall.Whenitisevaluated,KotlinknowsthatanimalisaFish(smartcast).

Page 91: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Non-nullablesmartcastSmartcastsalsohandleothercases,includingnullitychecks.Let'sassumethatwehaveaviewvariablethatismarkedasnullable,becausewedon'tknowwhereverornotfindViewByIdwillreturnaviewornull:

valview:View?=findViewById(R.layout.activity_shop)

Wecouldusesafecalloperatortoaccessviewmethodsandproperties,butinsomecaseswemaywanttoperformmoreoperationsonthesameobject.Inthesesituationssmartcastingmaybeabettersolution:

valview:View?

if(view!=null){

view.isShown()

//viewiscastedtonon-nullableinsideifcodeblock

}

view.isShown()//error,outsideiftheblockviewisnullable

Whenperformingnullcheckslikethis,thecompilerautomaticallycastsanullableview(View?)tonon-nullable(View).ThisiswhywecancalltheisShownmethodinsidetheifblock,withoutusingasafecalloperator.Outsidetheifblock,theviewisstillnullable.

Eachsmartcastsworksonlywithread-onlyvariables,becauseread-writevariablemaychangebetweenthetimethecheckwasperformedandthetimethevariableisaccessed.

Smartcastsalsoworkwithafunction'sreturnstatements.Ifweperformnullitychecksinsidethefunctionwithareturnstatement,thenthevariablewillalsobecastedtonon-nullable:

funsetView(view:View?){

if(view==null)

return

//viewiscastedtonon-nullable

view.isShown()

}

Inthiscase,Kotlinisabsolutelysurethatthevariablevaluewillnotbenull,

Page 92: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

becausethefunctionwouldcallreturnotherwise.FunctionswillbediscussedinmoredetailinChapter3,PlayingwithFunctions.Wecanmaketheprecedingsyntaxevensimplerbyusingelvisoperatorandperformanullitycheckinasingleline:

funverifyView(view:View?){

view?:return

//viewiscastedtonon-nullable

view.isShown()

//..

}

Insteadofjustreturningfromthefunction,wemaywanttobemoreexplicitaboutexistingproblemandthrowanexception.Thenwecanuseelvisoperatortogetherwiththeerrorthrow:

funsetView(view:View?){

view?:throwRuntimeException("Viewisempty")

//viewiscastedtonon-nullable

view.isShown()

}

Aswecansee,smartcastsareaverypowerfulmechanismthatallowsustodecreasethenumberofnullitychecks.ThisiswhyitisheavilyexploitedbyKotlin.Rememberthegeneralrule--smartcastsworkonlyifKotlinisabsolutelysurethatthevariablecannotchangeafterthecastevenbyanotherthread.

Page 93: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

PrimitivedatatypesInKotlin,everythingisanobject(referencetype,notprimitivetype).Wedon'tfindprimitivetypes,likeoneswecanuseinJava.Thisreducescodecomplexity.Wecancallmethodsandpropertiesonanyvariable.Forexample,thisishowwecanconverttheIntvariabletoaChar:

varcode:Int=75

code.toChar()

Usually(wheneveritispossible),underthehoodtypessuchasInt,Long,orCharareoptimized(storedasprimitivetypes)butwecanstillcallmethodsonthemasonanyotherobjects.

Bydefault,JavaplatformstoresnumbersasJVMprimitivetypes,butwhenanullablenumberreference(forexample,Int?)isneededorgenericsareinvolved,Javausesboxedrepresentation.Boxingmeanswrappingaprimitivetypeintocorrespondingboxedprimitivetype.Thismeansthattheinstancebehavesasanobject.ExamplesofJavaboxedrepresentationsofprimitivetypesareintversusIntegeroralongversusLong.SinceKotliniscompiledtoJVMbytecode,thesameistruehere:

varweight:Int=12//1

varweight:Int?=null//2

1. Valueisstoredasprimitivetype2. Valueisstoredasboxedinteger(compositetype)

Thismeansthateachtimewecreateanumber(Byte,Short,Int,Long,Double,Float),orwithChar,Boolean,itwillbestoredasaprimitivetypeunlesswedeclareitasanullabletype(Byte?,Char?,Array?,andsoon);otherwise,itwillbestoredasaboxedrepresentation:

vara:Int=1//1

varb:Int?=null//2

b=12//3

1. aisnon-nullable,soitisstoredasprimitivetype2. bisnullsoitisstoredasboxedrepresentation

Page 94: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

3. bisstillstoredasboxedrepresentationalthoughithasavalue

Generictypescannotbeparameterizedusingprimitivetypes,soboxingwillbeperformed.It'simportanttorememberthatusingboxedrepresentation(compositetype)insteadofprimaryrepresentationcanhaveperformancepenalties,becauseitwillalwayscreatememoryoverheadcomparedtoprimitivetyperepresentation.Thismaybenoticeableforlistsandarrayscontainingahugenumberofelements,sousingprimaryrepresentationmaybecrucialforapplicationperformance.Ontheotherhand,weshouldnotworryaboutthetypeofrepresentationwhenitcomestoasinglevariableorevenmultiplevariabledeclarations,evenintheAndroidworld,wherememoryislimited.

Nowlet'sdiscussthemostimportantKotlinprimitivedatatypes:numbers,characters,Booleans,andarrays.

Page 95: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

NumbersBasicKotlindatatypesusedfornumbersareequivalentsofJavanumericprimitives:

Kotlin,however,handlesnumbersalittlebitdifferentlythanJava.Thefirstdifferenceisthattherearenoimplicitconversionsfornumbers--smallertypesarenotimplicitlyconvertedtobiggertypes:

varweight:Int=12

vartruckWeight:Long=weight//Error1

ThismeansthatwecannotassignavalueoftypeInttotheLongvariablewithoutanexplicitconversion.Aswesaid,inKotlineverythingisanobject,sowecancallthemethodandexplicitlyconvertInttypetoLongtofixtheproblem:

varweight:Int=12

vartruckWeight:Long=weight.toLong()

Atfirst,thismayseemlikeboilerplatecode,butinpracticethiswillallowustoavoidmanyerrorsrelatedtonumberconversionandsavealotofdebuggingtime.ThisisactuallyarareexamplewhereKotlinsyntaxhasmoreamountofcodethanJava.TheKotlinstandardlibrarysupportsthefollowingconversionmethodsfornumbers:

toByte():BytetoShort():ShorttoInt():InttoLong():LongtoFloat():FloattoDouble():Double

Page 96: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

toChar():Char

Wecan,however,explicitlyspecifyanumberliteraltochangetheinferredvariabletype:

vala:Int=1

valb=a+1//InferredtypeisInt

valb=a+1L//InferredtypeisLong

TheseconddifferencebetweenKotlinandJavanumbersisthatnumberliteralsareslightlydifferentinsomecases.Therearethefollowingkindsofliteralconstantsforintegralvalues:

27//Decimalsbydefault

27L//LongsaretaggedbyauppercaseLsuffix

0x1B//Hexadecimalsaretaggedby0xprefix

0b11011//Binariesaretaggedby0bprefix

Octalliteralsarenotsupported.Kotlinalsosupportsaconventionalnotationforfloating-pointnumbers:

27.5//InferredtypeisDouble

27.5F//InferredtypeisFloat.FloataretaggedbyforF

Page 97: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

CharCharactersinKotlinarestoredintypeChar.Inmanyways,charactersaresimilartostrings,sowewillconcentrateonthesimilaritiesanddifferences.TodefineChar,wemustuseasinglequotekindofoppositetoaStringwhereweareusingdoublequotes:

valchar='a'\\1

valstring="a"\\2

1. DefinesvariableoftypeChar2. DefinesvariableoftypeString

Inbothcharactersandstrings,specialcharacterscanbeescapedusingabackslash.Thefollowingescapesequencesaresupported:

\t:Tabulator\b:Backspace\n:Newline\r:Newline\':Quote\":Doublequote\\:Slash\$:Dollarcharacter\u:Unicodeescapesequence

Let'sdefineCharcontainingtheYinYangunicodecharacter(U+262F):

varyinYang='\u262F'

Page 98: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ArraysInKotlin,arraysarerepresentedbytheArrayclass.TocreateanarrayinKotlin,wecanuseanumberofKotlinstandardlibraryfunctions.ThesimplestoneisarrayOf():

valarray=arrayOf(1,2,3)//inferredtypeArray<Int>

Bydefault,thisfunctionwillcreateanarrayofboxedInt.IfwewanttohaveanarraycontainingShortorLong,thenwehavetospecifyarraytypeexplicitly:

valarray2:Array<Short>=arrayOf(1,2,3)

valarray3:Array<Long>=arrayOf(1,2,3)

Aspreviouslymentioned,usingboxedrepresentationsmaydecreaseapplicationperformance.That'swhyKotlinhasafewspecializedclassesrepresentingarraysofprimitivetypestoreduceboxingmemoryoverhead:ShortArray,IntArray,LongArray,andsoon.TheseclasseshavenoinheritancerelationtotheArrayclass,althoughtheyhavethesamesetofmethodsandproperties.Tocreateinstancesofthisclasswehavetousethecorrespondingfactoryfunction:

valarray=shortArrayOf(1,2,3)

valarray=intArrayOf(1,2,3)

valarray=longArrayOf(1,2,3)

It'simportanttonoticeandkeepinmindthissubtledifference,becausethosemethodslooksimilar,butcreatedifferenttyperepresentations:

valarray=arrayOf(1,2,3)//1

valarray=longArrayOf(1,2,3)//2

1. GenericarrayofboxedLongelements(inferredtype:Array<Long>)2. ArraycontainingprimitiveLongelements(inferredtype:LongArray)

Knowingtheexactsizeofanarraywilloftenimproveperformance,soKotlinhasanotherlibraryfunction,arrayOfNulls,thatcreatesanarrayofagivensizefilledwithnullelements:

valarray=arrayOfNulls(3)//Prints:[null,null,null]

println(array)//Prints:[null,null,null]

Page 99: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Wecanalsofillapredefinedsizearrayusingthefactoryfunctionthattakesthearraysizeasthefirstparameterandthelambdathatcanreturntheinitialvalueofeacharrayelementgivenitsindexasthesecondparameter:

valarray=Array(5){it*2}

println(array)//Prints:[0,2,4,8,10]

Wewilldiscusslambdas(anonymousfunctions)inmoredetailinChapter5,FunctionsasFirstClassCitizen.AccessingarrayelementsinKotlinisdonethesamewayasinJava:

valarray=arrayOf(1,2,3)

println(array[1])//Prints:2

ElementarealsoindexedthesamewayasinJava,meaningthefirstelementhasindex0,secondhasindex1,andsoon.Noteverythingworksthesameandtherearesomedifferences.MainoneisthatarraysinKotlin,unlikeinJava,arraysareinvariant.WewilldiscussvarianceisChapter6,GenericsAreYourFriends.

Page 100: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

TheBooleantypeBooleanisalogictypethathastwopossiblevalues:trueandfalse.WecanalsousethenullableBooleantype:

valisGrowing:Boolean=true

valisGrowing:Boolean?=null

Booleantypealsosupportsstandardbuilt-inoperationsthataregenerallyavailableinmostmodernprogramminglanguages:

||:LogicalOR.Returnstruewhenanyoftwopredicatesreturntrue.&&:LogicalAND.Returnstruewhenbothpredicatesreturntrue.!:Negationoperator.Returnstrueforfalse,andfalsefortrue.

Keepinmindthatwecanonlyusenot-nullBooleanforanytypeofcondition.

LikeinJava,in||and&&,predicatesareevaluatedlazily,andonlywhenneeded(lazyconjunction).

Page 101: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

CompositedatatypesLet'sdiscussmorecomplextypesbuiltintoKotlin.SomedatatypeshavemajorimprovementscomparedtoJava,whileothersaretotallynew.

Page 102: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

StringsStringsinKotlinbehaveinasimilarwayasinJava,buttheyhaveafewniceimprovements.

Tostarttoaccesscharactersataspecifiedindexwecanuseindexingoperatorandaccesscharacterthesamewayweaccessarrayelements:

valstr="abcd"

println(str[1])//Prints:b

WealsohaveaccesstovariousextensionsdefinedinKotlinstandardlibrary,whichmakeworkingwithstringseasier:

valstr="abcd"

println(str.reversed())//Prints:dcba

println(str.takeLast(2))//Prints:cd

println("[email protected]".substringBefore("@"))//Prints:john

println("[email protected]".startsWith("@"))//Prints:false

ThisisexactlythesameStringclassasinJava,sothesemethodsarenotpartofStringclass.Theyweredefinedasextensions.WewilllearnmoreaboutextensionsinChapter7,ExtensionFunctionsandProperties.

ChecktheStringclassdocumentationforafulllistofthemethods(https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/).

Page 103: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

StringtemplatesBuildingstringsisaneasyprocess,butinJavaitusuallyrequireslongconcatenationexpressions.Let'sjumpstraighttoanexample.HereisastringbuiltfrommultipleelementsimplementedinJava:

\\Java

Stringname="Eva";

intage=27;

Stringmessage="Mynameis"+name+"andIam"+age+"yearsold";

InKotlin,wecangreatlysimplifytheprocessofstringcreationbyusingstringtemplates.Insteadofusingconcatenation,wecansimplyplaceavariableinsideastringusingadollarcharactertocreateaplaceholder.Duringinterpolation,stringplaceholderswillbereplacedwiththeactualvalue.Hereisanexample:

valname="Eva"

valage=27

valmessage="Mynameis$nameandIam$ageyearsold"

println(message)

//Prints:MynameisEvaandIam27yearsold

Thisisasefficientasconcatenation,becauseunderthehoodthecompiledcodecreatesaStringBuilderandappendsallthepartstogether.Stringtemplatesarenotlimitedtosinglevariables.Theycanalsocontainwholeexpressionsbetween${,and}characters.Itcanbeafunctioncallthatwillreturnthevalueorpropertyaccessasshowninthefollowingsnippet:

valname="Eva"

valmessage="Mynamehas${name.length}characters"

println(message)//Prints:Mynamehas3characters

Thissyntaxallowsustocreatemuchcleanercodewithouttheneedtobreakthestringeachtimeavaluefromavariableorexpressionisrequiredtoconstructstrings.

Page 104: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

RangesArangeisawaytodefineasequenceofvalues.Itisdenotedbythefirstandlastvalueinthesequence.Wecanuserangestostoreweights,temperatures,time,andage.Arangeisdefinedusingdoubledotsnotation(underthehood,arangeisusingtherangeTooperator):

valintRange=1..4//1

valcharRange='b'..'g'//2

1. InferredtypeisIntRange(equivalentofi>=1&&i<=4)2. InferredtypeisCharRange(equivalentoflettersfrom'b'to'g')

Noticethatweareusingsinglequotestodefinethecharacterrange.

TheInt,Long,andChartyperangescanbeusedtoiterateovernextvaluesinthefor...eachloop:

for(iin1..5)print(i)//Prints:1234

for(iin'b'..'g')print(i)//Prints:bcdefg

Rangescanbeusedtocheckifavalueisbiggerthanastartvalueandsmallerthananendvalue:

valweight=52

valhealthy=50..75

if(weightinhealthy)

println("$weightisin$healthyrange")

//Prints:52isin50..75range

Itcanbealsousedthiswayforothertypesofrange,suchasCharRange:

valc='k'//InferredtypeisChar

valalphabet='a'..'z'

if(cinalphabet)

println("$cischaracter")//Prints:kisacharacter

InKotlin,rangesareclosed(endinclusive).Thismeansthattherangeendingvalueisincludedintorange:

Page 105: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

for(iin1..1)print(i)//Prints:123

Note,thatrangesinKotlinareincrementalbydefault(astepisequalto1bydefault):

for(iin5..1)print(i)//Printsnothing

Toiterateinreverseorder,wemustuseadownTofunctionthatissettingastepto-1.Likeinthisexample:

for(iin5downTo1)print(i)//Prints:54321

Wecanalsosetdifferentsteps:

for(iin3..6step2)print(i)//Prints:35

Noticethatinthe3..6range,thelastelementwasnotprinted.Thisisbecausethesteppingindexismovingtwostepsineachoftheloopiterations.Sointhefirstiterationithasavalueof3,intheseconditerationavalueof5,andfinally,inathirditerationthevaluewouldbe7,soitisignored,becauseitisoutsidetherange.

Astepdefinedbythestepfunctionmustbepositive.IfwewanttodefineanegativestepthenweshouldusethedownTofunctiontogetherwiththestepfunction:

for(iin9downTo1step3)print(i)//Prints:963

Page 106: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

CollectionsAveryimportantaspectofprogrammingisworkingwithcollections.KotlinoffersmultiplekindsofcollectionsandmanyimprovementscomparedtoJava.WewilldiscussthissubjectinChapter7,ExtensionFunctionsandProperties.

Page 107: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

StatementsversusexpressionsKotlinutilizesexpressionsmorewidelythanJava,soitisimportanttoknowthedifferencebetweenastatementandanexpression.Aprogramisbasicallyasequenceofstatementsandexpressions.Expressionproducesavalue,whichcanbeusedaspartofanotherexpression,variableassignment,orfunctionparameter.Anexpressionisasequenceofoneormoreoperands(datathatismanipulated)andzeroormoreoperators(atokenthatrepresentsaspecificoperation)thatcanbeevaluatedtoasinglevalue:

Let'sreviewsomeexamplesofexpressionsfromKotlin:

Expression(produceavalue) Assignedvalue Expressionoftype

a=true true Boolean

a="foo"+"bar" "foobar" String

a=min(2,3) 2 Integer

a=computePosition().getX() ValuereturnedbygetXmethod Integer

Page 108: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Statements,ontheotherhand,performanactionandcannotbeassignedtoavariable,becausetheysimplydon'thaveavalue.Statementscancontainlanguagekeywordsthatareusedtodefineclasses(class),interfaces(interface),variables(val,var),functions(fun),looplogic(break,continue)andsoon.Expressionscanalsobetreatedasastatementwhenthevaluereturnedbytheexpressionisignored(donotassignvaluetovariable,donotreturnitfromafunction,donotuseitaspartofotherexpressions,andsoon).

Kotlinisanexpression-orientedlanguage.ThismeansthatmanyconstructsthatarestatementsinJavaaretreatedasexpressionsinKotlin.ThefirstmajordifferenceisthefactthatJavaandKotlinhavedifferentwaysoftreatingcontrolstructures.InJavatheyaretreatedasstatementswhileinKotlinallcontrolstructuresaretreatedasexpressions,exceptforloops.ThismeansthatinKotlinwecanwriteveryconcisesyntaxusingcontrolstructures.Wewillseeexamplesinupcomingsections.

Page 109: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ControlflowKotlinhasmanycontrolflowelementsknownfromJava,buttheyofferalittlebitmoreflexibilityandinsomecasestheirusageissimplified.KotlinintroducesanewcontrolflowconstructknownaswhenasareplacementforJavaswitch...case.

Page 110: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

TheifstatementAtitscore,Kotlin'sifclauseworksthesamewayasinJava:

valx=5

if(x>10){

println("greater")

}else{

println("smaller")

}

Theversionwiththeblockbodyisalsocorrectiftheblockcontainssinglestatementsorexpressions:

valx=5

if(x>10)

println("greater")

else

println("smaller")

Java,however,treatsifasastatementwhileKotlintreatsifasanexpression.Thisisthemaindifference,andthisfactallowsustousemoreconcisesyntax.Wecan,forexample,passtheresultofanifexpressiondirectlyasafunctionargument:

println(if(x>10)"greater"else"smaller")

Wecancompressourcodeintosingleline,becauseresulttheifexpression(oftypeString)isevaluatedandthenpassedtotheprintlnmethod.Whenconditionx>10istrue,thenfirstbranch(greater)willbereturnedbythisexpression,otherwisethesecondbranch(smaller)willbereturnedbythisexpression.Let'sexamineanotherexample:

valhour=10

valgreeting:String

if(hour<18){

greeting="Goodday"

}else{

greeting="Goodevening"

}

Intheprecedingexample,weareusingifasastatement.Butasweknow,ifin

Page 111: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Kotlinisanexpressionandtheresultoftheexpressioncanbeassignedtoavariable.Thiswaywecanassigntheresultoftheifexpressiontoagreetingvariabledirectly:

valgreeting=if(hour<18)"Goodday"else"Goodevening"

Butsometimesthereisaneedtoplacesomeothercodeinsidethebranchoftheifstatement.Wecanstilluseifasanexpression.Thenthelastlineofthematchingifbranchwillbereturnedasaresult:

valhour=10

valgreeting=if(hour<18){

//somecode

"Goodday"

}else{

//somecode

"Goodevening"

}

println(greeting)//Prints:"Goodday"

Ifweareusingifasanexpressionratherthanastatement,theexpressionisrequiredtohaveanelsebranch.TheKotlinversionisevenbetterthanJava.Sincethegreetingvariableisdefinedasnon-nullable,thecompilerwillvalidatethewholeifexpressionanditwillcheckthatallcasesarecoveredwithbranchconditions.Sinceifisanexpression,wecanuseitinsidestringtemplate:

valage=18

valmessage="Youare${if(age<18)"young"else"ofage"}person"

println(message)//Prints:Youareofageperson

TreatingifasexpressiongivesusawiderangeofpossibilitiespreviouslyunavailableinJavaworld.

Page 112: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ThewhenexpressionThewhenexpressioninKotlinisamultiwaybranchstatement.ThewhenexpressionisdesignedasamorepowerfulreplacementoftheJavaswitch...casestatement.Thewhenstatementoftenprovidesabetteralternativethanalargeseriesofif...elseifstatements,butitprovidesmoreconcisesyntax.Let'slookatanexample:

when(x){

1->print("x==1")

2->print("x==2")

else->println("xisneither1nor2")

}

Thewhenexpressionmatchesitsargumentagainstallbranchesoneafteranotheruntiltheconditionofsomebranchissatisfied.ThisbehaviorissimilartoJavaswitch...case,butwedonothavetowritearedundantbreakstatementaftereverybranch.

Similartotheifclause,wecanusewheneitherasastatementignoringreturnedvalueorasexpressionandassignitsvaluetoavariable.Ifwhenisusedasanexpression,thevalueofthelastlineofthesatisfiedbranchbecomesthevalueoftheoverallexpression.Ifitisusedasastatement,thevalueissimplyignored.Asusual,theelsebranchisevaluatedifnoneofthepreviousbranchessatisfythecondition:

valvehicle="Bike"

valmessage=when(vehicle){

"Car"->{

//Somecode

"Fourwheels"

}

"Bike"->{

//Somecode

"Twowheels"

}

else->{

//somecode

"Unknownnumberofwheels"

}

}

println(message)//Prints:Twowheels

Page 113: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Eachtimeabranchhasmorethanoneinstruction,wemustplaceitinsidethecodeblock,definedbytwobraces{...}.Ifwhenistreatedasanexpression(resultofevaluatingwhenisassignedtovariable),thelastlineofeachblockistreatedasreturnvalue.Wehaveseenthesamebehaviorwithanifexpression,sobynowweprobablyfiguredoutthatthisiscommonbehavioracrossmanyKotlinconstructsincludinglambdas,whichwillbediscussedfurtheracrossthebook.

Ifwhenisusedasanexpression,theelsebranchismandatory,unlessthecompilercanprovethatallpossiblecasesarecoveredwithbranchconditions.Wecanalsohandlemanymatchingargumentsinasinglebranchusingcommastoseparatethem:

valvehicle="Car"

when(vehicle){

"Car","Bike"->print("Vehicle")

else->print("Unidentifiedfunnyobject")

}

Anothernicefeatureofwhenistheabilitytocheckvariabletype.Wecaneasilyvalidatethatvalueisor!isofaparticulartype.Smartcastsbecomehandyagain,becausewecanaccessthemethodsandpropertiesofamatchingtypeinabranchblockwithoutanyextrachecks:

valname=when(person){

isString->person.toUpperCase()

isUser->person.name

//CodeissmartcastedtoString,sowecan

//callStringclassmethods

//...

}

Inasimilarway,wecancheckwhateverrangeorcollectioncontainsaparticularvalue.Thistimewe'lluseisand!iskeywords:

valriskAssessment=47

valrisk=when(riskAssessment){

in1..20->"negligiblerisk"

!in21..40->"minorrisk"

!in41..60->"majorrisk"

else->"undefinedrisk"

}

println(risk)//Prints:majorrisk

Actually,wecanputanykindofexpressionontheright-handsideofthewhenbranch.Itcanbeamethodcalloranyotherexpression.Considerthefollowing

Page 114: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

examplewherethesecondwhenexpressionisusedfortheelsestatement:

valriskAssessment=80

valhandleStrategy="Warn"

valrisk=when(riskAssessment){

in1..20->print("negligiblerisk")

!in21..40->print("minorrisk")

!in41..60->print("majorrisk")

else->when(handleStrategy){

"Warn"->"Riskassessmentwarning"

"Ignore"->"Riskignored"

else->"Unknownrisk!"

}

}

println(risk)//Prints:Riskassessmentwarning

Aswecansee,whenisaverypowerfulconstructallowingmorecontrolthanJavaswitch,butitisevenmorepowerfulbecauseitisnotlimitedonlytocheckingvaluesforequality.Inaway,itcanevenbeusedasareplacementforanif...elseifchain.Ifnoargumentissuppliedtothewhenexpression,thebranchconditionsbehaveasBooleanexpressions,andabranchisexecutedwhenitsconditionistrue:

privatefungetPasswordErrorId(password:String)=when{

password.isEmpty()->R.string.error_field_required

passwordInvalid(password)->R.string.error_invalid_password

else->null

}

Allthepresentedexamplesrequireanelsebranch.Eachtimewhenallthepossiblecasesarecovered,wecanomitanelsebranch(exhaustivewhen).Let'slookatthesimplestexamplewithBoolean:

vallarge:Boolean=true

when(large){

true->println("Big")

false->println("Big")

}

Compilercanverifythatallpossiblevaluesarehandled,sothereisnoneedtospecifyanelsebranch.ThesamelogicappliestoenumsandsealedclassesthatwillbediscussedinChapter4,ClassesandObjects.

ChecksareperformedbytheKotlincompiler,sowehavecertaintythatanycasewillnotbemissed.ThisreducesthepossibilityofacommonJavabugwherethedeveloperforgetstohandleallthecasesinsidetheswitchstatement(although

Page 115: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

polymorphismisusuallyabettersolution).

Page 116: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

LoopsLoopisacontrolstructurethatrepeatsthesamesetofinstructionsuntilaterminationconditionismet.InKotlin,loopscaniteratethroughanythingthatprovidesiterator.Iteratorisaninterfacethathastwomethods:hasNextandnext.Itknowshowtoiterateoveracollection,range,string,oranyentitythatcanberepresentedasasequenceofelements.

Toiteratethroughsomething,wehavetosupplyaniterator()method.AsStringdoesn'thaveone,soinKotlinitisdefinedasanextensionfunction.ExtensionswillbecoveredinChapter7,ExtensionFunctionsandProperties.

Kotlinprovidesthreekindsofloops:for,while,anddo...while.Allofthemworkthesameasinotherprogramminglanguages,sowewilldiscussthembriefly.

Page 117: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

TheforloopTheclassicJavaforloop,whereweneedtodefinetheiteratorexplicitly,isnotpresentinKotlin.HereisanexampleofthiskindofloopinJava:

//Java

Stringstr="FooBar";

for(inti=0;i<str.length();i++)

System.out.println(str.charAt(i));

Toiteratethroughacollectionofitemsfromstarttofinish,wecansimplyusetheforloopinstead:

vararray=arrayOf(1,2,3)

for(iteminarray){

print(item)

}

Itcanalsobedefinedwithoutablockbody:

for(iteminarray)

print(item)

Ifacollectionisagenericcollection,thenitemwillbesmartcastedtotypecorrespondingtoagenericcollectiontype.Inotherwords,ifacollectioncontainselementsoftypeInttheitemwillbesmartcasedtoInt:

vararray=arrayOf(1,2,3)

for(iteminarray)

print(item)//itemisInt

Wecanalsoiteratethroughthecollectionusingitsindex:

for(iinarray.indices)

print(array[i])

Thearray.indicesparamreturnsIntRangewithallindexes.Itistheequivalentof(1..array.length-1).ThereisalsoanalternativewithIndexlibrarymethodthatreturnsalistoftheIndexedValueproperty,whichcontainsanindexandvalue.Thiscanbedestructedintotheseelementsthisway:

for((index,value)inarray.withIndex()){

println("Elementat$indexis$value")

Page 118: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

}

Theconstruct(index,value)isknownasadestructivedeclarationandwewilldiscussitinChapter4,ClassesandObjects.

Page 119: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ThewhileloopThewhilelooprepeatsablock,whileitsconditionalexpressionreturnstrue:

while(condition){

//code

}

Thereisalsoado...whileloopthatrepeatsblocksaslongasaconditionalexpressionisreturningtrue:

do{

//code

}while(condition)

Kotlin,opposedtoJava,canusevariablesdeclaredinsidethedo...whileloopascondition.

do{

varfound=false

//..

}while(found)

Themaindifferencebetweenbothwhileanddo...whileloopsiswhenaconditionalexpressionisevaluated.Awhileloopischeckingtheconditionbeforecodeexecutionandifitisnottruethenthecodewon'tbeexecuted.Ontheotherhand,ado...whileloopfirstexecutesthebodyoftheloop,andthenevaluatestheconditionalexpression,sothebodywillalwaysexecuteatleastonce.Ifthisexpressionistrue,theloopwillrepeat.Otherwise,theloopterminates.

Page 120: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

OtheriterationsThereotherwaystoiterateovercollectionsusingbuilt-instandardlibraryfunctions,suchasforEach.WewillcovertheminChapter7,ExtensionFunctionsandProperties.

Page 121: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

BreakandcontinueAllloopsinKotlinsupportclassicbreakandcontinuestatements.Thecontinuestatementproceedstothenextiterationofthatloopwhilebreakstopstheexecutionofthemostinnerenclosingloop:

valrange=1..6

for(iinrange){

print("$i")

}

//prints:123456

Nowlet'saddaconditionandbreaktheiterationwhenthisconditionistrue:

valrange=1..6

for(iinrange){

print("$i")

if(i==3)

break

}

//prints:123

Thebreakandcontinuestatementsareespeciallyusefulwhendealingwithnestedloops.TheymaysimplifyourcontrolflowandsignificantlydecreasetheamountofperformedworktosavepricelessAndroidresources.Let'sperformanestediterationandbreaktheouterloop:

valintRange=1..6

valcharRange='A'..'B'

for(valueinintRange){

if(value==3)

break

println("Outerloop:$value")

for(charincharRange){

println("\tInnerloop:$char")

}

}

//prints

Outerloop:1

Innerloop:A

Innerloop:B

Outerloop:2

Page 122: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Innerloop:A

Innerloop:B

Weusedabreakstatementtoterminatetheouterloopatthebeginningofthethirditeration,sothenestedloopwasalsoterminated.Noticetheusageofthe\tescapedsequencethataddsindentsontheconsole.Wecanalsoutilizethecontinuestatementtoskipthecurrentiterationoftheloop:

valintRange=1..5

for(valueinintRange){

if(value==3)

continue

println("Outerloop:$value")

for(charincharRange){

println("\tInnerloop:$char")

}

}

//prints

Outerloop:1

Innerloop:A

Innerloop:B

Outerloop:2

Innerloop:A

Innerloop:B

Outerloop:4

Innerloop:A

Innerloop:B

Outerloop:5

Innerloop:A

Innerloop:B

Weskiptheiterationoftheouterloopwhenthecurrentvalueequalsto3.

Bothcontinueandbreakstatementsperformcorrespondingoperationsontheenclosingloop.Thereare,however,timeswhenwewanttoterminateorskipiterationofoneloopfromwithinanother;forexample,terminateanouterloopiterationfromwithinaninnerloop:

for(valueinintRange){

for(charincharRange){

//Howcanwebreakouterloophere?

}

}

Fortunately,bothacontinuestatementandbreakstatementhavetwoforms--labeledandunlabeled.Wealreadysawunlabeled,nowwewillneedlabeledtosolveourproblem.Hereisanexampleofhowalabeledbreakmightbeused:

Page 123: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

valcharRange='A'..'B'

valintRange=1..6

outer@for(valueinintRange){

println("Outerloop:$value")

for(charincharRange){

if(char=='B')

break@outer

println("\tInnerloop:$char")

}

}

//prints

Outerloop:1

Innerloop:A

[email protected],thelabelnamealwaysstartswith@followedbylabelname.Labelisplacedbeforetheloop.Labelingtheloopallowsustousequalifiedbreak(break@outer),whichisawaytostopexecutionofaloopthatisreferencedbythislabel.Theprecedingqualifiedbreak(breakwithlabel)jumpstotheexecutionpointrightaftertheloopmarkedwiththatlabel.

Placingthereturnstatementwillbreakalltheloopsandreturnfromenclosingananonymousornamedfunction:

fundoSth(){

valcharRange='A'..'B'

valintRange=1..6

for(valueinintRange){

println("Outerloop:$value")

for(charincharRange){

println("\tInnerloop:$char")

return

}

}

}

//usage

println("Beforemethodcall")

doSth()

println("Aftermethodcall")

//prints

Outerloop:1

Innerloop:A

Afterthemethodcall:

Outerloop:1

Innerloop:A

Page 124: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ExceptionsMostJavaprogrammingguidelines,includingthebookEffectiveJava,promotetheconceptofvaliditychecks.Thismeansthatweshouldalwaysverifyargumentsorthestateoftheobjectandthrowanexceptionifavaliditycheckfails.Javaexceptionsystemshavetwokindsofexceptions:checkedexceptionsanduncheckedexceptions.

Uncheckedexceptionmeansthatthedeveloperisnotforcedtocatchexceptionsbyusingatry...catchblock.Bydefault,exceptionsgoallthewayupthecallstack,sowemakedecisionswheretocatchthem.Ifweforgettocatchthem,theywillgoallthewayupthecallstackandstopthreadexecutionwithapropermessage(thustheyremindus):

Javahasareallystrongexceptionsystem,whichinmanycasesforcesdeveloperstoexplicitlymarkeachfunctionthatmaythrowanexceptionandexplicitlycatcheachexceptionbysurroundingthembytry...catchblocks(checkedexceptions).Thisworksgreatforverysmallprojects,butinreallarge-scaleapplicationsthisveryoftenleadstothefollowing,verbosecode:

//Java

try{

doSomething()

}catch(IOExceptione){

//Mustbesafe

}

Page 125: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Insteadofpassingtheexceptionupinthecallstack,itisignoredbyprovidinganemptycatchblock,soitwon'tbehandledproperlyanditwillvanish.Thiskindofcodemaymaskcriticalexceptionsandgiveafalsesenseofsecurityandleadtounexpectedproblemsanddifficulttofindbugs.

BeforewediscusshowexceptionhandlingisdoneinKotlin,let'scomparebothtypesofexceptions:

Code Checkedexceptions Uncheckedexceptions

Functiondeclaration

Wehavetospecifywhatexceptionscanbethrownbyfunctions.

Functiondeclarationdoesnotcontaininformationaboutallthrownexceptions.

Exceptionhandling

Functionthatthrowsexceptionmusttobesurroundedbyatry...catchblock.

Wecancatchexceptionanddosomethingifwewant,butwearen'tforcedtodothis.Exceptiongoesupinthecallstack.

ThebiggestdifferencebetweenKotlinandJavaexceptionsystemsisthatinKotlinallexceptionsareunchecked.Thismeansweneverhavetosurroundamethodwithtry...catchblockevenifthisisaJavamethodthatmaythrowacachedexception.Wecanstilldoit,butwearenotforcedto:

funfoo(){

throwIOException()

}

funbar(){

foo()//noneedtosurroundmethodwithtry-catchblock

}

Thisapproachremovescodeverbosityandimprovessafetybecausewedon'tneedtointroduceemptycatchblocks.

Page 126: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Thetry...catchblockKotlintry...catchblockistheequivalentoftheJavatry...catchblock.Let'slookatquickexample:

funsendFormData(user:User?,data:Data?){//1

user?:throwNullPointerException("Usercannotbenull")

//2

data?:throwNullPointerException("Datacannotbenull")

//dosomething

}

funonSendDataClicked(){

try{//3

sendFormData(user,data)

}catch(e:AssertionError){//4

//handleerror

}finally{//5

//optionalfinallyblock

}

}

1. ExceptionsarenotspecifiedonfunctionsignaturelikeinJava.2. WecheckvalidityofdataandthrowNullPointerException(noticethatnonew

keywordisrequiredwhencreatinganobjectinstance).3. Thetry...catchblockissimilarconstructfromJava.4. Handleonlythisspecificexceptions(AssertionErrorexception).5. Thefinallyblockisalwaysexecuted.

Theremaybezeroormorecatchblocksandfinallyblockmaybeomitted.However,atleastonecatchorfinallyblockshouldbepresent.

InKotlin,exceptionhandlingtryisanexpression,soitcanreturnavalueandwecanassignitsvaluetoavariable.Theactualassignedvalueisthelastexpressionoftheexecutedblock.Let'scheckifaparticularAndroidapplicationisinstalledonthedevice:

valresult=try{//1

context.packageManager.getPackageInfo("com.text.app",0)//2

true

}catch(ex:PackageManager.NameNotFoundException){//3

false

}

Page 127: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

1. Thetry...catchblockisreturningvaluethatisreturnedbyasingleexpressionfunction.

2. Ifanapplicationisinstalled,thegetPackageInfomethodwillreturnavalue(thisvalueisignored)andthenextlinecontainingtrueexpressionwillbeexecuted.Thisisthelastoperationperformedbyatryblock,soitsvaluewillbeassignedtoavariable(true).

Ifanappisnotinstalled,getPackageInfowillthrowPackageManager.NameNotFoundExceptionandthecatchblockwillbeexecuted.Thelastlineofthecatchblockcontainsafalseexpression,soitsvaluewillbeassignedtoavariable.

Page 128: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Compile-timeconstantsSincethevalvariableisreadonly,inmostcaseswecouldtreatitasaconstant.Weneedtobeawarethatitsinitializationmaybedelayed,sothismeansthattherearescenarioswherethevalvariablemaynotbeinitializedatcompiletime,forexample,assigningtheresultofthemethodcalltoavalue:

valfruit:String=getName()

Thisvaluewillbeassignedatruntime.Thereare,however,situationswhereweneedtoknowthevalueatcompiletime.Theexactvalueisrequiredwhenwewanttopassparameterstoannotations.Annotationsareprocessedbyanannotationprocessorthatrunslongbeforetheapplicationisstarted:

Tomakeabsolutelysurethatthevalueisknownatcompiletime(andthuscanbeprocessedbyanannotationprocessor),weneedtomarkitwithaconstmodifier.Let'sdefineacustomannotationMyLoggerwithasingleparameterdefiningmaximumlogentriesandannotateaTestclasswithit:

constvalMAX_LOG_ENTRIES=100

@MyLogger(MAX_LOG_ENTRIES)

//valueavailableatcompiletime

classTest{}

Therearecouplelimitationsregardingusageofconstthatwemustbeawareof.ThefirstlimitationisthatitmustbeinitializedwithvaluesofprimitivetypesorStringtype.Thesecondlimitationisthatitmustbedeclaredatthetoplevelorasamemberofanobject.WewilldiscussobjectsinChapter4,ClassesandObjects.Thethirdlimitationisthattheycannothaveacustomgetter.

Page 129: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

DelegatesKotlinprovidesfirst-classsupportfordelegation.ItisveryusefulimprovementcomparingtoJava.Iffact,therearemanyapplicationsfordelegatesinAndroiddevelopment,sowehavedecidedtospareawholechapteronthissubject(Chapter8,Delegates).

Page 130: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

SummaryInthischapter,wehavediscussedthedifferencesbetweenvariables,values,andconstsanddiscussedbasicKotlindatatypesincludingranges.WealsolookedintoaKotlintypesystemthatenforcesstrictnullsafetyandwaystodealwithnullablereferencesusingvariousoperatorsandsmartcasts.WeknowthatwecanwritemoreconcisecodebytakingadvantageofusingtypeinferenceandvariouscontrolstructuresthatinKotlinaretreatedasexpressions.Finally,wediscussedwaysofexceptionhandling.

Inthenextchapter,wewilllearnaboutfunctionsandpresentdifferentwaysofdefiningthem.Wewillcoverconceptssuchassingle-expressionfunctions,defaultargumentsandnamedargumentsyntax,anddiscussvariousmodifiers.

Page 131: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

PlayingwithFunctionsInpreviouschapters,we'veseenKotlinvariables,typesystems,andcontrolstructures.Buttocreateapplications,weneedbuildingblocksthatallowustomakestructures.InJava,theclassisthebuildingblockofthecode.Kotlin,ontheotherhand,supportsfunctionalprogramming;therefore,itmakesitpossibletocreatewholeprogramsorlibrarieswithoutanyclasses.FunctionisthemostbasicbuildingblockinKotlin.ThischapterintroducesfunctionsinKotlin,togetherwithdifferentfunctionfeaturesandtypes.

Inthischapter,wewillcoverthefollowingtopics:

BasicfunctionusageinKotlinUnitreturntypeThevarargparameterSingle-expressionfunctionsTail-recursivefunctionsDefaultargumentvaluesNamedargumentsyntaxTop-levelfunctionsLocalfunctionsNothingreturntype

Page 132: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

BasicfunctiondeclarationandusagesThemostcommonfirstprogramthatprogrammerswritetotestsomeprogramminglanguageistheHello,World!program.ItisafullprogramthatisjustdisplayingHello,World!textontheconsole.Wearealsogoingtostartwiththisprogram,becauseinKotlinitisbasedonafunctionandonlyonafunction(noclassisneeded).SotheKotlinHello,World!programlooksasfollows:

//SomeFile.kt

funmain(args:Array<String>){//1

println("Hello,World!")//2,Prints:Hello,World!

}

1. Afunctiondefinessingleparameterargs,whichcontainsanarrayofallargumentsusedtoruntheprogram(fromthecommandline).Itisdefinedasnon-nullable,becauseanemptyarrayispassedtoamethodwhentheprogramisstartedwithoutanyarguments.

2. TheprintlnfunctionisaKotlinfunctiondefinedintheKotlinstandardlibrarythatisequivalentoftheJavafunctionSystem.out.println.

ThisprogramtellsusalotaboutKotlin.Itshowshowfunctionlookslikeandthatwecandefinefunctionwithoutanyclass.First,let'sanalyzethestructureofthefunction.Itstartswiththefunkeyword,andthencomesthenameofthefunction,parametersinthebracket,andthefunctionbody.Hereisanotherexampleofasimplefunction,butthisoneisreturningavalue:

fundouble(i:Int):Int{

return2*i

}

Goodtoknowframe

Thereismuchconfusionaroundthedifferencebetweenmethodsandfunction.Commondefinitionsareasfollows:

Afunctionisapieceofcodethatiscalledbyname.Themethodisafunctionassociatedwithaninstanceofclass(object).Sometimesitiscalledmemberfunction.

Page 133: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Soinsimplerwords,functionsinsideclassesarecalledmethods.InJava,thereareofficiallyonlymethods,butacademicenvironmentsareoftenarguingthatstaticJavamethodsareinfactfunctions.InKotlinwecandefinefunctionsthatarenotassociatedwithanyobject.

SyntaxtocallafunctionisthesameinKotlinasinJava,andmostmodernprogramminglanguages:

vala=double(5)

Wecallthedoublefunctionandassignavaluereturnedbyittoavariable.Let'sdiscussthedetailsofparametersandreturntypesofKotlinfunctions.

Page 134: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ParametersParametersinKotlinfunctionsaredeclaredusingthePascalnotation,andthetypeofeachparametermustbeexplicitlyspecified.Allparametersaredefinedasaread-onlyvariable.Thereisnowaytomakeparametersmutable,becausesuchbehavioriserror-proneandinJavaitwasoftenabusedbytheprogrammers.Ifthereisaneedforthat,thenwecanexplicitlyshadowparametersbydeclaringlocalvariableswiththesamename:

funfindDuplicates(list:List<Int>):Set<Int>{

varlist=list.sorted()

//...

}

Thisispossible,butitistreatedasbadpractice,soawarningwillbedisplayed.Abetterapproachistonameparametersbydatatheyprovideandvariablesbythepurposetheyserve.Thesenamesshouldbethendifferentinmostcases.

Parametersversusarguments

Intheprogrammingcommunity,argumentsandparametersareoftenthoughtobethesamething.Thesewordscannotbeusedinterchangeablybecausetheyhavedifferentmeanings.Anargumentisanactualvaluethatispassedtothefunctionwhenafunctioniscalled.Parameterreferstothevariablesdeclaredinsidefunctiondeclaration.Considerthefollowingexample:

funprintSum(a1:Int,a2:Int){//1.

print(a1+a2)

}

add(3,5)//2.

1-a1anda2areparameters

2-3and5arearguments

AswithJava,functionsinKotlincancontainmultipleparameters:

funprintSum(a:Int,b:Int){

valsum=a+b

print(sum)

}

Page 135: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Argumentsprovidedtofunctionscanbesubtypesofthetypespecifiedinparameterdeclaration.Asweknow,inKotlin,thesupertypeofallthenon-nullabletypesisAny,soweneedtouseit,ifwewanttoacceptalltypes:

funpresentGently(v:Any){

println("Hello.Iwouldliketopresentyou:$v")

}

presentGently("Duck")

//Hello.Iwouldliketopresentyou:Duck

presentGently(42)

//Hello.Iwouldliketopresentyou:42

Toallownullonarguments,thetypeneedstobespecifiedasnullable.NotethatAny?issupertypeofallnullableandnon-nullabletypes,sowecanpassobjectsofanytypeasarguments:

funpresentGently(v:Any?){

println("Hello.Iwouldliketopresentyou:$v")

}

presentGently(null)

//Prints:Hello.Iwouldliketopresentyou:null

presentGently(1)

//Prints:Hello.Iwouldliketopresentyou:1

presentGently("Str")

//Prints:Hello.Iwouldliketopresentyou:Str

Page 136: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ReturningfunctionsSofar,mostofthefunctionsweredefinedlikeprocedures(functionsthatdoesnotreturnanyvalues).Butinfact,therearenoproceduresinKotlinandallfunctionsreturnsomevalue.Whenitisnotspecified,thedefaultreturnvalueistheUnitinstance.Wecansetitexplicitlyfordemonstrationpurposes:

funprintSum(a:Int,b:Int):Unit{//1

valsum=a+b

print(sum)

}

1. UnlikeinJava,wearedefiningthereturntypeafterfunctionnameandparameters.

TheUnitobjectistheequivalentofJava'svoid,butitcanbetreatedasanyotherobject.Sowecanstoreitinvariable:

valp=printSum(1,2)

println(pisUnit)//Prints:true

Ofcourse,KotlincodingconventionsclaimsthatwhenafunctionisreturningUnitthenthetypedefinitionshouldbeomitted.Thiswaycodeismorereadableandsimplertounderstand:

funprintSum(a:Int,b:Int){

valsum=a+b

print(sum)

}

GoodtoknowframeUnitisasingleton,whatmeansthatthereisonlyoneinstanceofit.Soallthreeconditionsaretrue:

println(pisUnit)//Print:true

println(p==Unit)//Print:true

println(p===Unit)//Print:true

SingletonpatternishighlysupportedinKotlinanditwillbemorethoroughlycoveredinChapter4,Classesandobjects.

ToreturnoutputfromfunctionswithUnitreturntype,wecansimplyuseareturn

Page 137: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

statementwithoutanyvalue:

funprintSum(a:Int,b:Int){//1

if(a<0||b<0){

return//2

}

valsum=a+b

print(sum)

//3

}

1. Thereisnoreturntypespecified,soreturntypeisimplicitlysettoUnit.

2. Wecanjustusereturnwithoutanyvalue.3. WhenafunctionreturnsUnit,thenreturncallisoptional.Wedon'thaveto

useit.

WecouldalsousereturnUnit,butitshouldnotbeusedbecausethatwouldbemisleadingandlessreadable.

Whenwespecifythereturntype,otherthantheUnit,thenwealwaysneedtoreturnthevalueexplicitly:

funsumPositive(a:Int,b:Int):Int{

if(a>0&&b>0){

returna+b

}

//Error,1

}

1. Functionwillnotcompileit,becausenoreturnvaluewasspecified,theifconditionisnotfulfilled.

Theproblemcanbefixedbyaddingasecondreturnstatement:

funsumPositive(a:Int,b:Int):Int{

if(a>=0&&b>=0){

returna+b

}

return0

}

Page 138: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

VarargparameterSometimes,thenumberofparametersisnotknowninadvance.Insuchcaseswecanaddavarargmodifiertoaparameter.Itallowsthefunctiontoacceptanynumberofarguments.Hereisanexample,wherethefunctionisprintingthesumofmultipleintegers:

funprintSum(varargnumbers:Int){

valsum=numbers.sum()

print(sum)

}

printSum(1,2,3,4,5)//Prints:15

printSum()//Prints:0

Argumentswillbeaccessibleinsidethemethodasanarraythatholdsalltheprovidedvalues.Thetypeofthearraywillcorrespondtoavarargparametertype.Normallywewouldexpectittobeagenericarrayholdingaspecifiedtype(Array<T>),butasweknow,KotlinhasanoptimizedtypeforarrayofIntcalledIntArray,sothistypewillbeused.Here,forexample,isthetypeofthevarargparameterwiththetypeString:

funprintAll(varargtexts:String){

//InferredtypeoftextsisArray<String>

valallTexts=texts.joinToString(",")

println("Textsare$allTexts")

}

printAll("A","B","C")//Prints:TextsareA,B,C

Notethatwearestillabletospecifymoreparametersbeforeorafterthevarargparameter,aslongasitisclearwhichargumentisdirectedtowhichparameter:

funprintAll(prefix:String,postfix:String,varargtexts:String)

{

valallTexts=texts.joinToString(",")

println("$prefix$allTexts$postfix")

}

printAll("Alltexts:","!")//Prints:Alltexts:!

printAll("Alltexts:","!","Hello","World")

//Prints:Alltexts:Hello,World!

Additionally,argumentsprovidedtovarargparameterscanbesubtypesofthespecifiedtype:

Page 139: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

funprintAll(varargtexts:Any){

valallTexts=texts.joinToString(",")//1

println(allTexts)

}

//Usage

printAll("A",1,'c')//Prints:A,1,c

1. ThejoinToStringfunctioncanbeinvokedonlists.Itisjoiningelementsintoasinglestring.Onthefirstargumentthereisaseparatorspecified.

Onelimitationwithvarargusageisthatthereisonlyonevarargparameterallowedperfunctiondeclaration.

Whenwecallvarargparameters,wecanpassargumentvaluesone-by-one,butwecanalsopassanarrayofvalues.Thiscanbedoneusingthespreadoperator(*prefixingarray),asinthefollowingexample:

valtexts=arrayOf("B","C","D")

printAll(*texts)//Prints:Textsare:B,C,D

printAll("A",*texts,"E")//Prints:Textsare:A,B,C,D,E

Page 140: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Single-expressionfunctionsDuringtypicalprogramming,manyfunctionscontainonlyoneexpression.Hereisanexampleofthiskindoffunction:

funsquare(x:Int):Int{

returnx*x

}

Oranotherone,whichcanbeoftenfoundinAndroidprojects,isapatternusedinActivity,todefinemethodsthatarejustgettingtextfromsomevieworprovidingsomeotherdatafromtheviewtoallowapresentertogetthem:

fungetEmail():String{

returnemailView.text.toString()

}

Bothfunctionsaredefinedtoreturnresultsofasingleexpression.Inthefirstexample,itistheresultofx*xmultiplication,andinthesecondoneitistheresultoftheexpressionemailView.text.toString().ThesekindsoffunctionsareusedallaroundAndroidprojects.Herearesomecommonusecases:

Extractingsomesmalloperations(likeintheprecedingsquarefunction)UsingpolymorphismtoprovidevaluesspecifictoaclassFunctionsthatareonlycreatingsomeobjectFunctionsthatarepassingdatabetweenarchitecturelayers(likeintheprecedingexample,Activityispassingdatafromtheviewtothepresenter)Functionalprogrammingstylefunctionsthatarebasedonrecurrence

Suchfunctionsareoftenused,soKotlinhasanotationforthiskindofthem.Whenafunctionreturnsasingleexpression,thencurlybracesandbodyofthefunctioncanbeomitted.Wespecifyexpressiondirectlyusingtheequalitycharacter.Functionsdefinedthiswayarecalledsingle-expressionfunctions.Let'supdateoursquarefunction,anddefineitasasingle-expressionfunction:

Page 141: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Aswecansee,single-expressionfunctionshaveexpressionbodyinsteadofblockbody.Thisnotationisshorter,butwholebodyneedstobejustasingleexpression.

Insingle-expressionfunctions,declaringthereturntypeisoptional,becauseitcanbeinferredbythecompilerfromthetypeofexpression.Thisiswhywecansimplifythesquarefunction,anddefineitthisway:

funsquare(x:Int)=x*x

TherearemanyplacesinsideAndroidapplicationswherewecanutilizesingleexpressionfunctions.Let'sconsidertheRecyclerViewadapterthatisprovidingthelayoutIDandcreatingViewHolder:

classAddressAdapter:ItemAdapter<AddressAdapter.ViewHolder>(){

overridefungetLayoutId()=R.layout.choose_address_view

overridefunonCreateViewHolder(itemView:View)=ViewHolder(itemView)

//Restofmethods

}

Inthefollowingexample,weachievehighreadabilitythankstoasingleexpressionfunction.Singleexpressionfunctionsarealsoverypopularinthefunctionalworld.Theexamplewillbedescribedlater,inthesectionabouttail-recursivefunctions.Singleexpressionfunctionnotationalsopairswellwiththewhenstructure.Hereisanexampleoftheirconnection,usedtogetspecific

Page 142: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

datafromanobjectaccordingtoakey(usecasefrombigKotlinproject):

funvalueFromBooking(key:String,booking:Booking?)=when(key){

//1

"patient.nin"->booking?.patient?.nin

"patient.email"->booking?.patient?.email

"patient.phone"->booking?.patient?.phone

"comment"->booking?.comment

else->null

}

1. Wedon'tneedatype,becauseitisinferredfromthewhenexpression.

AnothercommonAndroidexampleisthatwecancombinewhenexpressionswithactivitymethodonOptionsItemSelectedthathandlestopbarmenuclicks:

overridefunonOptionsItemSelected(item:MenuItem):Boolean=when

{

item.itemId==android.R.id.home->{

onBackPressed()

true

}

else->super.onOptionsItemSelected(item)

}

Anotherexamplewherethesyntaxofthesingle-expressionfunctionisusefuliswhenwechainmultipleoperationsonasingleobject:

funtextFormatted(text:String,name:String)=text

.trim()

.capitalize()

.replace("{name}",name)

valformatted=textFormatted("hello,{name}","Marcin")

println(formatted)//Hello,Marcin

Aswecansee,singleexpressionfunctionscanmakeourcodemoreconciseandimprovereadability.Single-expressionfunctionsarecommonlyusedinKotlinAndroidprojectsandtheyarereallypopularforfunctionalprogramming.

Imperativeversusdeclarativeprogramming

Imperativeprogramming:Thisprogrammingparadigmdescribestheexactsequenceofstepsrequiredtoperformanoperation.Itismostintuitiveformostprogrammers.

Declarativeprogramming:Thisprogrammingparadigmdescribesadesiredresult,butnotnecessarilystepstoachieveit

Page 143: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

(implementationofbehavior).Thismeansthatprogrammingisdonewithexpressionsordeclarationsinsteadofstatements.Bothfunctionalandlogicprogrammingarecharacterizedasdeclarativeprogrammingstyle.Declarativeprogrammingisoftenshorterandmorereadablethanimperative.

Page 144: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Tail-recursivefunctionsRecursivefunctionsarefunctionsthatarecallingthemselves.Let'sseeanexampleofrecursivefunction,getState:

fungetState(state:State,n:Int):State=

if(n<=0)state//1

elsegetState(nextState(state),n-1)

Theyareanimportantpartoffunctionalprogrammingstyle,buttheproblemisthateachrecursivefunctioncallneedstokeepthereturnaddressofthepreviousfunctiononthestack.Whenanapplicationrecursetoodeeply(therearetoomanyfunctionsonthestack),StackOverflowErroristhrown.Thislimitationpresentsaveryseriousproblemforrecurrenceusage.

Aclassicsolutionforthisproblemwastouseiterationinsteadofrecurrence,butthisapproachislessexpressive:

fungetState(state:State,n:Int):State{

varstate=state

for(iin1..n){

state=state.nextState()

}

returnstate

}

Apropersolutionforthisproblemisusageofthetail-recursivefunctionsupportedbymodernlanguagessuchasKotlin.Tail-recursivefunctionisaspecialkindofrecursivefunction,wherethefunctioniscallingitselfasthelastoperationitperforms(inotherwords:recursiontakesplaceinlastoperationofafunction).ThisallowsustooptimizerecursivecallsbycompilerandperformrecursiveoperationsinamoreefficientwaywithoutworryingaboutpotentialStackOverflowError.Tomakeafunctiontail-recursive,weneedtomarkitwithatailrecmodifier:

tailrecfungetState(state:State,n:Int):State=

if(n<=0)state

elsegetState(state.nextState(),n-1)

Tocheckouthowitisworking,let'scompilethiscodeanddecompiletoJava.Hereiswhatcanbefoundthen(codeaftersimplification):

Page 145: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

publicstaticfinalStategetState(@NotNullStatestate,intn)

{

while(true){

if(n<=0){

returnstate;

}

state=state.nextState();

n=n-1;

}

}

Implementationisbasedoniteration,sothereisnowaythatstackoverflowerrormighthappen.Tomakethetailrecmodifierwork,therearesomerequirementstobemet:

ThefunctionmustcallitselfonlyasthelastoperationitperformsItcannotbeusedwithintry/catch/finallyblocksAtthetimeofwriting,itwasallowedonlyinKotlincompiledtoJVM

Page 146: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

DifferentwaysofcallingafunctionSometimesweneedtocallafunctionandprovideonlyselectedarguments.InJava,wecouldcreatemultipleoverloadsofthesamemethod,butthissolutionhassomelimitations.Thefirstproblemisthatthenumberofpossiblepermutationsofagivenmethodisgrowingveryquickly(2n),makingthemverydifficulttomaintain.Thesecondproblemisthatoverloadsmustbedistinguishablefromeachother,socompilermayknowwhichoverloadtocall,sowhenamethoddefinesafewparameterswiththesametypewecan'tdefineallpossibleoverloads.That'swhyinJava,weoftenneedtopassmultiplenullvaluestoamethod:

//Java

printValue("abc",null,null,"!");

Multiplenullparametersprovideboilerplate.Suchasituationgreatlydecreasesmethodreadability.InKotlin,thereisnosuchproblem,becauseKotlinhasafeaturecalleddefaultargumentsandnamedargumentsyntax.

Page 147: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

DefaultargumentsvaluesDefaultargumentsaremostlyknownfromC++,whichisoneoftheoldestlanguagessupportingit.Adefaultargumentprovidesavalueforaparameterincaseitisnotprovidedduringmethodcall.Eachfunctionparametercanhaveadefaultvalue.Itmightbeanyvaluethatismatchingaspecifiedtypeincludingnull.Thiswaywecansimplydefinefunctionsthatcanbecalledinmultipleways.Thisisanexampleofafunctionwithdefaultvalues:

funprintValue(value:String,inBracket:Boolean=true,

prefix:String="",suffix:String=""){

print(prefix)

if(inBracket){

print("(${value})")

}else{

print(value)

}

println(suffix)

}

Wecanusethisfunctionthesamewayasanormalfunction(afunctionwithoutdefaultargumentvalues)byprovidingvaluesforeachparameter(allarguments):

printValue("str",true,"","")//Prints:(str)

Thankstothedefaultargumentvalues,wecancallafunctionbyprovidingargumentsonlyforparameterswithoutdefaultvalues:

printValue("str")//Prints:(str)

Wecanalsoprovideallparameterswithoutdefaultvalues,andonlysomethathaveadefaultvalue:

printValue("str",false)//Prints:str

Page 148: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

NamedargumentssyntaxSometimeswewantonlytopassavalueforthelastargument.Let'ssupposethatwedefinewanttovalueforasuffix,butnotforaprefixandinBracket(whichisdefinedbeforesuffix).Normallywewouldhavetoprovidevaluesforallpreviousparametersincludingthedefaultparametervalues:

printValue("str",true,true,"!")//Prints:(str)

Byusingnamedargumentsyntax,wecanpassspecificargumentsusingtheargumentname:

printValue("str",suffix="!")//Prints:(str)!

Thisallowsveryflexiblesyntax,wherewecansupplyonlychosenargumentswhencallingafunction(thatis,thefirstoneandthesecondfromtheend).Itisoftenusedtospecifywhatthisargumentisbecausesuchacallismorereadable:

printValue("str",inBracket=true)//Prints:(str)

printValue("str",prefix="Valueis")//Prints:Valueisstr

printValue("str",prefix="Valueis",suffix="!!")

//Prints:Valueisstr!!

Wecansetanyparameterswewantusingnamedparametersyntaxinanyorderaslongasallparameterswithoutdefaultvaluesareprovided.Theorderoftheargumentsisrelevant:

printValue("str",inBracket=true,prefix="Valueis")

//Prints:Valueis(str)

printValue("str",prefix="Valueis",inBracket=true)

//Prints:Valueis(str)

Orderofargumentsisdifferent,butbothprecedingcallsareequivalent.

Wecanalsousenamedargumentsyntaxtogetherwithclassiccall.Theonlyrestrictionisifwestartusingnamedsyntax,wecannotuseaclassiconefornextargumentsweareserving:

printValue("str",true,"")

printValue("str",true,prefix="")

printValue("str",inBracket=true,prefix="")

printValue("str",inBracket=true,"")//Error

Page 149: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

printValue("str",inBracket=true,prefix="","")//Error

Thisfeatureallowsustocallmethodsinaveryflexiblewaywithouttheneedtodefinemultiplemethodoverloads.

ThenamedargumentsyntaximposessomeextraresponsibilityforKotlinprogrammers.Weneedtokeepinmindthatwhenwechangeaparametername,wemaycauseerrorsintheproject,becausetheparameternamemaybeusedinotherclasses.AndroidStudiowilltakecareofitifwerenametheparameterusingbuilt-inrefactoringtools,butthiswillworkonlyinsideourproject.TheKotlinlibrarycreatorsshouldbeverycarefulwhileusingnamedargumentsyntax.ChangeoftheparameternamewillbreaktheAPI.NotethatthenamedargumentsyntaxcannotbeusedwhencallingJavafunctions,becauseJavabytecodedoesnotalwayspreservenamesoffunctionparameters.

Page 150: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Top-levelfunctionsAnotherthingwecanobserveinasimpleHello,World!program,isthatthemainfunctionisnotlocatedinsideanyclass.InChapter2,LayingaFoundation,wealreadymentionedthatKotlincandefinevariousentitiesatthetoplevel.Afunctionthatisdefinedattop-leveliscalledthetop-levelfunction.Hereisanexampleofoneofthem:

//Test.kt

packagecom.example

funprintTwo(){

print(2)

}

Top-levelfunctionscanbeusedallaroundthecode(assumingthattheyarepublic,whatisdefaultvisibilitymodifier).Wecancalltheminthesamewayasfunctionsfromthelocalcontext.Toaccesstop-levelfunction,weneedtoexplicitlyimportitintoafilebyusingtheimportstatement.FunctionsareavailableincodehintlistinAndroidStudio,soimportsareautomaticallyaddedwhenafunctionisselected(used).Asanexample,let'sseeatop-levelfunctiondefinedinTest.ktanduseitinsidetheMain.ktfile:

//Test.kt

packagecom.example

funprintTwo(){

print(2)

}

//Main.kt

importcom.example.printTwo

funmain(args:Array<String>){

printTwo()

}

Top-levelfunctionsareoftenuseful,butitisimportanttousethemwisely.Keepinmindthatdefiningpublictop-levelfunctionswillincreasethenumberoffunctionsavailableincodehintlist(byhintlistImeanalistofmethodssuggestedbytheIDEashints,whenwearewritingcode).Itisbecausepublictop-levelfunctionsaresuggestedbytheIDEineverycontext(becausetheycanbeusedeverywhere).Ifthenameofthetop-levelfunctiondoesnotclearlystate

Page 151: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

thatthisisatop-levelfunction,thenitmaybeconfusedwithamethodfromthelocalcontextandusedaccidentally.Herearesomegoodexamplesoftop-levelfunctions:

factorial

maxOfandminOflistOf

println

Herearesomeexamplesoffunctionsthatmaybepoorcandidatesfortoplevelfunctions:

sendUserData

showPossiblePlayers

ThisruleisapplicableonlyinKotlinobject-orientedprogrammingprojects.Infunction-orientedprogrammingprojects,thesearevalidtop-levelnames,butthenwesupposethatnearlyallfunctionsaredefinedinthetop-levelandnotasmethods.

Oftenwedefinefunctionswewanttouseonlyinspecificmodulesorspecificclasses.Tolimitfunctionvisibility(placewhereitcanbeused)wecanusevisibilitymodifiers.WewilldiscussvisibilitymodifiersinChapter4,ClassesandObjects.

Page 152: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Top-levelfunctionsunderthehoodWiththeAndroidprojects,KotliniscompiledtoJavabytecodethatrunsonDalvikVirtualMachine(beforeAndroid5.0)orAndroidRuntime(Android5.0andnewer).Bothvirtualmachinescanexecuteonlythecodethatisdefinedinsideaclass.TosolvethisproblemKotlincompilergeneratesclassesfortop-levelfunctions.TheclassnameisconstructedfromthefilenameandKtsuffix.Insidesuchaclass,allfunctionsandpropertiesarestatic.Forexample,let'ssupposethatwedefineafunctionwithinthePrinter.ktfile:

//Printer.kt

funprintTwo(){

print(2)

}

KotlincodeiscompiledintoJavabytecode.ThegeneratedbytecodewillbeanalogicaltothecodegeneratedfromthefollowingJavaclass:

//Java

publicfinalclassPrinterKt{//1

publicstaticvoidprintTwo(){//2

System.out.print(2);//3

}

}

1. PrinterKtisthenamemadefromthenameoffileandKtsuffix.2. Alltop-levelfunctionsandpropertiesarecompiledtostaticmethodsand

variables.3. printisaKotlinfunction,butsinceitisaninlinefunction,itscallis

replacedbyitsbodyduringcompilationtime.AnditsbodyincludesonlySystem.out.printlncall.

InlinefunctionswillbedescribedinChapter5,FunctionsasaFirstClassCitizen.

KotlinclassatJavabytecodelevelwillcontainmoredata(forexample,nameofparameters).WecanalsoaccessKotlintop-levelfunctionsfromJavafilesbyprefixingafunctioncallwiththeclassname:

//Javafile,callinsidesomemethod

PrinterKt.printTwo()

Page 153: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Thisway,Kotlintop-levelfunctionscallsfromJavaarefullysupported.Aswecansee,KotlinisreallyinteroperablewithJava.TomakeKotlintop-levelfunctionsusagemorecomfortableinJava,wecanaddanannotationthatwillchangethenameofaJVMgeneratedclass.Thiscomesinhandywhenmakingusageoftop-levelKotlinpropertiesandfunctionsfromJavaclasses.Thisannotationlooksasfollows:

@file:JvmName("Printer")

WeneedtoaddtheJvmNameannotationatthetopofthefile(beforepackagename).Whenthisisapplied,thenameofthegeneratedclasswillbechangedtoPrinter.ThisallowsustocalltheprintTwofunctioninJavausingPrinterastheclassname:

//Java

Printer.printTwo()

Sometimeswearedefiningtop-levelfunctions,andwewanttodefinetheminseparatefiles,butwealsowanttheminthesameclassaftercompilationtoJVM.Thisispossibleifweusethefollowingannotationintopofthefile:

@file:JvmMultifileClass

Forexample,let'sassumethatwearemakingalibrarywithmathematicalhelpersthatwewanttousefromJava.Wecandefinethefollowingfiles:

//Max.kt

@file:JvmName("Math")

@file:JvmMultifileClass

packagecom.example.math

funmax(n1:Int,n2:Int):Int=if(n1>n2)n1elsen2

//Min.kt

@file:JvmName("Math")

@file:JvmMultifileClass

packagecom.example.math

funmin(n1:Int,n2:Int):Int=if(n1<n2)n1elsen2

AndwecanuseitfromJavaclassesthisway:

Math.min(1,2)

Math.max(1,2)

Thankstothis,wecankeepfilesshortandsimple,whilekeepingthemalleasytousefromJava.

Page 154: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

TheJvmNameannotationtochangegeneratedclassnamesisespeciallyusefulwhenwecreatelibrariesinKotlinthatarealsodirectedtobeusedinJavaclasses.Itcanbeusefulincaseofnameconflictstoo.SuchasituationcanoccurwhenwecreatebothanX.ktfilewithsometop-levelfunctionsorpropertiesandanXKtclassinthesamepackage.ButitisrareandshouldnevertakeplacesincethereisaconventionthatnoclassesshouldhaveKtsuffix.

Page 155: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

LocalfunctionsKotlinallowsdefiningfunctionsinmanycontexts.Wecandefinefunctionsattop-level,asmembers(insidetheclass,interface,andsoon),andinsideotherfunction(localfunction).Considerthefollowingexampleofthedefinitionoflocalfunction:

funprintTwoThreeTimes(){

funprintThree(){//1

print(3)

}

printThree()//2

printThree()//2

}

1. printThreeisalocalfunction,becauseitislocatedinsideanotherfunction.2. Localfunctionsarenotaccessiblefromoutsidethefunctiontheywere

declaredin.

Elementsaccessibleinsidelocalfunctionsdon'thavetobepassedfromenclosingfunctionsasargumentsbecausetheyareaccessibledirectly.Forexample:

funloadUsers(ids:List<Int>){

vardownloaded:List<User>=emptyList()

funprintLog(comment:String){

Log.i("loadUsers(withids$ids):$comment\nDownloaded:

$downloaded")//1

}

for(idinids){

printLog("Startdownloadingforid$id")

downloaded+=loadUser(id)

printLog("Finisheddownloadingforid$id")

}

}

1. Localfunctioncanaccesscommentparameterandlocalvariables(downloadedandIDs),definedinsideanenclosingfunction.

IfwewouldliketodefineprintLogasatop-levelfunctionthenwewouldhavetopassasargumentsbothidsanddownloaded:

funloadUsers(ids:List<Int>){

vardownloaded:List<User>=emptyList()

Page 156: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

for(idinids){

printLog("Startdownloadingforid$id",downloaded,ids)

downloaded+=loadUser(id)

printLog("Finisheddownloadingfor

id$id",downloaded,ids))

}

}

funprintLog(state:String,downloaded:List<User>,ids:List<Int>)

{

Log.i("loadUsers(withids$ids):

$state\nDownloaded:downloaded")

}

Thisimplementationisnotonlylonger,butalsohardertomaintain.ChangesinprintLogmightdemanddifferentparameters,andachangeinparametersdemandschangesinargumentsinthisfunctioncall.Also,ifwechangetheloadUsersparametertypethatisusedinprintLogthenwewillneedtoalsochangetheparameterofprintLog.TherewouldbenosuchproblemsifprintLogwasalocalfunction.Thisexplainswhenlocalfunctionsshouldbeused:Whenweareextractingfunctionalitythatisusedonlybyasinglefunction,andthatfunctionalityisusingelements(variables,values,parameters)ofthisfunction.Also,localfunctionsareallowedtomodifylocalvariables.Likeinthisexample:

funmakeStudentList():List<Student>{

varstudents:List<Student>=emptyList()

funaddStudent(name:String,state:Student.State=

Student.State.New){

students+=Student(name,state,courses=emptyList())

}

//...

addStudent("AdamSmith")

addStudent("DonaldDuck")

//...

returnstudents

}

Thisway,wecanextractandreusefunctionalitythatcouldnotbeextractedinJava.Itisgoodtorememberaboutlocalfunctions,becausetheysometimesallowcodeextractionthatishardtoimplementinotherways.

Page 157: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

NothingreturntypeSometimesweneedtodefineafunctionthatisalwaysthrowingexceptions(neverterminatingnormally).Tworeal-lifeusecasesare:

Functionsthatsimplifyerrorthrowing.Thisisespeciallyusefulinlibrarieswhereerrorsystemisimportantandthereisaneedtoprovidemoredataabouterroroccurrence.(AsanexamplelookatthethrowErrorfunctionpresentedinthissection).Functionsusedforthrowingerrorsinunittests.Thisisusefulwhenweneedtotesterrorhandlinginourcode.

Forthesekindsofsituations,thereisaspecialclasscalledNothing.TheNothingclassisemptytype(uninhabitedtype),meaningithasnoinstances.AfunctionthathasNothingreturntypewon'treturnanythinganditwillneverreachreturnstatement.Itcanonlythrowanexception.ThisiswhywhenweseethatfunctionisreturningNothing,thenitisdesignedtothrowexceptions.Thiswaywecandistinguishfunctionsthatdonotreturnavalue(likeJava'svoid,Kotlin'sUnit)fromfunctionsthatneverterminate(returnsNothing).Letushavealookatanexampleoffunctionsthatmightbeusedtosimplifyerrorthrowinginunittests:

funfail():Nothing=throwError()

Andfunctionsthatareconstructingcomplexerrormessagesusingelementsavailableincontextwhereitisdefined(inclassorfunction):

funprocessElement(element:Element){

funthrowError(message:String):Nothing

=throwProcessingError("Errorinelement$element:$message")

//...

if(element.kind!=ElementKind.METHOD)

throwError("Notamethod")

//...

}

Thiskindoffunctionisthatitcanbeused,justlikeathrowstatement,asanalternativethatisnotinfluencingfunctionreturnedtype:

fungetFirstCharOrFail(str:String):Char

=if(str.isNotEmpty())str[0]elsefail()

Page 158: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

valname:String=getName()?:fail()

valenclosingElement=element.enclosingElement?:throwError("Lackofenclosingelement")

Howisitpossible?ThisisaspecialtraitoftheNothingclass,whichisactingasifitisasubtypeofallthepossibletypes:bothnullableandnot-nullable.ThisiswhyNothingisreferredasanemptytype,whichmeansthatnovaluecanhavethistypeatruntime,andit'salsoasubtypeofeveryotherclass.

TheconceptofuninhabitedtypeisnewintheworldofJava,andthisiswhyitmightbeconfusing.Theideaisactuallyprettysimple.TheNothinginstanceisneverexisting,whilethereisonlyanerrorthatmightbereturnedfromfunctionsthatarespecifyingitasareturntype.AndthereisnoneedforNothingaddedtosomethingtoinfluenceitstype.

Page 159: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

SummaryInthischapter,we'veseenhowtodefineandusefunctions.Welearnedhowfunctionscanbedefinedonthetop-levelorinsideotherfunctions.Therewasalsoadiscussionondifferentfeaturesconnectedtofunctions--varargparameters,defaultnames,andnamedargumentsyntax.Finally,wesawsomeKotlinspecialreturntypes:Unit,whichisequivalentofJavavoid,andNothing,whichisatypethatcannotbedefinedandmeansthatnothingcanbereturned(onlyexceptions).

Inthenextchapter,wearegoingtoseehowclassesaredefinedinKotlin.ClassesarealsospeciallysupportedbytheKotlinlanguage,andtherearelotsofimprovementsintroducedoverJavadefinitions.

Page 160: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ClassesandObjectsTheKotlinlanguageprovidesfullsupportforOOPprogramming.Wewillreviewpowerfulstructuresthatallowustosimplifydatamodeldefinitionandoperateonitinaneasyandflexibleway.We'lllearnhowKotlinsimplifiesandimprovesimplementationsofmanyconceptsknownfromJava.Wewilltakealookatdifferenttypeofclasses,properties,initializerblocks,andconstructors.Wewilllearnaboutoperatoroverloadingandinterfacedefaultimplementations.

Inthischapter,wewillcoverthefollowingtopics:

ClassdeclarationPropertiesPropertyaccesssyntaxConstructorsandinitializersblocksConstructorsInheritanceInterfacesDataclassesDestructivedeclarationsOperatoroverloadingObjectdeclarationObjectexpressionCompanionobjectsEnumclassesSealedclassesNestedclasses

Page 161: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ClassesClassesareafundamentalbuildingblockofOOP.Infact,KotlinclassesareverysimilartoJavaclasses.Kotlin,however,allowsmorefunctionalitytogetherwithsimplerandmuchmoreconcisesyntax.

Page 162: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ClassdeclarationClassesinKotlinaredefinedusingtheclasskeyword.Thefollowingisthesimplestclassdeclaration--anemptyclassnamedPerson:

classPerson

DefinitionofPersondoesnotcontainanybody.Still,itcanbeinstantiatedusingadefaultconstructor:

valperson=Person()

EvensuchasimpletaskasclassinstantiationissimplifiedinKotlin.UnlikeJava,Kotlindoesnotrequirethenewkeywordtocreateaclassinstance.DuetostrongKotlininteroperabilitywithJava,wecaninstantiateclassesdefinedinJavaandKotlinexactlythesameway(withoutthenewkeyword).Thesyntaxusedtoinstantiateaclassdependsontheactuallanguageusedtocreateclassinstance(KotlinorJava),notthelanguagetheclasswasdeclaredin:

//InstantiateKotlinclassinsideJavafile

Personperson=newPerson()

//InstantiateclassinsideKotlinfile

varperson=Person()

ItistheruleofthumbtousethenewkeywordinsideaJavafileandneverusethenewkeywordinsideaKotlinfile.

Page 163: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

PropertiesPropertyisjustacombinationofabackingfieldanditsaccessors.Itcouldbeabackingfieldwithbothagetterandasetterorabackingfieldwithonlyoneofthem.Propertiescanbedefinedatthetop-level(directlyinsidefile)orasamember(forexample,insideclass,interface,andsoon).

Ingeneral,itisadvisabletodefineproperties(privatefieldswithgetters/setters)insteadofaccessingpublicfieldsdirectly(AccordingtoEffectiveJava,byJoshuaBloch,book'sitem14:inpublicclasses,useaccessormethods,notpublicfields).

Javagetterandsetterconventionsforprivatefields

Getter:Aparameterlessmethodwithanamethatcorrespondstopropertynameandagetprefix(foraBooleanpropertytheremightbeanisprefixusedinstead)

Setter:Single-argumentmethodswithnamesstartingwithset:forexample,setResult(StringresultCode)

Kotlinguardsthisprinciplebylanguagedesign,becausethisapproachprovidesvariousencapsulationbenefits:

AbilitytochangeinternalimplementationwithoutchanginganexternalAPIEnforcesinvariants(callmethodsthatvalidateobjectsstate)Abilitytoperformadditionalactionswhenaccessingmember(forexample,logoperation)

Todefineatop-levelproperty,wesimplydefineitintheKotlinfile:

//Test.kt

valname:String

Let'simaginethatweneedaclasstostorebasicdataregardingaperson.ThisdatamaybedownloadedfromanexternalAPI(backend)orretrievedfroma

Page 164: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

localdatabase.Ourclasswillhavetodefinetwo(member)properties,nameandage.Let'slookattheJavaimplementationfirst:

publicclassPerson{

privateintage;

privateStringname;

publicPerson(Stringname,intage){

this.name=name;

this.age=age;

}

publicintgetAge(){

returnage;

}

publicvoidsetAge(intage){

this.age=age;

}

publicStringgetName(){

returnname;

}

publicvoidsetName(Stringname){

this.name=name;

}

}

Thisclasscontainsonlytwoproperties.SincewecanmaketheJavaIDEgenerateaccessorscodeforus,atleastwedon'thavetowritethecodebyourselves.However,theproblemwiththisapproachisthatwecannotgetalongwithouttheseautomaticallygeneratedchunks,andthatmakesthecodeveryverbose.We(developers)spendmostofourtimejustreadingthecode,notwritingit,soreadingredundantcodewastesalotofvaluabletime.Alsoasimpletasksuchasrefactoringthepropertynamebecomesalittlebittrickier,becausetheIDEmightnotupdateconstructorparameternames.

Fortunately,boilerplatecodecanbedecreasedsignificantlybyusingKotlin.Kotlinsolvesthisproblembyintroducingtheconceptofpropertiesthatisbuiltintothelanguage.Let'slookataKotlinequivalentoftheprecedingJavaclass:

classPerson{

varname:String

varage:Int

constructor(name:String,age:Int){

this.name=name

this.age=age

}

}

Page 165: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ThisisanexactequivalentoftheprecedingJavaclass:

TheconstructormethodisequivalentoftheJavaconstructorthatiscalledwhenanobjectinstanceiscreatedGettersandsettersaregeneratedbytheKotlincompiler

Wecanstilldefinecustomimplementationsofgettersandsetters.WewilldiscussthisinmoredetailintheCustomgetters/setterssection.

Alltheconstructorsthatwehavealreadydefinedarecalledsecondaryconstructors.Kotlinalsoprovidesalternative,veryconcisesyntaxfordefiningconstructors.Wecandefineaconstructor(withallparameters)aspartoftheclassheader.Thiskindofconstructoriscalledaprimaryconstructor.Let'smoveapropertydeclarationfromthesecondaryconstructorintotheprimaryconstructortomakeourcodealittlebitshorter:

classPersonconstructor(name:String,age:Int){

varname:String

varage:Int

init{

this.name=name

this.age=age

println("Personinstancecreated")

}

}

InKotlin,theprimaryconstructor,asopposedtothesecondaryconstructor,can'tcontainanycode,soallinitializationcodemustbeplacedinsidetheinitializerblock(init).Aninitializerblockwillbeexecutedduringclasscreation,sowecanassignconstructorparameterstofieldsinsideit.

Tosimplifycode,wecanremovetheinitializerblockandaccessconstructorparametersdirectlyinpropertyinitializers.Thisallowsustoassignconstructorparameterstoafield:

classPersonconstructor(name:String,age:Int){

varname:String=name

varage:Int=age

}

Wemanagedtomakethecodeshorter,butitstillcontainsalotofboilerplate,becausetypedeclarationsandpropertynamesareduplicated(constructorparameter,fieldassignment,andfielditself).Whenpropertiesdoesnothaveany

Page 166: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

customgettersorsetterswecandefinethemdirectlyinsideprimaryconstructorbyaddingvalorvarmodifier:

classPersonconstructor(varname:String,varage:Int)

Finally,iftheprimaryconstructordoesnothaveanyannotations(@Inject,andsoon)orvisibilitymodifiers(public,private,andsoon),thentheconstructorkeywordcanbeomitted:

classPerson(varname:String,varage:Int)

Whentheconstructortakesafewparameters,itisgoodpracticetodefineeachparameterinanewlinetoimprovecodereadabilityanddecreasechanceofpotentialmergeconflicts(whenmergingbranchesfromsourcecoderepository):

classPerson(

varname:String,

varage:Int

)

Summingup,theprecedingexampleisequivalentoftheJavaclasspresentedatthebeginningofthissection--bothpropertiesaredefineddirectlyintheclassprimaryconstructorandKotlincompilerdoesalltheworkforus--itgeneratesappropriatefieldsandaccessors(getters/setters).

Notethatthisnotationcontainsonlythemostimportantinformationaboutthisdatamodelclass--itsname,parameternames,types,andmutability(val/var)information.Implementationhasnearlyzeroboilerplate.Thismakestheclassveryeasytoread,understand,andmaintain.

Page 167: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Read-writeversusread-onlypropertyAllthepropertiesinthepreviousexamplesweredefinedasread-write(asetterandagetteraregenerated).Todefineread-onlypropertiesweneedtousethevalkeyword,soonlygetterwillbegenerated.Let'slookatasimpleexample:

classPerson(

varname:String,

//Read-writeproperty(generatedgetterandsetter)

valage:Int//Read-onlyproperty(generatedgetter)

)

\\usage

valperson=Person("Eva",25)

valname=person.name

person.name="Kate"

valage=person.age

person.age=28\\error:read-onlyproperty

Kotlindoesnotsupportwrite-onlyproperties(propertiesofwhichonlysetterisgenerated).

Keyword Read Write

var Yes Yes

val Yes No

(unsupported) No Yes

Page 168: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

PropertyaccesssyntaxbetweenKotlinandJavaAnotherbigimprovementintroducedbyKotlinisthewayofaccessingproperties.InJava,wewouldaccesspropertyusingthecorrespondingmethod(setSpeed/getSpeed).Kotlinpromotespropertyaccesssyntax,whichisamoreexpressivewayofaccessingproperties.Let'scomparebothapproaches,assumingwehaveasimpleCarclassthathasasinglespeedproperty:

classCar(varspeed:Double)

//Javaaccesspropertiesusingmethodaccesssyntax

Carcar=newCar(7.4)

car.setSpeed(9.2)

Doublespeed=car.getSpeed();

//Kotlinaccesspropertiesusingpropertyaccesssyntax

valcar:Car=Car(7.4)

car.speed=9.2

valspeed=car.speed

Aswecansee,inKotlinthereisnoneedtoaddget,setprefixesandparenthesestoaccessormodifyanobjectproperty.Usingpropertyaccesssyntaxallowsfordirectusageofincrement(++)anddecrement(--)operatorstogetherwithpropertyaccess:

valcar=Car(7.0)

println(car.speed)//prints7.0

car.speed++

println(car.speed)//prints8.0

car.speed--

car.speed--

println(car.speed)//prints:6.0

Page 169: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

IncrementanddecrementoperatorsTherearetwokindsofincrement(++)anddecrement(--)operators:pre-increment/pre-decrementwheretheoperatorisdefinedbeforetheexpression,andpost-increment/post-decrementwheretheoperatorisdefinedaftertheexpression:

++speed//preincrement

--speed//predecrement

speed++//postincrement

speed--//postdecrement

Intheprecedingexample,usingpost-versuspre-increment/decrementwouldchangenothingbecausethoseoperationsareexecutedinsequence.Butthismakesahugedifferencewhentheincrement/decrementoperatoriscombinedwithafunctioncall.

Inthepre-incrementoperator,speedisretrieved,incremented,andpassedtoafunctionasanargument:

varspeed=1.0

println(++speed)//Prints:2.0

println(speed)//Prints:2.0

Inpost-incrementoperatorspeedisretrieved,passedtoafunctionasanargument,andthenitisincremented,sotheoldvalueispassedtoafunction:

varspeed=1.0

println(speed++)//Prints:1.0

println(speed)//Prints:2.0

Thisworksinananalogicalwayforpre-decrementandpost-decrementoperators.

PropertyaccesssyntaxisnotlimitedonlytoclassesdefinedinKotlin.EachmethodthatfollowstheJavaconventionsforgettersandsettersisrepresentedasapropertyinKotlin.

ThismeansthatwecandefineaclassinJavaandaccessitspropertiesinKotlin

Page 170: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

usingpropertyaccesssyntax.Let'sdefineaJavaFishclasswithtwoproperties,sizeandisHungry,andlet'sinstantiatethisclassinKotlinandaccesstheproperties:

//Javaclassdeclaration

publicclassFish{

privateintsize;

privatebooleanhungry;

publicFish(intsize,booleanisHungry){

this.size=size;

this.hungry=isHungry;

}

publicintgetSize(){

returnsize;

}

publicvoidsetSize(intsize){

this.size=size;

}

publicbooleanisHungry(){

returnhungry;

}

publicvoidsetHungry(booleanhungry){

this.hungry=hungry;

}

}

//Kotlinclassusage

valfish=Fish(12,true)

fish.size=7

println(fish.size)//Prints:7

fish.isHungry=true

println(fish.isHungry)//Prints:true

Thisworksbothways,sowecandefinetheFishclassinKotlinusingveryconcisesyntaxandaccessitinausualJavaway,becausetheKotlincompilerwillgeneratealltherequiredgettersandsetters:

//Kotlinclassdeclaration

classFish(varsize:Int,varhungry:Boolean)

//classusageinJava

Fishfish=newFish(12,true);

fish.setSize(7);

System.out.println(fish.getSize());

fish.setHungry(false);

System.out.println(fish.getHungry());

Aswecansee,syntaxusedtoaccesstheclasspropertydependsontheactuallanguagethattheclassuses,notthelanguagethattheclasswasdeclaredin.ThisallowsformoreidiomaticusageofmanyclassesdefinedintheAndroid

Page 171: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

framework.Let'sseesomeexamples:

Javamethodaccesssyntax Kotlinpropertyaccesssyntax

activity.getFragmentManager() activity.fragmentManager

view.setVisibility(Visibility.GONE) view.visibility=Visibility.GONE

context.getResources().getDisplayMetrics().density context.resources.displayMetrics.density

PropertyaccesssyntaxresultsinmoreconcisecodethatdecreasestheoriginalJavalanguagecomplexity.NoticethatitisstillpossibletousemethodaccesssyntaxwithKotlinalthoughpropertyaccesssyntaxisoftenthebetteralternative.

TherearesomemethodsintheAndroidframeworkthatusetheisprefixfortheirname;inthiscasesBooleanpropertiesalsohavetheisprefix:

classMainActivity:AppCompatActivity(){

overridefunonDestroy(){//1

super.onDestroy()

isFinishing()//methodaccesssyntax

isFinishing//propertyaccesssyntax

finishing//error

}

}

1. Kotlinmarksoverriddenmembersusingtheoverridemodifier,not@OverrideannotationlikeJava.

Althoughusingfinishingwouldbethemostnaturalandconsistentapproach,it'simpossibletouseitbydefaultduetopotentialconflicts.

Anothercasewherewecan'tusethepropertyaccesssyntaxiswhenthepropertydefinesonlysetterwithoutgetter,becauseKotlindoesnotsupportwrite-onlyproperties,asinthisexample:

Page 172: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

fragment.setHasOptionsMenu(true)

fragment.hasOptionsMenu=true//Error!

Page 173: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Customgetters/settersSometimeswewanttohavemorecontrolaboutpropertyusage.Wemaywanttoperformotherauxiliaryoperationswhenusingproperty;forexample,verifyavaluebeforeit'sassignedtoafield,logthewholeoperation,orinvalidateaninstancestate.Wecandoitbyspecifyingcustomsettersand/orgetters.Let'saddtheecoRatingpropertytoourFruitclass.Inmostcases,wewouldaddthispropertytotheclassdeclarationheaderlikethis:

classFruit(varweight:Double,

valfresh:Boolean,

valecoRating:Int)

Ifwewanttodefinecustomgettersandsetters,weneedtodefineapropertyintheclassbodyinsteadoftheclassdeclarationheader.Let'smovetheecoRatingpropertyintotheclassbody:

classFruit(varweight:Double,valfresh:Boolean,ecoRating:Int)

{

varecoRating:Int=ecoRating

}

Whenthepropertyisdefinedinsidethebodyofaclass,wehavetoinitializeitwithvalue(evennullablepropertiesneedtobeinitializedwithanullvalue).Wecanprovidethedefaultvalueinsteadoffillingapropertywiththeconstructorargument:

classFruit(varweight:Double,valfresh:Boolean){

varecoRating:Int=3

}

Wecanalsocomputedefaultvaluesbasedonsomeotherproperties:

classApple(varweight:Double,valfresh:Boolean){

varecoRating:Int=when(weight){

in0.5..2.0->5

in0.4..0.5->4

in0.3..0.4->3

in0.2..0.3->2

else->1

}

}

Differentvalueswillbesetfordifferentweightconstructorarguments.

Page 174: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Whenapropertyisdefinedinaclassbody,thetypedeclarationcanbeomitted,becauseitcanbeinferredfromthecontext:

classFruit(varweight:Double){

varecoRating=3

}

Let'sdefineacustomgetterandsetterwiththedefaultbehaviorthatwillbetheequivalentoftheprecedingproperty:

classFruit(varweight:Double){

varecoRating:Int=3

get(){

println("gettervalueretrieved")

returnfield

}

set(value){

field=if(value<0)0elsevalue

println("setternewvalueassigned$field")

}

}

//Usage

valfruit=Fruit(12.0)

valecoRating=fruit.ecoRating

//Prints:gettervalueretrieved

fruit.ecoRating=3;

//Prints:setternewvalueassigned3

fruit.ecoRating=-5;

//Prints:setternewvalueassigned0

Insidethegetandsetblock,wecanhaveaccesstoaspecialvariablecalledfield,whichreferstothecorrespondingbackingfieldoftheproperty.NoticethattheKotlinpropertydeclarationiscloselypositionedtoacustomgetter/setter.ThiscontradictswithJavaandsolvestheissuewherethefielddeclarationisusuallyatthetopofthefilecontainingclassandcorrespondinggetter/setterisatthebottomofthisfile,sowecan'treallyseethemonasinglescreenandthuscodeismoredifficulttoread.Apartfromthatlocation,KotlinpropertybehaviorisquitesimilartoJava.Eachtimeweretrieve,valuefromtheecoRatingproperty,agetblockwillbeexecuted,andeachtimeweassignanewvaluetotheecoRatingproperty,asetblockwillbeexecuted.

Thisisaread-writeproperty(var),soitmaycontainbothcorrespondinggettersandsetters.Incaseweexplicitlydefineonlyoneofthem,thedefaultimplementationwillbeusedforanother.

Tomakeavaluecomputedeachtimewhenapropertyvalueisretrieved,weneedtoexplicitlydefinegetter:

Page 175: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

classFruit(varweight:Double){

valheavy//1

get()=weight>20

}

//usage

varfruit=Fruit(7.0)

println(fruit.heavy)//prints:false

fruit.weight=30.5

println(fruit.heavy)//prints:true

1. SinceKotlin1.1typecanbeomitted(itistobeinferred).

Page 176: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ThegetterversuspropertydefaultvalueIntheprecedingexample,weusedgetter,sothepropertyvalueiscalculatedeachtimethevalueisretrieved.Byomittinggetterwecancreateadefaultvaluefortheproperty.Thisvaluewillbecomputedonlyonceduringclasscreationanditwillneverchange(changingtheweightpropertywillhavenoeffectontheisHeavypropertyvalue):

classFruit(varweight:Double){

valisHeavy=weight>20

}

varfruit=Fruit(7.0)

println(fruit.isHeavy)//Prints:false

fruit.weight=30.5

println(fruit.isHeavy)//Prints:false

Thistypeofpropertydoeshaveabackingfield,becauseitsvalueisalwayscomputedduringobjectcreation.Wecanalsocreateread-writepropertieswithoutabackingfield:

classCar{

varusable:Boolean=true

varinGoodState:Boolean=true

varcrashed:Boolean

get()=!usable&&!inGoodState

set(value){

usable=false

inGoodState=false

}

}

Thistypeofpropertydoesnothaveabackingfield,becauseitsvalueisalwayscomputedusinganotherproperty.

Page 177: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Late-initializedpropertiesSometimesweknowthatapropertywon'tbenull,butitwon'tbeinitializedwiththevalueatdeclarationtime.Let'slookatcommonAndroidexamples--retrievingreferencetoalayoutelement:

classMainActivity:AppCompatActivity(){

privatevarbutton:Button?=null

overridefunonCreate(savedInstanceState:Bundle?){

super.onCreate(savedInstanceState)

button=findViewById(R.id.button)asButton

}

}

Thebuttonvariablecan'tbeinitializedatdeclarationtime,becausetheMainActivitylayoutisnotyetinitialized.WecanretrievereferencetothebuttondefinedinthelayoutinsidetheonCreatemethod,buttodoitweneedtodeclareavariableasnullable(Button?).

Suchanapproachseemsquiteimpractical,becauseaftertheonCreatemethodiscalledabuttoninstanceisavailableallthetime.However,theclientstillneedstousethesafecalloperatororothernullitycheckstoaccessit.

Toavoidnullitycheckswhenaccessingaproperty,weneedawaytoinformtheKotlincompilerthatthisvariablewillbefilledbeforeusage,butitsinitializationwillbedelayedintime.Todothis,wecanusethelateinitmodifier:

classMainActivity:AppCompatActivity(){

privatelateinitvarbutton:Button

overridefunonCreate(savedInstanceState:Bundle?){

button=findViewById(R.id.button)asButton

button.text="ClickMe"

}

}

Now,withthepropertymarkedaslateinit,wecanaccessourapplicationinstancewithoutperformingnullitychecks.

Thelateinitmodifiertellsthecompilerthatthispropertyisnon-nullable,butits

Page 178: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

initializationisdelayedintime.Naturally,whenwetrytoaccessthepropertybeforeitisinitialized,theapplicationwillthrowUninitializedPropertyAccessException.Thisisfine,becauseweassumethatthisscenarioshouldnothappen.

Ascenariowhereavariableinitializationisnotpossibleatdeclarationtimeisquitecommonanditisnotalwaysrelatedtoviews.PropertiescanbeinitializedthroughDependencyInjection,orviathesetupmethodofaunittest.Insuchscenarios,wecannotsupplyanon-nullablevalueintheconstructor,butwestillwanttoavoidnullitychecks.

Thelateinitpropertyandframeworks

ThelateinitpropertyisalsohelpfulwhenapropertyisinjectedbytheDependencyInjectionframework.ThepopularAndroidDependencyInjectionframework,Dagger,usesthe@Injectannotationtomarkpropertiesthatneedtobeinjected:

@InjectlateinitvarlocationManager:LocationManager

Weknowthatthepropertywillneverbenull(becauseitwillbeinjected),buttheKotlincompilerdoesnotunderstandthisannotation.

Similarscenarioshappenwiththepopularframework,Mockito:

@MocklateinitvarmockEventBus:EventBus

Thevariablewillbemocked,butitwillhappensometimelater,aftertestclasscreation.

Page 179: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

AnnotatingpropertiesKotlingeneratesmultipleJVMbytecodeelementsfromasingleproperty(privatefield,getter,setter).Sometimestheframeworkannotationprocessororthereflection-basedlibraryrequiresaparticularelementtobedefinedasapublicfield.AgoodexampleofsuchbehavioristheJUnittestframework.Itrequiresrulestobeprovidedthroughatestclassfieldoragettermethod.WemayencounterthisproblemwhendefiningActivityTestRuleorMockito's(mockingframeworkforunittests)Ruleannotation:

@Rule

valactivityRule=ActivityTestRule(MainActivity::class.Java)

TheprecedingcodeannotatestheKotlinpropertythatJUnitwon'trecognize,soActivityTestRulecan'tbeproperlyinitialized.TheJUnitannotationprocessorexpectstheRuleannotationonthefieldorgetter.Thereareafewwaystosolvethisproblem.WecanexposetheKotlinpropertyasaJavafieldbyannotatingitwiththe@JvmFieldannotation:

@JvmField@Rule

valactivityRule=ActivityTestRule(MainActivity::class.Java)

Thefieldwillhavethesamevisibilityastheunderlyingproperty.Thereareafewlimitationsregarding@JvmFieldannotationusage.Wecanannotateapropertywith@JvmFieldifithasabackingfield,itisnotprivate,doesnothaveopen,override,orconstmodifiers,andisnotadelegatedproperty.

Wecanalsoannotategetterbyaddinganannotationdirectlytogetter:

valactivityRule

@Ruleget()=ActivityTestRule(MainActivity::class.java)

Ifwedon'twanttodefinegetter,wecanstilladdanannotationtogetterusingtheuse-sitetarget(get).Bydoingso,wesimplyspecifywhichelementgeneratedbytheKotlincompilerwillbeannotated:

@get:Rule

valactivityRule=ActivityTestRule(MainActivity::class.Java)

Page 180: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

InlinepropertiesWecanoptimizepropertycallsbyusingtheinlinemodifier.Duringcompilationeachpropertycallwillbeoptimized.Insteadofreallycallingaproperty,thecallwillbereplacedwiththepropertybody:

inlinevalnow:Long

get(){

println("Timeretrieved")

returnSystem.currentTimeMillis()

}

Withinlineproperty,weareusingtheinlinemodifier.Theprecedingcodewillbecompiledto:

println("Timeretrieved")

System.currentTimeMillis()

Inliningimprovesperformance,becausethereisnoneedtocreateadditionalobjects.Nogetterwillbeinvoked,becausethebodywouldreplacethepropertyusage.Inlininghasonelimitation--itcanbeonlyappliedtopropertiesthatdonothaveabackingfield.

Page 181: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ConstructorsKotlinallowsustodefineclasseswithoutanyconstructors.Wecanalsodefineaprimaryconstructorandoneormoresecondaryconstructors:

classFruit(valweight:Int){

constructor(weight:Int,fresh:Boolean):this(weight){}

}

//classinstantiation

valfruit1=Fruit(10)

valfruit2=Fruit(10,true)

Declaringpropertiesisnotallowedforsecondaryconstructors.Ifweneedapropertythatisinitializedbysecondaryconstructors,wemustdeclareitintheclassbody,andwecaninitializeitinthesecondaryconstructorbody.Let'sdefinethefreshproperty:

classTest(valweight:Int){

varfresh:Boolean?=null

//definefreshpropertyinclassbody

constructor(weight:Int,fresh:Boolean):this(weight){

this.fresh=fresh

//assignconstructorparametertofreshproperty

}

}

Noticethatwedefinedourfreshpropertyasnullable,becausewhenaninstanceoftheobjectwillbecreatedusingaprimaryconstructorthefreshpropertywillbenull:

valfruit=Fruit(10)

println(fruit.weight)//prints:10

println(fruit.fresh)//prints:null

Wecanalsoassignthedefaultvaluetothefreshpropertytomakeitnon-nullable:

classFruit(valweight:Int){

varfresh:Boolean=true

constructor(weight:Int,fresh:Boolean):this(weight){

this.fresh=fresh

}

}

valfruit=Fruit(10)

println(fruit.weight)//prints:10

Page 182: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

println(fruit.fresh)//prints:true

Whenaprimaryconstructorisdefined,everysecondaryconstructormustcalltheprimaryconstructorimplicitlyorexplicitly.Animplicitcallmeansthatwecalltheprimaryconstructordirectly.Anexplicitcallmeansthatwecallanothersecondaryconstructorthatcallsprimaryconstructor.Tocallanotherconstructor,weusethethiskeyword:

classFruit(valweight:Int){

constructor(weight:Int,fresh:Boolean):this(weight)//1

constructor(weight:Int,fresh:Boolean,color:String):

this(weight,fresh)//2

}

1. Calltoprimaryconstructor2. Calltosecondaryconstructor

Iftheclasshasnoprimaryconstructorandthesuperclasshasanon-emptyconstructor,theneachsecondaryconstructorhastoinitializethebaseclassusingthesuperkeywordorcallanotherconstructorthatdoesthat:

classProductView:View{

constructor(ctx:Context):super(ctx)

constructor(ctx:Context,attrs:AttributeSet):

super(ctx,attrs)

}

Aviewexamplecanbegreatlysimplifiedbyusingthe@JvmOverloadsannotationthatwillbedescribedinthe@JvmOverloadssection.

Bydefault,thisgeneratedconstructorwillbepublic.Ifwewanttopreventthegenerationofsuchanimplicitpublicconstructor,wehavetodeclareanemptyprimaryconstructorwithaprivateorprotectedvisibilitymodifier:

classFruitprivateconstructor()

Tochangetheconstructorvisibility,weneedtoexplicitlyusetheconstructorkeywordintheclassdefinitionheader.Theconstructorkeywordisalsorequiredwhenwewanttoannotateaconstructor.AcommonexampleistoannotateaclassconstructorusingtheDagger(DependencyInjectionframework)@Injectannotation:

classFruit@Injectconstructor()

Page 183: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Boththevisibilitymodifierandannotationcanbeappliedatthesametime:

classFruit@Injectprivateconstructor{

varweight:Int?=null

}

Page 184: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

PropertyversusconstructorparameterTheimportantthingtonoticeisthefactthatifweremovethevar/valkeywordfromconstructorpropertydeclaration,we'llendupwithaconstructorparameterdeclaration.Thismeansthatthepropertywillbechangedintoconstructorparameter,sonoaccessorswillbegeneratedandwewillnotbeabletoaccessthepropertyontheclassinstance:

classFruit(varweight:Double,fresh:Boolean)

valfruit=Fruit(12.0,true)

println(fruit.weight)

println(fruit.fresh)//error

Intheprecedingexample,wehaveanerrorbecausefreshismissingavalorvarkeyword,soitisaconstructorparameter,notaclasspropertysuchasweight.Thefollowingtablesummarizesthecompileraccessorgeneration:

Classdeclaration Gettergenerated

Settergenerated Type

classFruit(name:String) No No Constructorparameter

classFruit(val

name:String) Yes No Property

classFruit(var

name:String) Yes Yes Property

Sometimeswemaywonderwhenweshoulduseapropertyandwhenweshoulduseamethod.Agoodguidelinetofollowistousepropertyinsteadofmethodwhen:

Page 185: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ItdoesnotthrowanexceptionItischeaptocalculate(orcachedonthefirstrun)Itreturnsthesameresultovermultipleinvocations

Page 186: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ConstructorwithdefaultargumentsSincetheearlydaysofJava,therewasaseriousflawwithobjectcreation.Itisdifficulttocreateanobjectinstancewhenanobjectrequiresmultipleparametersandsomeofthoseparametersareoptional.Thereareafewwaystosolvethisproblem,suchas,theTelescopingconstructorpattern,theJavaBeanspattern,andeventheBuilderpattern.Eachofthemhavetheirprosandcons.

Page 187: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

PatternsThepatternssolvetheissueofobjectcreation.Eachofthemisexplainedasfollows:

Telescopingconstructorpattern:Classwithalistofconstructorswhereeachoneaddsanewparameter.Nowadaysit'sconsideredananti-pattern,butAndroidframeworkstillusesitinafewplaces;forexample,theandroid.view.Viewclass:

valview1=View(context)

valview1=View(context,attributeSet)

valview1=View(context,attributeSet,defStyleAttr)

JavaBeanspattern:Parameterlessconstructorplusoneormoresettersmethodstoconfigureobjects.Themainproblemwiththispatternisthatwecan'tsaywhetherornotalltherequiredmethodshavebeencalledonanobject,soitmaybeonlypartiallyconstructed:

valanimal=Animal()

fruit.setWeight(10)

fruit.setSpeed(7.4)

fruit.setColor("Gray")

Builderpattern:Usesanotherobject,abuilder,thatreceivesinitializationargumentsstepbystepandthenreturnstheresultingconstructedobjectatoncewhenthebuildmethodiscalled;forexample,android.app.Notification.Builder,orandroid.app.AlertDialog.Builder:

Retrofitretrofit=newRetrofit.Builder()

.baseUrl("https://api.github.com/")

.build();

Foralongtime,builderwasmostwidelyused,butacombinationofdefaultargumentsandnamedargumentsyntaxisanevenmoreconciseoption.Let'sdefinesomedefaultvalue:

classFruit(weight:Int=0,fresh:Boolean=true,color:

String="Green")

Bydefiningdefaultparametervalues,wecancreateobjectsinmultipleways,

Page 188: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

withoutaneedtopassallarguments:

valfruit=Fruit(7.4,false)

println(fruit.fresh)//prints:false

valfruit2=Fruit(7.4)

println(fruit.fresh)//prints:true

Usingargumentssyntaxwithdefaultparametersgivesusmuchmoreflexibilityinobjectscreation.Wecanpassonlyrequiredparametersinanyorderthatwewantwithoutdefiningmultiplemethodsandconstructors,asinthefollowingexample:

valfruit1=Fruit(weight=7.4,fresh=true,color="Yellow")

valfruit2=Fruit(color="Yellow")

Page 189: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

InheritanceAswealreadyknow,asupertypeofallKotlintypesisAny.ItistheequivalentoftheJavaObjecttype.EachKotlinclassexplicitlyorimplicitlyextendstheAnyclass.Ifwedonotspecifytheparentclass,theAnywillbeusedimplicitlysetasparentfortheclass:

classPlant//ImplicitlyextendsAny

classPlant:Any//ExplicitlyextendsAny

Kotlin,likeJava,promotessingleinheritance,soaclasscanhaveonlyoneparentclass,butitcanimplementmultipleinterfaces.

IncontrasttoJava,everyclassandeverymethodinKotlinisfinalbydefault.ThisplaysalongwiththeEffectiveJavaItem17:Designanddocumentforinheritanceorelseprohibititrule.Thisisusedtopreventunexpectedbehaviorfromasubclassaltering.Modificationofabaseclasscancausetheincorrectbehaviorofsubclasses,becausethechangedcodeofthebaseclassnolongermatchestheassumptionsinitssubclasses.

Thismeansthataclasscannotbeextendedandamethodcannotbeoverriddenuntilit'sexplicitlydeclaredasopenusingtheopenkeyword.ThisistheexactoppositeoftheJavafinalkeyword.

Let'ssaywewanttodeclareabaseclassPlantandsubclassTree:

classPlant

classTree:Plant()//Error

Theprecedingcodewillnotcompile,becausetheclassPlantisfinalbydefault.Let'smakeitopen:

openclassPlant

classTree:Plant()

NoticethatwedefineinheritanceinKotlinsimplybyusingthecoloncharacter(:).ThereisnoextendsorimplementskeywordsknownfromJava.

Nowlet'saddsomemethodsandpropertiestoourPlantclass,andtrytooverride

Page 190: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

itintheTreeclass:

openclassPlant{

varheight:Int=0

fungrow(height:Int){}

}

classTree:Plant(){

overridefungrow(height:Int){//Error

this.height+=height

}

}

Thiscodewillalsonotcompile.Wehavesaidalreadythatallmethodsarealsoclosedbydefault,soeachmethodwewanttooverridemustbeexplicitlymarkedasopen.Let'sfixthecodebymarkingthegrowmethodasopen:

openclassPlant{

varheight:Int=0

openfungrow(height:Int){}

}

classTree:Plant(){

overridefungrow(height:Int){

this.height+=height

}

}

Inasimilarway,wecouldopenandoverridetheheightproperty:

openclassPlant{

openvarheight:Int=0

openfungrow(height:Int){}

}

classTree:Plant(){

overridevarheight:Int=super.height

get()=super.height

set(value){field=value}

overridefungrow(height:Int){

this.height+=height

}

}

Toquicklyoverrideanymember,gotoaclasswhereamemberisdeclared,addtheopenmodifier,andthengotoaclasswherewewanttooverridemember,runtheoverridemembers(theshortcutforWindowsisCtrl+O,andformacOS,itisCommand+O)action,andselectallthemembersyouwanttooverride.ThiswayalltherequiredcodewillbegeneratedbyAndroidStudio.

Let'sassumethatalltreesgrowinthesameway(thesamecomputationof

Page 191: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

growingalgorithmisapplicableforalltrees).WewanttoallowcreatingnewsubclassesoftheTreeclasstohavemorecontrolovertrees,butatthesametimewewanttopreserveourgrowingalgorithm--notallowinganysubclassesoftheTreeclasstooverridethisbehavior.Toachievethis,weneedtoexplicitlymarkthegrowmethodintheTreeclassasfinal:

openclassPlant{

varheight:Int=0

openfungrow(height:Int){}

}

classTree:Plant(){

finaloverridefungrow(height:Int){

this.height+=height

}

}

classOak:Tree(){

//1

}

1. It'snotpossibletooverridegrowmethodherebecauseit'sfinal

Let'ssumupallthisopenandfinalbehavior.Tomakeamethodoverridableinasubclass,weneededtoexplicitlymarkitasopeninthesuperclass.Tomakesurethatoverriddenmethodwillnotbeoverriddenagainbyanysubclass,weneedtomarkitasfinal.

Intheprecedingexample,thegrowmethodinthePlantclassdoesnotreallyprovideanyfunctionality(ithasanemptybody).Thisisasignthatmaybewedon'twanttoinstantiatethePlantclassatall,buttreatitasabaseclassandonlyinstantiatevariousclassessuchasTreethatextendsthePlantclass.WeshouldmarkthePlantclassasabstracttodisallowitsinstantiation:

abstractclassPlant{

varheight:Int=0

abstractfungrow(height:Int)

}

classTree:Plant(){

overridefungrow(height:Int){

this.height+=height

}

}

valplant=Plant()

//error:abstractclasscan'tbeinstantiated

valtree=Tree()

Page 192: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Markingtheclassasabstractwillalsomakethemethodclassopenbydefault,sowedon'thavetoexplicitlymarkeachmemberasopen.Noticethatwhenwearedefiningthegrowmethodasabstract,wehavetoremoveitsbody,becausetheabstractmethodcan'thaveabody.

Page 193: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

TheJvmOverloadsannotationSomeclassesintheAndroidplatformuseTelescopingconstructors,whichisconsideredasananti-pattern.Agoodexampleofsuchaclassistheandroid.view.Viewclass.Theremaybeacasewhenonlyasingleconstructorisused(inflatingthecustomviewfromKotlincode),butitismuchsafertooverrideallthreeconstructorswhenthesubclassingsubclassandroid.view.View,becausetheclasswillworkcorrectlyinallscenarios.Normallyourcustomviewclasswouldlooklikethis:

classCustomView:View{

constructor(context:Context?):this(context,null)

constructor(context:Context?,attrs:AttributeSet?):

this(context,attrs,0)

constructor(context:Context?,attrs:AttributeSet?,defStyleAttr:Int):super(context,attrs,defStyleAttr){

//...

}

}

Thiscaseintroducesalotofboilerplatecodejustforconstructorsthatdelegatecallstootherconstructors.Kotlin'ssolutiontothisproblemistousethe@JvmOverloadannotation:

classKotlinView@JvmOverloadsconstructor(

context:Context,

attrs:AttributeSet?=null,

defStyleAttr:Int=0

):View(context,attrs,defStyleAttr)

Annotatingaconstructorwiththe@JvmOverloadannotationinformsthecompilertogenerateinJVMbytecodeadditionalconstructoroverloadforeveryparameterwithadefaultvalue.Inthiscase,alltherequiredconstructorswillbegenerated:

publicSampleView(Contextcontext){

super(context);

}

publicSampleView(Contextcontext,@NullableAttributeSetattrs){

super(context,attrs);

}

publicSampleView(Contextcontext,@NullableAttributeSetattrs,intdefStyleAttr){

super(context,attrs,defStyleAttr);

}

Page 194: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

InterfacesKotlininterfacesaresimilartoJava8interfacesandincontrasttointerfacesfrompreviousJavaversions.Aninterfaceisdefinedusingtheinterfacekeyword.Let'sdefineanEmailProviderinterface:

interfaceEmailProvider{

funvalidateEmail()

}

ToimplementtheprecedinginterfaceinKotlin,usethesamesyntaxasforextendingclasses--asinglecoloncharacter(:).ThereisnoimplementskeywordlikeinJava:

classUser:EmailProvider{

overridefunvalidateEmail(){

//emailvalidation

}

}

Thequestionmayariseofhowtoextendaclassandimplementaninterfaceatthesametime.Simplyplacetheclassnameafterthecolon,andusecommacharactertoaddoneormoreinterfaces.It'snotrequiredtoplacethesuperclassatthefirstpositionalthoughit'sconsideredgoodpractice:

openclassPerson{

interfaceEmailProvider{

funvalidateEmail()

}

classUser:Person(),EmailProvider{

overridefunvalidateEmail(){

//emailvalidation

}

}

AswithJava,theKotlinclasscanextendonlyoneclass,butitcanimplementoneormoreinterfaces.Wecanalsodeclarepropertiesintheinterfaces:

interfaceEmailProvider{

valemail:String

funvalidateEmail()

}

Allmethodsandpropertieshavetobeoverriddeninaclassimplementing

Page 195: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

interface:

classUser():EmailProvider{

overridevalemail:String="UserEmailProvider"

overridefunvalidateEmail(){

//emailvalidation

}

}

Also,propertiesdefinedinaprimaryconstructorcanbeusedtooverrideparametersfromaninterface:

classUser(overridevalemail:String):EmailProvider{

overridefunvalidateEmail(){

//emailvalidation

}

}

Allmethodsandpropertiesdefinedinaninterfacethatdoesnothavedefaultimplementationaretreatedbydefaultasabstract,sowedon'thavetoexplicitlydefinethemasabstract.Allabstractmethodsandpropertiesmustbeimplemented(overridden)byaconcrete(non-abstract)classthatimplementsaninterface.

Thereis,however,anotherwaytodefinemethodsandpropertiesintheinterface.Kotlin,similartoJava8,introducesmajorimprovementtointerfaces.Aninterfacecannotonlydefinebehavior,butalsoimplementsit.Thismeansthatthedefaultmethodofpropertyimplementationcanbeprovidedbyaninterface.Theonlylimitationisthataninterfacecannotreferenceanybackingfields--storeastate(becausethereisnogoodplacetostoreit).Thisisadifferingfactorbetweeninterfaceandabstractclass.Interfacesarestateless(theycan'thaveastate),whileabstractclassesarestateful(theycanhaveastate).Let'sseeanexample:

interfaceEmailProvider{

funvalidateEmail():Boolean

valemail:String

valnickname:String

get()=email.substringBefore("@")

}

classUser(overridevalemail:String):EmailProvider{

overridefunvalidateEmail(){

//emailvalidation

}

Page 196: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

}

TheEmailProviderinterfaceprovidesthedefaultimplementationforthenicknameproperty,sowedon'thavetodefineitintheUserclass,andwecanstillusethepropertyasanyotherpropertydefinedintheclass:

valuser=User("[email protected]")

print(user.nickname)//prints:johnny

Thesameappliesformethods.Simplydefineamethodwiththebodyintheinterface,sotheUserclasswilltakealldefaultimplementationfromtheinterface,andwillhavetooverrideonlytheemailmember--theonlymemberintheinferencewithoutdefaultimplementation:

interfaceEmailProvider{

valemail:String

valnickname:String

get()=email.substringBefore("@")

funvalidateEmail()=nickname.isNotEmpty()

}

classUser(overridevalemail:String):EmailProvider

//usage

valuser=User("[email protected]")

print(user.validateEmail())//Prints:true

print(user.nickname)//Prints:joey

Thereisoneinterestingcaserelatedtodefaultimplementations.Aclasscan'tinheritfrommultipleclasses,butitcanimplementmultipleinterfaces.Wecanhavetwointerfacescontainingmethodswiththesamesignatureanddefaultimplementations:

interfaceA{

funfoo(){

println("A")

}

}

interfaceB{

funfoo(){

println("B")

}

}

Insuchcases,conflictmustberesolvedexplicitlybyoverridingthefoomethodinaclassimplementingtheinterfaces:

Page 197: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

classItem:A,B{

overridefunfoo(){

println("Item")

}

}

//usage

valitem=Item()

item.foo()//prints:Item

Wecanstillcallbothdefaultinterfaceimplementationsbyqualifyingsuperusinganglebracketsandspecifyingtheparentinterfacetypename:

classItem:A,B{

overridefunfoo(){

vala=super<A>.foo()

valb=super<B>.foo()

print("Item$a$b")

}

}

//usage

valitem=Item()

item.foo()

//Prints:A

B

ItemsAB

Page 198: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

DataclassesOftenwecreateaclasswhoseonlypurposeistostoredata;forexample,dataretrievedfromaserverorlocaldatabase.Thoseclassesarebuildingblocksofapplicationdatamodels:

classProduct(varname:String?,varprice:Double?){

overridefunhashCode():Int{

varresult=if(name!=null)name!!.hashCode()else0

result=31*result+if(price!=null)price!!.hashCode()

else0

returnresult

}

overridefunequals(other:Any?):Boolean=when{

this===other->true

other==null||other!isProduct->false

if(name!=null)name!=other.nameelseother.name!=

null->false

price!=null->price==other.price

else->other.price==null

}

overridefuntoString():String{

return"Product(name=$name,price=$price)"

}

}

InJava,weneedtogeneratealotofredundantgetters/setterstogetherwithhashCodeandequalsmethods.AndroidStudiocangeneratemostofthecodeforus,butmaintainingthiscodeisstillanissue.InKotlin,wecandefineaspecialkindofclasscalledthedataclassbyaddingthedatakeywordtoaclassdeclarationheader:

classProduct(varname:String,varprice:Double)

//normalclass

dataclassProduct(varname:String,varprice:Double)

//dataclass

AdataclassaddsadditionalcapabilitiestoaclassintheformofmethodsgeneratedbytheKotlincompiler.Thosemethodsareequals,hashCode,toString,copy,andmultiplecomponentNmethods.Thelimitationisthatdataclassescan'tbemarkedasabstract,inner,andsealed.Let'sdiscussmethodsaddedbyadatamodifierinmoredetail.

Page 199: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

TheequalsandhashCodemethodWhendealingwithdataclasses,thereisoftenaneedtocomparetwoinstancesforstructuralequality(thattheycontainthesamedata,butnotnecessarilyarethesameinstance).WemanysimplywanttocheckifoneinstanceoftheUserclassequalsanotherUserinstanceoriftwoproductinstancesrepresentthesameproduct.AcommonpatternusedtocheckifobjectsareequalistouseanequalsmethodthatusesthehashCodemethodinternally:

product.equals(product2)

ThegeneralcontractforoverriddenimplementationsofhashCodeisthattwoequalobjects(accordingtoequalsimplementation)needtohavethesamehashcode.ThereasonbehinditisthathashCodeisoftencomparedbeforeequals,becauseofitsperformance--it'smuchcheapertocomparehashcodethaneveryfieldintheobject.

IfhashCodeisthesamethentheequalsmethodchecksiftwoobjectsarethesameinstance,thesametype,andthenverifiesequalitybycomparingallsignificantfields.Ifatleastoneofthefieldsofthefirstobjectisnotequaltoacorrespondingfieldofasecondobjectthentheobjectsarenotconsideredasequal.Anotherwayaround--twoobjectsareequalwhentheyhavethesamehashCodeandallsignificant(compared)fieldshavethesamevalue.Let'scheckanexampleoftheJavaproductclasscontainingtwofields,nameandprice:

publicclassProduct{

privateStringname;

privateDoubleprice;

publicProduct(Stringname,Doubleprice){

this.name=name;

this.price=price;

}

@Override

publicinthashCode(){

intresult=name!=null?name.hashCode():0;

result=31*result+(price!=null?

price.hashCode():0);

returnresult;

}

@Override

Page 200: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

publicbooleanequals(Objecto){

if(this==o){

returntrue;

}

if(o==null||getClass()!=o.getClass()){

returnfalse;

}

Productproduct=(Product)o;

if(name!=null?!name.equals(product.name):

product.name!=null){

returnfalse;

}

returnprice!=null?price.equals(product.price):

product.price==null;

}

publicStringgetName(){

returnname;

}

publicvoidsetName(Stringname){

this.name=name;

}

publicDoublegetPrice(){

returnprice;

}

publicvoidsetPrice(Doubleprice){

this.price=price;

}

}

ThisapproachiswidelyusedinJavaandotherOOPprogramminglanguages.Intheearlydays,programmershadtowritethiscodemanuallyforeveryclassthatneededtobecomparedandmaintainedthecodemakingsurethatitwascorrectanditcompareseverysignificantvalue.

Nowadays,modernIDEssuchasAndroidStudiocangeneratethiscodeandupdatetheappropriatemethods.Wedon'thavetowritethecode,butwestillhavetomaintainitbymakingsurethatalltherequiredfieldsarecomparedbytheequalsmethod.Sometimeswedon'tknowifitisastandardcodegeneratedbytheIDEoritisatweakedversion.ForeachKotlindataclass,thosemethodsareautomaticallygeneratedbyacompiler,sothisproblemdoesnotexist.HereisadefinitionofProductinKotlin,whichcontainsallmethodsdefinedinthepreviousJavaclasses:

dataclassProduct(varname:String,varprice:Double)

TheprecedingclasscontainsallmethodsdefinedinpreviousJavaclasses,but

Page 201: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

thereisnomassiveboilerplatecodetomaintain.

InChapter2,LayingaFoundation,wementionedthat,inKotlin,usingthestructuralequalityoperator(==)willalwayscalltheequalsmethodunderthehood,soitmeansthatwecaneasilyandsafelycompareinstancesofourProductdataclass:

dataclassProduct(varname:String,varprice:Double)

valproductA=Product("Spoon",30.2)

valproductB=Product("Spoon",30.2)

valproductC=Product("Fork",17.4)

print(productA==productA)//prints:true

print(productA==productB)//prints:true

print(productB==productA)//prints:true

print(productA==productC)//prints:false

print(productB==productC)//prints:false

Bydefault,thehashCodeandequalsmethodsaregeneratedbasedoneverypropertydeclaredintheprimaryconstructor.Inmostscenariosthisisenough,butifweneedmorecontrolwearestillallowedtooverridethesemethodsbyourselvesinthedataclass.Inthiscase,thedefaultimplementationwon'tbegeneratedbythecompiler.

Page 202: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ThetoStringmethodGeneratedmethodscontainnamesandvaluesofallpropertiesdeclaredintheprimaryconstructor:

dataclassProduct(varname:String,varprice:Double)

valproductA=Product("Spoon",30.2)

println(productA)//prints:Product(name=Spoon,price=30.2)

Wecanactuallylogmeaningfuldatatoaconsoleorlogfile,insteadofclassnameandmemoryaddresslikeinJava(Person@a4d2e77).Thismakesthedebuggingprocessmuchsimpler,becausewehaveaproper,humanreadableformat.

Page 203: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ThecopymethodBydefault,theKotlincompilerwillalsogenerateanappropriatecopymethodthatwillallowustoeasilycreateacopyofanobject:

dataclassProduct(varname:String,varprice:Double)

valproductA=Product("Spoon",30.2)

print(productA)//prints:Product(name=Spoon,price=30.2)

valproductB=productA.copy()

print(productB)//prints:Product(name=Spoon,price=30.2)

Javadoesnothavenamedargumentsyntax,sowhencallingthecopymethodJavacodeweneedtopassallarguments(theorderoftheargumentscorrespondstotheorderofpropertiesdefinedintheprimaryconstructor).InKotlin,thisapproachdecreasestheneedforcopyconstructorsorcopyfactories:

ThecopyconstructortakesasingleargumentandtypeistheclasscontainingtheconstructorandreturnsthenewInstanceofthisclass:

valproductB=Product(productA)

Thecopyfactoryisthestaticfactorythattakesasingleargumentwhosetypeistheclasscontainingthefactoryandreturnsanewinstanceofthisclass:

valproductB=ProductFactory.newInstance(productA)

Thecopymethodtakesargumentsthatcorrespondtoallpropertiesdeclaredintheprimaryconstructor.Whencombinedwiththedefaultargumentssyntax,wecanprovidealloronlysomeofthepropertiestocreateamodifiedinstancecopy:

dataclassProduct(varname:String,varprice:Double)

valproductA=Product("Spoon",30.2)

print(productA)//prints:Product(name=Spoon,price=30.2)

valproductB=productA.copy(price=24.0)

print(productB)//prints:Product(name=Spoon,price=24.0)

valproductC=productA.copy(price=24.0,name="Knife")

print(productB)//prints:Product(name=Knife,price=24.0)

Thisisaveryflexiblewayofcreatingcopyoftheobjectwherewecaneasily

Page 204: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

sayif,andhow,copyshoulddifferfromoriginalinstances.Ontheotherhand,theprogrammingapproachpromotesconceptofimmutability,whichcanbeeasilyimplementedwithanargumentlesscallofthecopymethod:

//Mutableobject-modifyobjectstate

dataclassProduct(varname:String,varprice:Double)

varproductA=Product("Spoon",30.2)

productA.name="Knife"

//immutableobject-createnewobjectinstance

dataclassProduct(valname:String,valprice:Double)

varproductA=Product("Spoon",30.2)

productA=productA.copy(name="Knife")

Insteadofdefiningmutableproperties(var)andmodifyingtheobjectstate,wecandefineimmutableproperties(val),makeanobjectimmutable,andoperateonitbygettingitscopywiththechangedvalues.Thisapproachreducestheneedofdatasynchronizationinmultithreadingapplicationsandthenumberofpotentialerrorsrelatedwithit,becauseimmutableobjectscanbefreelysharedacrossthreads.

Page 205: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

DestructivedeclarationsSometimesitmakessensetorestructureobjectsintomultiplevariables.Thissyntaxiscalledadestructuringdeclaration:

dataclassPerson(valfirstName:String,vallastName:String,

valheight:Int)

valperson=Person("Igor","Wojda",180)

var(firstName,lastName,height)=person

println(firstName)//prints:"Igor"

println(lastName)//prints:"Wojda"

println(height)//prints:180

Adestructuringdeclarationallowsustocreatemultiplevariablesatonce.TheprecedingcodewillresultincreatingvaluesthefirstName,lastName,andheightvariables.Underthehood,thecompilerwillgeneratecodelikethis:

valperson=Person("Igor","Wojda",180)

varfirstName=person.component1()

varlastName=person.component2()

varheight=person.component3()

Foreverypropertydeclaredintheprimaryconstructorofthedataclass,theKotlincompilergeneratesasinglecomponentNmethod.Thesuffixofthecomponentfunctioncorrespondstotheorderofpropertiesdeclaredintheprimaryconstructor,sothefirstNamecorrespondstocomponent1,lastNamecorrespondstocomponent2,andheightcorrespondstocomponent3.Infact,wecouldinvokethosemethodsdirectlyonthePersonclasstoretrieveapropertyvalue,butthereisnopointofdoingso,becausetheirnamesaremeaninglessandcodewouldbeverydifficulttoreadandmaintain.Weshouldleavethosemethodsforthecompilerfordestructuringtheobjectandusepropertyaccesssyntaxsuchasperson.firstName.

Wecanalsoomitoneormorepropertiesusinganunderscore:

valperson=Person("Igor","Wojda",180)

var(firstName,_,height)=person

println(firstName)//prints:"Igor"

println(height)//prints:180

Inthiscase,wewantonlytocreatetwovariables,firstNameandheight;thelastNameisignored.Thecodegeneratedbythecompilerwilllookasfollows:

Page 206: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

valperson=Person("Igor","Wojda",180)

varfirstName=person.component1()

varheight=person.component3()

WecanalsodestructuresimpletypeslikeString:

valfile="MainActivity.kt"

val(name,extension)=file.split(".",limit=2)

Destructivedeclarationscanalsobeusedtogetherwiththeforloop:

valauthors=listOf(

Person("Igor","Wojda",180),

Person("Marcin","Moskała",180)

)

println("Authors:")

for((name,surname)inauthors){

println("$name$surname")

}

Page 207: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

OperatoroverloadingKotlinhasapredefinedsetofoperatorswithfixedsymbolicrepresentation(+,*,andsoon)andfixedprecedence.Mostoftheoperatorsaretranslateddirectlyintomethodcalls;somearetranslatedintomorecomplexexpressions.ThefollowingtablecontainsalistofalltheoperatorsavailableinKotlin:

Operatortoken Correspondingmethod/expression

a+b a.plus(b)

a-b a.minus(b)

a*b a.times(b)

a/b a.div(b)

a%b a.rem(b)

a..b a.rangeTo(b)

a+=b a.plusAssign(b)

a-=b a.minusAssign(b)

a*=b a.timesAssign(b)

a/=b a.divAssign(b)

Page 208: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

a%=b a.remAssign(b)

a++ a.inc()

a-- a.dec()

ainb b.contains(a)

a!inb !b.contains(a)

a[i] a.get(i)

a[i,j] a.get(i,j)

a[i_1,...,i_n] a.get(i_1,...,i_n)

a[i]=b a.set(i,b)

a[i,j]=b a.set(i,j,b)

a[i_1,...,i_n]=b a.set(i_1,...,i_n,b)

a() a.invoke()

a(i) a.invoke(i)

a(i,j) a.invoke(i,j)

a(i_1,...,i_n) a.invoke(i_1,...,i_n)

a==b a?.equals(b)?:(b===null)

Page 209: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

a!=b !(a?.equals(b)?:(b===null))

a>b a.compareTo(b)>0

a<b a.compareTo(b)<0

a>=b a.compareTo(b)>=0

a<=b a.compareTo(b)<=0

TheKotlincompilertranslatestokensthatrepresentspecificoperations(leftcolumn)tocorrespondingmethodsorexpressionsthatwillbeinvoked(rightcolumn).

Wecanprovidecustomimplementationsforeachoperatorbyusingtheminclassoperatormethodcorrespondingwithanoperatortoken.Let'sdefineasimplePointclasscontainingxandypropertiestogetherwithtwooperators,plusandtimes:

dataclassPoint(varx:Double,vary:Double){

operatorfunplus(point:Point)=Point(x+point.x,y+point.y)

operatorfuntimes(other:Int)=Point(x*other,y*other)

}

//usage

varp1=Point(2.9,5.0)

varp2=Point(2.0,7.5)

println(p1+p2)//prints:Point(x=4.9,y=12.5)

println(p1*3)//prints:Point(x=8.7,y=21.0)

Bydefiningplusandtimesoperators,wecanperformadditionandmultiplicationoperationsonanyPointinstance.Eachtime+or*operationsarecalled,Kotlincallscorrespondingoperatormethodplusortimes.Underthehood,thecompilerwillgeneratemethodcalls:

Page 210: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

p1.plus(p2)

p1.times(3)

Inourexample,wearepassingtheotherpointinstancetotheplusoperatormethod,butthistypeisnotmandatory.Operatormethoddoesnotactuallyoverrideanymethodfromthesuperclass,soithasnofixeddeclarationwithfixedparametersandfixedtypes.Wedon'thavetoinheritfromaparticularKotlintypetobeabletooverloadoperators.Allweneedtohaveisamethodwithpropersignaturemarkedasoperator.TheKotlincompilerwilldotherestbytherunningmethodthatcorrespondstotheoperator.Infact,wecandefinemultipleoperatorswiththesamenameanddifferentparametertypes:

dataclassPoint(varx:Double,vary:Double){

operatorfunplus(point:Point)=Point(x+point.x,y+point.y)

operatorfunplus(vector:Double)=Point(x+vector,y+vector)

}

varp1=Point(2.9,5.0)

varp2=Point(2.0,7.5)

println(p1+p2)//prints:Point(x=4.9,y=12.5)

println(p1+3.1)//prints:Point(x=6.0,y=10.1)

Bothoperatorsareworkingfine,becausetheKotlincompilercanselectproperoverloadoftheoperator.Manybasicoperatorshavecorrespondingcompoundassignoperator(plushasplusAssign,timeshastimesAssign,andsoon),sowhenwedefineanoperatorsuchasthe+operator,Kotlinsupportsthe+operationand+=operationaswell:

varp1=Point(2.9,7.0)

varp2=Point(2.0,7.5)

p1+=p2

println(p1)//prints:Point(x=4.9,y=14.5)

Noticetheimportantdifferencethatinsomescenariositmaybeperformancecritical.Acompoundassignoperator(forexample,the+=operator)hastheUnitreturntype,soitjustmodifiesthestateoftheexistingobject,whilethebasicoperator(forexample,the+operator)alwaysreturnsanewinstanceofanobject:

varp1=Wallet(39.0,14.5)

p1+=p2//updatestateofp1

valp3=p1+p2//createsnewobjectp3

WhenwedefinebothplusandplusAssignoperatorswiththesameparametertypes,whenwetrytousetheplusAssign(compound)operator,thecompilerwillthrow

Page 211: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

anerror,becauseitdoesnotknowwhichmethodshouldbeinvoked:

dataclassPoint(varx:Double,vary:Double){

init{

println("Pointcreated$x.$y")

}

operatorfunplus(point:Point)=Point(x+point.x,y+point.y)

operatorfunplusAssign(point:Point){

x+=point.x

y+=point.y

}

}

\\usage

varp1=Point(2.9,7.0)

varp2=Point(2.0,7.5)

valp3=p1+p2

p1+=p2//Error:Assignmentoperationsambiguity

OperatoroverloadingworksalsoforclassesdefinedinJava.Allweneedisamethodwiththepropersignatureandnamethatcorrespondstotheoperator'smethodname.TheKotlincompilerwilltranslateoperatorusagetothismethod.OperatormodifierisnotpresentinJava,soit'snotrequiredintheJavaclass:

//Java

publicclassPoint{

privatefinalintx;

privatefinalinty;

publicPoint(intx,inty){

this.x=x;

this.y=y;

}

publicintgetX(){

returnx;

}

publicintgetY(){

returny;

}

publicPointplus(Pointpoint){

returnnewPoint(point.getX()+x,point.getY()+y);

}

}

//Main.kt

valp1=Point(1,2)

valp2=Point(3,4)

valp3=p1+p2;

println("$x:{p3.x},y:${p3.y}")//prints:x:4,y:6

Page 212: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ObjectdeclarationThereareafewwaystodeclaresingletonsinJava.Hereisthemostcommonwaytodefinetheclassthathasaprivateconstructorandretrievesinstancesviaastaticfactorymethod:

publicclassSingleton{

privateSingleton(){

}

privatestaticSingletoninstance;

publicstaticSingletongetInstance(){

if(instance==null){

instance=newSingleton();

}

returninstance;

}

}

Theprecedingcodeworksfineforasinglethread,butit'snotthreadsafe,soinsomecasestwoinstancesofSingletoncanbecreated.Thereareafewwaystofixit.Wecanusethesynchronizedblockpresentedasfollows:

//synchronized

publicclassSingleton{

privatestaticSingletoninstance=null;

privateSingleton(){

}

privatesynchronizedstaticvoidcreateInstance(){

if(instance==null){

instance=newSingleton();

}

}

publicstaticSingletongetInstance(){

if(instance==null)createInstance();

returninstance;

}

}

Thissolution,however,isveryverbose.InKotlin,thereisaspeciallanguageconstructforcreatingsingletonscalledobjectdeclaration,sowecanachievethesameresultinamuchsimplerway.Definingobjectsissimilartodefiningclasses;theonlydifferenceisthatweusetheobjectkeywordinsteadoftheclass

Page 213: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

keyword:

objectSingleton

Wecanaddmethodsandpropertiestoanobjectdeclarationexactlythesamewayasinaclass:

objectSQLiteSingleton{

fungetAllUsers():List<User>{

//...

}

}

ThismethodisaccessedthesamewayasanyJavastaticmethod:

SQLiteSingleton.getAllUsers()

Objectdeclarationsareinitializedlazilyandtheycanbenestedinsideotherobjectdeclarationsornon-innerclasses.Also,theycannotbeassignedtoavariable.

Page 214: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ObjectexpressionAnobjectexpressionisequivalenttoJava'sanonymousclass.Itisusedtoinstantiateobjectsthatmightinheritfromsomeclassorimplementsaninterface.Aclassicuse-caseiswhenweneedtodefineobjectsthatareimplementingsomeinterface.ThisishowinJavawecouldimplementtheServiceConnectioninterfaceandassignittoavariable:

ServiceConnectionserviceConnection=newServiceConnection(){

@Override

publicvoidonServiceDisconnected(ComponentNamename){

...

}

@Override

publicvoidonServiceConnected(ComponentNamename,

IBinderservice)

{

...

}

}

TheclosestKotlinequivalentoftheprecedingimplementationisthefollowing:

valserviceConnection=object:ServiceConnection{

overridefunonServiceDisconnected(name:ComponentName?){}

overridefunonServiceConnected(name:ComponentName?,

service:IBinder?){}

}

Theprecedingexampleisusinganobjectexpression,whichcreatesinstanceofanonymousclassthatimplementsServiceConnectioninterface.Anobjectexpressioncanalsoextendclasses.HereishowwecancreateaninstanceoftheabstractclassBroadcastReceiver:

valbroadcastReceiver=object:BroadcastReceiver(){

overridefunonReceive(context:Context,intent:Intent){

println("Gotabroadcast${intent.action}")

}

}

valintentFilter=IntentFilter("SomeAction");

registerReceiver(broadcastReceiver,intentFilter)

Whileobjectexpressionsallowustocreateobjectsofananonymoustypethat

Page 215: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

canimplementsomeinterfaceandextendsomeclass,wecanusethemtoeasilysolveinterestingproblemsrelatedtotheAdapterpattern.

TheAdapterdesignpatternallowsotherwiseincompatibleclassestoworktogetherbyconvertingtheinterfaceofoneclassintoaninterfaceexpectedbytheclients.

Let'ssaythatwehaveaPlayerinterfaceandfunctionthatrequiresPlayerasaparameter:

interfacePlayer{

funplay()

}

funplayWith(player:Player){

print("Iplaywith")

player.play()

}

Also,wehaveVideoPlayerclassfromapubliclibrarythathastheplaymethoddefined,butitisnotimplementingourPlayerinterface:

openclassVideoPlayer{

funplay(){

println("Playvideo")

}

}

TheVideoPlayerclassmeetsalltheinterfacerequirements,butitcannotbepassedasPlayerbecauseitisnotimplementingtheinterface.Touseitasaplayer,weneedtomakeanAdapter.Inthisexample,wewillimplementitasanobjectofananonymoustypethatimplementsthePlayerinterface:

valplayer=object:VideoPlayer(),Player{}

playWith(player)

WewereabletosolveourproblemwithoutdefiningtheVideoPlayersubclass.Wecanalsodefinecustommethodsandpropertiesintheobjectexpression:

valdata=object{

varsize=1

funupdate(){

//...

}

}

data.size=2

data.update()

Page 216: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ThisisaveryeasywaytodefinecustomanonymousobjectsthatarenotpresentinJava.TodefinesimilartypesinJava,weneedtodefinethecustominterface.WecannowaddabehaviortoourVideoPlayerclasstofullyimplementthePlayerinterface:

openclassVideoPlayer{

funplay(){

println("Playvideo")

}

}

interfacePlayer{

funplay()

funstop()

}

//usage

valplayer=object:VideoPlayer(),Player{

varduration:Double=0.0

funstop(){

println("Stopvideo")

}

}

player.play()//println("Playvideo")

player.stop()//println("Stopvideo")

player.duration=12.5

Intheprecedingcode,wecancallonanonymousobject(player)methodsdefinedintheVideoPlayerclassandexpressionobject.

Page 217: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

CompanionobjectsKotlin,asopposedtoJava,lackstheabilitytodefinestaticmembers,butinsteaditallowsustodefineobjectsthatareassociatedwithaclass.Inotherwords,anobjectisinitializedonlyonce;thereforeonlyoneinstanceofanobjectexists,sharingitsstateacrossallinstancesofaparticularclass.Whenasingletonobjectisassociatedwithaclassofthesamename,itiscalledthecompanionobjectoftheclass,andtheclassiscalledthecompanionclassoftheobject:

TheprecedingdiagrampresentsthreeinstancesoftheCarclasssharingasingleinstanceofanobject.

Members,suchasmethodsandproperties,definedinsideacompanionobjectmaybeaccessedsimilarlytothewayweaccessstaticfieldsandmethodsinJava.Themainpurposeofacompanionobjectistohavecodethatisrelatedtoclass,butnotnecessarytoanyparticularinstanceofthisclass.ItisagoodwaytodefinemembersthatwouldbedefinedasstaticinJava;forexample,factory,whichcreatesaclassinstancemethodconvertingsomeunits,activityrequestcode,sharedpreferenceskey,andsoon.Todefinethesimplestcompanionobject,weneedtodefineasingleblockofcode:

classProductDetailsActivity{

companionobject{

}

}

Nowlet'sdefineastartmethodthatwillallowustostartanactivityinaneasyway:

Page 218: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

//ProductDetailsActivity.kt

classProductDetailsActivity:AppCompatActivity(){

overridefunonCreate(savedInstanceState:Bundle?){

super.onCreate(savedInstanceState)

valproduct=intent.getParcelableExtra<Product>

(KEY_PRODUCT)//3

//...

}

companionobject{

constvalKEY_PRODUCT="product"//1

funstart(context:Context,product:Product){//2

valintent=Intent(context,

ProductDetailsActivity::class.java)

intent.putExtra(KEY_PRODUCT,product)//3

context.startActivity(intent)

}

}

}

//Startactivity

ViewProductActivity.start(context,productId)//2

1. Onlysingleinstanceofkeyexists2. Methodstartcanbeinvokedwithoutcreatingobjectinstance.JustlikeJava

staticmethod.3. Retreivevalueafterinstanceiscreated.

Noticethatweareabletocallstartpriortotheactivityinstancecreation.Let'susethecompanionobjecttotrackhowmanyinstancesoftheCarclasswerecreated.Toachievethisweneedtodefinethatcountpropertywithaprivatesetter.Itcouldbealsodefinedasatop-levelproperty,butitisbettertoplaceitinsideacompanionobject,becausewedon'twanttoallowcountermodificationoutsideofthisclass:

classCar{

init{

count++;

}

companionobject{

varcount:Int=0

privateset

}

}

Theclasscanaccessallthemethodsandpropertiesdefinedinthecompanionobject,butthecompanionobjectcan'taccessanyoftheclasscontent.Thecompanionobjectisassignedtoaspecificclass,butnottoaparticularinstance:

Page 219: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

println(Car.count)//Prints0

Car()

Car()

println(Car.count)//Prints:2

Toaccessaninstanceofthecompanionobjectdirectly,wecanusetheclassname.

Wecanalsoaccessthecompanionobjectbyusingmoreverbosesyntax,Car.Companion.count,butinmostcasesthereisnopointofdoingso,unlesswewanttoaccesscompanionfromJavacode.

Page 220: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

CompanionobjectinstantiationAcompanionobjectisasingletoncreatedbyacompanionclassandkeptinitsstaticproperty.Theinstantiationofacompanionobjectislazy.Thismeansthatcompanionobjectwillbeinstantiatedwhenitisneededforthefirsttime--whenitsmembersareaccessed,orinstanceofaclasscontainingthecompanionobjectiscreated.TomarkupwhentheCarclassinstanceanditscorrespondingcompanionobjectarecreated,weneedtoaddtwoinitializerblocks--onefortheCarclass,anotherforthecompanionobject.

Theinitializerblockinsidethecompanionobjectworksexactlythesamewayasintheclass--it'sexecutedwhenaninstanceiscreated:

classCar{

init{

count++;

println("Carcreated")

}

companionobject{

varcount:Int=0

init{

println("Carcompanionobjectcreated")

}

}

}

WhiletheclassinitializationblockisequivalentoftheJavaconstructorbody,thecompilationobjectinitializationblockistheequivalentoftheJavastaticinitializationblockinKotlin.Fornow,thecountpropertycanbeupdatedbyanyclient,becauseit'saccessiblefromoutsideoftheCarclass.WewillfixthisissuelaterinthischapterintheVisibilitymodifierssection.Nowlet'saccesstheCarcompanionobjectclassmember:

Car.count//Prints:Carcompanionobjectcreated

Car()//Prints:Carcreated

Byaccessingthecountpropertydefinedinthecompanionobject,wetriggeritscreation,butnoticethataninstanceofCarclassisnotcreated.LaterwhenwecreateaCarclassinstancecompanionobjectisalreadycreated.Nowlet'sinstantiatetheCarclassbeforeaccessingthecompanionobject:

Page 221: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Car()

//Prints:Carcompanionobjectcreated

//Prints:Carcreated

Car()//Prints:Carcreated

Car.count

CompanionobjectiscreatedtogetherwithfirstinstanceofCarclass,sowhenwecreatesomeotherinstancesoftheuserclass,thecompanionobjectfortheclassalreadyexists,soit'snotcreated.

Keepinmindthattheprecedinginstantiationdescribestwoseparateexamples.Bothcouldnotbetrueinasingleprogram,becauseonlyasingleinstanceofclasscompanionobjectcanexistanditiscreatedthefirsttimewhenitisneeded.

Thecompanionobjectscanalsocontainfunctions,implementinterfaces,andevenextendclasses.Wecandefineacompanionobjectthatwillincludeastaticconstrictionmethodwiththeadditionalpossibilitytooverrideimplementationfortestingpurposes:

abstractclassProvider<T>{//1

abstractfuncreator():T//2

privatevarinstance:T?=null//3

varoverride:T?=null//4

funget():T=override?:instance?:creator().also{instance=it}//5

}

1. Providerisagenericclass.2. Abstractfunctionusedtocreateinstance.3. Fieldusedtokeepcreatedinstance.4. Fieldusedtointests,toprovidealternativeimplementationofinstance.5. Functionthatisreturningoverrideinstanceifitwasset,instanceifitwas

created,oritiscreatinginstanceusingthecreatemethodandfillinginstancefieldwithit.

Withsuchimplementation,wecandefinetheinterfacewithadefaultstaticconstructor:

interfaceMarvelRepository{

fungetAllCharacters(searchQuery:String?):Single<List<MarvelCharacter>>

companionobject:Provider<MarvelRepository>(){

overridefuncreator()=MarvelRepositoryImpl()

Page 222: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

}

}

Togetinstance,weneedtouse:

MarvelRepository.get()

Ifweneedtospecifysomeotherinstancefortestingpurposes(forexample,inEspressotests)thenwecanalwaysspecifythemusingobjectexpression:

MarvelRepository.override=object:MarvelRepository{

overridefungetAllCharacters(searchQuery:String?):

Single<List<MarvelCharacter>>{

//...

}

}

CompanionobjectsarereallypopularintheKotlinAndroidworld.TheyaremostlyusedtodefineallelementsthatwerestaticinJava(constantfields,staticcreators,andsoon),buttheyalsoprovideadditionalcapabilities.

Page 223: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

EnumclassesEnumeratedtype(enum)isadatatypeconsistingofasetofnamedvalues.Todefineanenumtype,weneedtoaddtheenumkeywordtotheclassdeclarationheader:

enumclassColor{

RED,

ORANGE,

BLUE,

GRAY,

VIOLET

}

valfavouriteColor=Color.BLUE

Toparsestringintoenum,usethevalueOfmethod(likeinJava):

valselectedColor=Color.valueOf("BLUE")

println(selectedColor==Color.BLUE)//prints:true

OrtheKotlinhelpermethod:

valselectedColor=enumValueOf<Color>("BLUE")

println(selectedColor==Color.BLUE)//prints:true

TodisplayallvaluesintheColorenum,usevaluesfunction(likeinJava):

for(colorinColor.values()){

println("name:${it.name},ordinal:${it.ordinal}")

}

OrtheKotlinenumerateValueshelpermethod:

for(colorinenumValues<Color>()){

println("name:${it.name},ordinal:${it.ordinal}")

}

//Prints:

name:RED,ordinal:0

name:ORANGE,ordinal:1

name:BLUE,ordinal:2

name:GRAY,ordinal:3

name:VIOLET,ordinal:4

Theenumtypecanalsohaveitsconstructorandtherecanbecustomdataassociatedtoeachenumconstant.Let'saddpropertieswithvaluesofred,green,and

Page 224: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

bluecolorcomponents:

enumclassColor(valr:Int,valg:Int,valb:Int){

RED(255,0,0),

ORANGE(255,165,0),

BLUE(0,0,255),

GRAY(49,79,79),

VIOLET(238,130,238)

}

valcolor=Color.BLUE

valrValue=color.r

valgValue=color.g

valbValue=color.b

Havingthesevalues,wecandefineafunctionthatwillcalculateRGBvalueforeachcolor.

Noticethatthelastconstant(VIOLET)isfollowedbyasemicolon.ThisisararesituationwhereasemicolonisactuallyrequiredinKotlincode.Itseparatestheconstantdefinitionsfromthememberdefinitions:

enumclassColor(valr:Int,valg:Int,valb:Int){

BLUE(0,0,255),

ORANGE(255,165,0),

GRAY(49,79,79),

RED(255,0,0),

VIOLET(238,130,238);

funrgb()=rshl16+gshl8+b

}

funprintHex(num:Int){

println(num.toString(16))

}

printHex(Color.BLUE.rgb())//Prints:ff

printHex(Color.ORANGE.rgb())//Prints:ffa500

printHex(Color.GRAY.rgb())//Prints:314f4f

Thergb()methodaccessesther,g,andbvariabledataforaparticularenumandcalculatesthevalueforeachenumelementseparately.WecanalsoaddavalidationfortheenumconstructorargumentsusingtheinitblockandtheKotlinstandardlibraryrequirefunction:

enumclassColor(valr:Int,valg:Int,valb:Int){

BLUE(0,0,255),

ORANGE(255,165,0),

GRAY(49,79,79),

RED(255,0,0),

VIOLET(238,130,238);

init{

require(rin0..255)

Page 225: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

require(gin0..255)

require(bin0..255)

}

funrgb()=rshl16+gshl8+b

}

Defininganincorrectenumwillresultinanexception:

GRAY(33,33,333)//IllegalArgumentException:Failedrequirement.

Therearecaseswherewewanttoassociatefundamentallydifferentbehaviorwitheachconstant.Todothiswecandefineanabstractmethodorpropertyandoverrideitineachenumblock.Let'sdefinetheenumTemperatureandtemperatureproperty:

enumclassTemperature{COLD,NEUTRAL,WARM}

enumclassColor(valr:Int,valg:Int,valb:Int){

RED(255,0,0){

overridevaltemperature=Temperature.WARM

},

ORANGE(255,165,0){

overridevaltemperature=Temperature.WARM

},

BLUE(0,0,255){

overridevaltemperature=Temperature.COLD

},

GRAY(49,79,79){

overridevaltemperature=Temperature.NEUTRAL

},

VIOLET(238,130,238{

overridevaltemperature=Temperature.COLD

};

init{

require(rin0..256)

require(gin0..256)

require(bin0..256)

}

funrgb()=(r*256+g)*256+b

abstractvaltemperature:Temperature

}

println(Color.BLUE.temperature)//prints:COLD

println(Color.ORANGE.temperature)//prints:WARM

println(Color.GRAY.temperature)//prints:NEUTRAL

Now,eachcolorcontainsnotonlyRGBinformation,butalsoanadditionalenumdescribingitstemperature.Wehaveaddedaproperty,butinananalogicalwaywecouldaddcustommethodstoeachenumelement.

Page 226: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

InfixcallsfornamedmethodsInfixcallsareoneofKotlin'sfeaturesthatallowustocreatemorefluidandreadablecode.Itallowsustowritecodethatisclosertonaturalhumanlanguage.WehavealreadyseenusageoftheinfixmethodinChapter2,LayingaFoundation,whichallowedustoeasilycreateaninstanceofaPairclass.Hereisaquickreminder:

varpair="Everest"to8848

ThePairclassrepresentsagenericpairoftwovalues.Thereisnomeaningattachedtovaluesinthisclass,soitcanbeusedforanypurpose.Pairisadataclass,soitcontainsalldataclassmethods(equals,hashCode,component1,andsoon).HereisadefinitionofthePairclassfromtheKotlinstandardlibrary:

publicdataclassPair<outA,outB>(//1

publicvalfirst:A,

publicvalsecond:B

):Serializable{

publicoverridefuntoString():String="($first,$second)"

//2

}

1. MeaningofthisoutmodifierusedbehindagenerictypewillbedescribedinChapter6,Genericareyourfriends.

2. PairshaveacustomtoStringmethod.Thisisimplementedtomakeprintedsyntaxmorereadablewhilefirstandsecondnamesarenotmeaningfulinmostusagecontexts.

Beforewedivedeeperandlearnhowtodefineourowninfixmethod,let'stranslatejustthepresentedcodeintoamorefamiliarform.Eachinfixmethodcanbeusedlikeanyothermethod:

valmountain="Everest";

varpair=mountain.to(8848)

Initsessence,theinfixnotationissimplytheabilitytocallamethodwithoutusingthedotoperatorandcalloperator(parentheses).Theinfixnotationonlylooksdifferent,butit'sstillaregularmethodcallunderneath.Inbothforegoingexamples,wesimplycallthetomethodontheStringclassinstance.toisan

Page 227: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

extensionfunctionanditwillbeexplainedinChapter7,ExtensionFunctionsandProperties,butwecanimagineasifitisamethodoftheStringclass,inthiscase,whichisjustreturninganinstanceofPaircontainingitselfandthepassedargument.WecanoperateonthereturnedPairlikeonanydataclassobject:

valmountain="Everest";

varpair=mountain.to(8848)

println(pair.first)//prints:Everest

println(pair.second)//prints:8848

InKotlin,thismethodisallowedtobeinfixonlywhenithasasingleparameter.Also,aninfixnotationdoesnothappenautomatically--weneedtoexplicitlymarkthemethodasinfix.Let'sdefineourPointclasswiththeinfixmethod:

dataclassPoint(valx:Int,valy:Int){

infixfunmoveRight(shift:Int)=Point(x+shift,y)

}

Usageexample:

valpointA=Point(1,4)

valpointB=pointAmoveRight2

println(pointB)//prints:Point(x=3,y=4)

NoticethatwearecreatinganewPointinstance,butwecouldalsomodifyanexistingone(ifthetypewasmutable).Thisdecisionisforthedevelopertomake,butinfixismoreoftenusedtogetherwithimmutabletypes.

Wecanuseinfixmethodscombinedwithenumstoachieveveryfluentsyntax.Let'simplementnaturalsyntaxthatwillallowustodefinecardsfromaclassicplayingcarddeck.Itincludes52:13ranksofeachofthefoursuits:clubs,diamonds,hearts,andspades.

Page 228: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Sourcefortheprecedingimage:https://mathematica.stackexchange.com/questions/16108/standard-deck-of-52-playing-cards-in-curated-data

Thegoalistodefinethesyntaxthatwillallowustodefineacardfromitssuitandrankitthisway:

valcard=KINGofHEARTS

Firstofall,weneedtwoenumstorepresentalltheranksandsuits:

enumclassSuit{

HEARTS,

SPADES,

CLUBS,

DIAMONDS

}

enumclassRank{

TWO,THREE,FOUR,FIVE,

SIX,SEVEN,EIGHT,NINE,

TEN,JACK,QUEEN,KING,ACE;

}

Thenweneedaclassthatwillrepresentacardcomposedofaparticularrankandparticularsuite:

dataclassCard(valrank:Rank,valsuit:Suit)

NowwecaninstantiateaCardclasslikethis:

valcard=Card(Rank.KING,Suit.HEARTS)

Page 229: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Tosimplifythesyntax,weintroduceanewinfixmethodintotheRankenum:

enumclassRank{

TWO,THREE,FOUR,FIVE,

SIX,SEVEN,EIGHT,NINE,

TEN,JACK,QUEEN,KING,ACE;

infixfunof(suit:Suit)=Card(this,suit)

}

ThiswillallowustocreateaCardcalllikethis:

valcard=Rank.KING.of(Suit.HEARTS)

Becausethemethodismarkedasinfix,wecanremovethedotcalloperatorandparentheses:

valcard=Rank.KINGofSuit.HEARTS

Usageofstaticimportswillallowustoshortenthesyntaxevenmoreandachieveourfinalresult:

importRank.KING

importSuit.HEARTS

valcard=KINGofHEARTS

Besidesbeingsupersimple,thiscodeisalso100%type-safe.WecanonlydefinecardsusingpredefinedenumsofRankandSuit,soweareunabletodefinesomefictionalcardbymistake.

Page 230: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

VisibilitymodifiersKotlinsupportsfourtypesofvisibilitymodifiers(accessmodifiers)--private,protected,public,andinternal.Kotlindoesnotsupportpackage-privateJavamodifiers.ThemaindifferenceisthatthedefaultvisibilitymodifierinKotlinispublic,andit'snotrequiredtospecifyitexplicitly,soitcanbeomittedforaparticulardeclaration.Allofthemodifierscanbeappliedtovariouselementsdividedintotwomaingroupsbasedontheirdeclarationsite:top-levelelementsandnestedmembers.

AquickreminderfromChapter3,PlayingwithFunctions,toplevelelementsareelementsdeclareddirectlyinsidetheKotlinfile,asopposedtoelementsnestedinsideaclass,object,interface,orfunction.InJava,wecoulddeclareonlyclassesandinterfacesatthetoplevel,whileKotlinalsoallowsfunctions,objects,properties,andextensionsthere.

Firstwehavetop-levelelementsvisibilitymodifiers:

public(default):Elementisvisibleeverywhere.private:Elementisvisibleinsidethefilecontainingthedeclaration.protected:Notavailableattoplevel.internal:Elementisvisibleeverywhereinthesamemodule.Itispublicforelementsinthesamemodule.

WhatisamoduleinJavaandKotlin?

AmoduleisjustasetofKotlinfilescompiledtogether;forexample,IntelliJIDEAmodule,Gradleproject.Themodularstructureofapplicationsallowsforbetterdistributedresponsibilitiesandspeedsupbuildtime,becauseonlychangedmodulesarerecompiled.

Let'slookatanexample:

//top.kt

publicvalversion:String="3.5.0"//1

Page 231: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

internalclassUnitConveter//3

privatefunprintSomething(){

println("Something")

}

funmain(args:Array<String>){

println(version)//1,Prints:"3.5.0"

UnitConveter()//2,Accessible

printSomething()//3,Prints:Something

}

//branch.kt

funmain(args:Array<String>){

println(version)//1,Accessible

UnitConveter()//2,Accessible

printSomething()//3,Error

}

//main.ktinanothermodule

funmain(args:Array<String>){

println(version)//1,Accessible

UnitConveter()//2,Error

printSomething()//3,Accessible

}

1. versionpropertyispublic,soitisaccessibleinallfiles.2. UnitConveterisaccessibleinthebranch.ktfile,whileitisinthesamemodule,

butnotinmain.ktbecauseitislocatedinanothermodule.3. TheprintSomethingfunctionisaccessibleonlyinthesamefilewhereitis

defined.

NotethatthepackageinKotlinisnotgivinganyextravisibilityprivileges.

Thesecondgroupconsistsofmembers--elements,declaredinsideatoplevelelement.Mainlythosewillbemethods,properties,constructors,sometimesobjects,companionobjects,gettersandsetters,andoccasionallynestedclassesandnestedinterfaces.Herearetheobligatoryrules:

public(default):Clientwhoseesthedeclaringclassseesitspublicmembers.private:Elementisvisibleonlyinsidetheclassorinterfacecontainingthemember.protected:Visibleinsidetheclasscontainingthedeclarationandsubclasses.Itisnotapplicableinsideanobject,becauseanobjectcannotbeopened.internal:Anyclientinsidethismodulewhoseesthedeclaringclassseesitsinternalmembers.

Let'sdefineatoplevelelement.Inthisexample,wewilldefineclass,butthe

Page 232: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

samelogicisappliedtoanytoplevelelementthathasnestedmembers:

classPerson{

publicvalname:String="Igor"

protectedvarage:Int=23

internalfunlearn(){}

privatefunspeak(){}

}

WhenwecreateaninstanceofthePersonclass,wecanaccessonlythenamepropertymarkedwithapublicmodifierandthelearnmethodmarkedwithinternalmodifier:

//main.ktinsidethesamepackageasPersondefinition

valperson=Person()

println(person.name)//1

person.speak()//2,Error

person.age//3,Error

person.learn()//4

1. clientwhocanaccessthePersoninstance,canalsoaccessthenameproperty.2. speakmethodisaccessibleonlyinsidethePersonclass.3. agepropertyisaccessibleinsidethePersonclassanditssubclasses.4. clientinsidethemodulethatcanaccessthePersonclassinstancecanalso

accessitspublicmembers.

Inheritanceaccessibilityissimilartoexternalaccessaccessibility,butthemaindifferenceisthatthemembermarkedwiththeprotectedmodifierisalsovisibleinsidethesubclasses:

openclassPerson{

publicvalname:String="Igor"

privatefunspeak(){}

protectedvarage:Int=23

internalfunlearn(){}

}

classStudent():Person(){

fundoSth(){

println(name)

learn()

print(age)

//speak()//1

}

}

1. IntheStudentsubclasswecanaccessmembersmarkedwithpublic,protected,andinternal,butnotmembersmarkedwithprivatemodifier.

Page 233: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

InternalmodifierandJavabytecodeItisprettyobvioushowpublic,private,andprotectedmodifiersarecompiledtoJavawhiletheyhavedirectanalogs.ButthereisaproblemwiththeinternalmodifierbecauseithasnodirectanaloginJavasothereisalsonosupportonJavabytecode.Thisiswhytheinternalmodifierisactuallycompiledtothepublicmodifier,andtocommunicatethatitshouldn'tbeusedinJava,itsnameismashed(changedsothatitisnotusableanymore).Forexample,whenwehavetheFooclass:

openclassFoo{

internalfunboo(){}

}

ItcouldbepossibletouseitfromJavathisway:

publicclassJava{

voida(){

newFoo().boo$production_sources_for_module_SmallTest();

}

}

ItisprettycontroversialthatinternalvisibilityisguardedbyKotlinanditcanbebypassedusingaJavaadapter,butthereisnootherpossibilitytoimplementit.

Besidesdefiningvisibilitymodifiersinaclass,wearealsoabletooverridethemwhileoverridingamember.Thisgivesustheabilitytoweakenaccessrestrictionsintheinheritancehierarchy:

openclassPerson{

protectedopenfunspeak(){}

}

classStudent():Person(){

publicoverridefunspeak(){

}

}

valperson=Person()

//person.speak()//1

valstudent=Student()

student.speak()//2

Page 234: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

1. Errorspeakmethodisnotaccessiblebecauseit'sprotected.2. Visibilityofthespeakmethodwaschangedtopublicsowecanaccessit.

Definingmodifiersformembersandtheirvisibilityscopeisquitestraightforward,solet'sseehowtodefineclassandconstructorvisibility.Asweknow,primaryconstructordefinitionisintheclassheader,sotwovisibilitymodifiersarerequiredinasingleline:

internalclassFruitprivateconstructor{

varweight:Double?=null

companionobject{

funcreate()=Fruit()

}

}

Assumingtheprecedingclassisdefinedatthetoplevel,itwillbevisibleinsidethemodule,butitcanbeonlyinstantiatedfromwithinthefilecontainingtheclassdeclaration:

varfruit:Fruit?=null//Accessible

fruit=Fruit()//Error

fruit=Fruit.create()//Accessible

Getterandsettersbydefaulthavethesamevisibilitymodifierastheproperty,butwecanmodifyit.Kotlinallowsustoplaceavisibilitymodifierbeforetheget/setkeyword:

classCar{

init{

count++;

println("Carcreated")

}

companionobject{

init{

println("Carcompanionobjectcreated")

}

varcount:Int=0

privateset

}

}

Intheprecedingexample,wehavechangedthegettervisibility.Noticethatthisapproachallowsustochangethevisibilitymodifierwithoutchangingitsdefaultimplementation(generatedbythecompiler).Now,ourinstancecounterissafe,becauseit'sread-onlyexternalclients,butwecanstillmodifythepropertyvaluefrominsidetheCarclass.

Page 235: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

SealedclassesAsealedclassisaclasswithlimitednumberofsubclasses(sealedsubtypinghierarchy).PriortoKotlin1.1,thosesubclasseshadtobedefinedinsideasealedclassbody.Kotlin1.1weakenedthisrestrictionandallowedustodefinesealedclasssubclassesinthesamefileasasealedclassdeclaration.Alltheclassesaredeclaredclosetoeachother,sowecaneasilyseeallpossiblesubclassesbysimplylookingatonefile:

//vehicle.kt

sealedclassVehicle()

classCar:Vehicle()

classTruck:Vehicle()

classBus:Vehicle()

Tomarkaclassassealed,simplyaddasealedmodifiertotheclassdeclarationheader.TheprecedingdeclarationmeansthattheVehicleclasscanbeonlyextendedbythreeclassesCar,Truck,andBusbecausetheyaredeclaredinsidethesamefile.Wecouldaddafourthclassinourvehicle.ktfile,butitwouldnotbepossibletodefinesuchaclassinanotherfile.

ThesealedsubtypingrestrictionappliesonlytodirectinheritorsoftheVehicleclass.ThismeansthatVehiclecanbeextendedonlybyclassesdefinedinthesamefile(Car,Truck,orBus),butassumingthatCar,Truck,orBusclasseswouldbeopenthentheycanbeextendedbyaclassdeclaredinsideanyfile:

//vehicle.kt

sealedclassVehicle()

openclassBus:Vehicle()

//data.kt

classSchoolBus:Bus()

Topreventthisbehavior,wewouldneedtoalsomarkCar,Truck,orBusclassesassealed:

//vehicle.kt

sealedclassVehicle()

sealedclassBus:Vehicle()

//data.kt

classSchoolBus:Bus()//ErrorcannotaccessBus

Page 236: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Thesealedclassesworkreallywellwiththewhenexpression.Thereisnoneedforanelseclause,becauseacompilercanverifythateachsubclassofasealedclasshasacorrespondingclauseinsidethewhenblock:

when(vehicle){

isCar->println("Cantransport4people")

isTruck->println("Cantransportfurnitures")

isBus->println("Cantransport50people")

}

WecansafelyaddanewsubclasstotheVehicleclass,becauseifsomewhereintheapplicationthecorrespondingclauseofthewhenexpressionismissing,theapplicationwillnotcompile.ThisfixesproblemswiththeJavaswitchstatement,whereprogrammersoftenforgettoaddpropercapsules,whichleadstoprogramcrashesatruntimeorundetectedbugs.

Sealedclassesareabstractbydefault,soaabstractmodifierisredundant.Sealedclassescanneverbeopenorfinal.Wecanalsosubstituteasubclasswithobjectsincaseweneedtomakesurethatonlyasingleinstanceexists:

sealedclassEmployee()

classProgrammer:Employee()

classManager:Employee()

objectCEO:Employee()

Theprecedingdeclarationnotonlyprotectstheinheritancehierarchy,butalsolimitsCEOtoasingleinstance.Thereareafewinterestingapplicationsforsealedclassesthatexceedthescopeofthisbook,butit'sgoodtobeawareofthem:

Definedatatypessuchasalinkedlistorbinarytree(https://en.wikipedia.org/wiki/Algebraic_data_type).ProtectinheritancehierarchywhenbuildinganapplicationmoduleorlibrarybydisallowingclientstoextendourclassandstillkeeptheabilitytoextenditbyourselvesStatemachinewheresomestatescontaindatathatmakesnosenseinotherstates(https://en.wikipedia.org/wiki/Finite-state_machine)Listofpossibletokenstypesforlexicalanalysis

Page 237: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

NestedclassesAnestedclassisaclassdefinedinsideanotherclass.Nestingsmallclasseswithintop-levelclassesplacesthecodeclosertowhereitisused,andallowsabetterwayofgroupingclasses.TypicalexamplesareTree/Leaflistenersorpresenterstates.KotlinsimilartoJavaallowsustodefineanestedclassandtherearetwomainwaystodoso.Wecandefineclassasamemberinsideaclass:

classOuter{

privatevalbar:Int=1

classNested{

funfoo()=2

}

}

valdemo=Outer.Nested().foo()//==2

TheprecedingexampleallowsustocreateaninstanceofaNestedclasswithoutcreatinginstancesofanOuterclass.Inthiscase,aclasscannotreferdirectlytoinstancevariablesormethodsdefinedinitsenclosingclass(itcanusethemonlythroughanobjectreference).ThisisequivalentofaJavastaticnestedclassandingeneralstaticmembers.

Tobeabletoaccessmembersofanouterclass,wemustcreateasecondkindofclassbymarkinganestedclassasinner:

classOuter{

privatevalbar:Int=1

innerclassInner{

funfoo()=bar

}

}

valouter=Outer()

valdemo=outer.Inner().foo()//==1

NowtoinstantiatetheinnerclasswemustfirstinstantiatetheOuterclass.Inthiscase,theInnerclasscanaccessallthemethodsandpropertiesdefinedintheouterclassandsharestatewithouterclass.OnlyasingleinstanceofInnerclasscanexistperinstanceoftheOuterclass.Let'ssumupthedifferences:

Page 238: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Behavior Class(member)Innerclass(member)

BehaveasaJavastaticmember Yes No

Instanceofthisclasscanexistwithoutaninstanceofenclosingclass Yes No

HasReferencetoouterclass No Yes

Sharestatewithouterclass(canaccessouterclassmembers) No Yes

Numberofinstances Unlimited Oneperouterclassinstance

Whendecidingwhetherweshoulddefineinnerclassortop-levelclassweshouldthinkaboutpotentialclassusage.Iftheclassisonlyusefulforasingleclassinstanceweshoulddeclareitasinner.Ifaninnerclassatsomepointwouldbeusefulinanothercontextthanservingitsouterclass,thenweshoulddeclareitasatop-levelclass.

Page 239: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ImportaliasesAnaliasisawaytointroducenewnamesfortypes.Ifthetypenameisalreadyusedinthefile,isinappropriate,ortoolong,youcanintroduceadifferentnameanduseitinsteadoftheoriginaltypename.Aliasdoesnotintroduceanewtypeanditisavailableonlybeforecompiletime(whenwritingcode).Thecompilerreplacesaclassaliaswithanactualclass,soitdoesnotexistatruntime.

Sometimesweneedtouseafewclasseswiththesamenameinasinglefile.Forexample,InterstitialAdtypeisdefinedbothintheFacebookandGoogleadvertisinglibraries.Let'ssupposethatwewanttousethembothinasinglefile.Thissituationiscommoninprojectswhereweneedbothadprovidersimplementedtoallowforaprofitcomparisonbetweenthem.Theproblemisthatusingbothdatatypesinasinglefilewouldmeanthatweneedtoaccessoneorbothofthembyafullyqualifiedclassname(namespace+classname):

importcom.facebook.ads.InterstitialAd

valfbAd=InterstitialAd(context,"...")

valgoogleAd=com.google.android.gms.ads.InterstitialAd(context)

Qualifiedversusunqualifiedclassname

Theunqualifiedclassnameissimplythenameoftheclass;forexample,Box.Aqualifiedclassnameisanamespacecombinedwithaclassname;forexample,com.test.Box.

Inthesesituations,peopleoftensaythatthebestfixistorenameoneoftheclasses,butsometimesthismaynotbepossible(theclassisdefinedinanexternallibrary)ordesirable(classnameisconsistentwithbackenddatabasetable).Inthissituation,whereboththeclassesarelocatedinanexternallibrary,thesolutionforclassnamingconflictistouseanimportalias.WecanuseittorenameGoogleInterstitialAdtoGoogleAd,andFacebookInterstitialAdtoFbAd:

importcom.facebook.ads.InterstitialAdasFbAd

importcom.google.android.gms.ads.InterstitialAdasGoogleAd

Andnowwecanusethesealiasesaroundthefileasiftheywereactualtypes:

Page 240: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

valfbAd=FbAd(context,"...")

valgoogleAd=GoogleAd(context)

Usingtheimportalias,wecanexplicitlyredefinenamesofaclassthatareimportedintoafile.Inthissituation,wedidn'thavetousetwoaliases,butthisservestoimprovereadability--it'sbettertohaveFbAdandGoogleAdthanInterstitialAdandGoogleAd.Wedon'thavetousefullyqualifiedclassnamesanymore,becausewesimplysaidtothecompiler"eachtimewhenyouencounterGoogleAdaliastranslateittocom.google.android.gms.ads.InterstitialAdduringcompilationandeachtimewhenyouencounterFbAdaliastranslateittocom.facebook.ads.InterstitialAd.Importaliasworksonlyinsideafilewherealiasisdefined.

Page 241: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

SummaryInthischapter,wehavediscussedconstructs,whicharebuildingsblocksforobject-orientedprogramming.We'velearnedhowtodefineinterfacesandvariousclassesandthedifferencebetweeninner,sealed,enum,anddataclasses.Welearnedthatallelementsarepublicbydefaultandallclasses/interfacesarefinal(bydefault),soweneedtoexplicitlyopenthemtoallowinheritanceandmembersoverriding.

Wediscussedhowtodefineproperdatamodelsusingveryconcisedataclassescombinedwithevenmorepowerfulproperties.Weknowhowtoproperlyoperateondatausingvariousmethodsgeneratedbythecompilerandhowtooverloadoperators.

Welearnedhowtocreatesingletonsbyusingobjectdeclarationsandhowtodefineobjectsofananonymoustypethatmayextendsomeclassand/orimplementsomeinterfaceusingobjectexpressions.Wealsopresentedusageofthelateinitmodifierthatallowsustodefinenon-nullabledatatypeswithinitializationdelayedintime.

Inthenextchapter,wewillcoverthemorefunctionalsideofKotlinbylookingintoconceptsrelatedtofunctionalprogramming(FP).Wewilldiscussfunctionaltypes,lambdas,andhigher-orderfunctions.

Page 242: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

FunctionsasFirst-ClassCitizensInthepreviouschapter,wesawhowKotlinfeaturesrelatetoOOP.ThischapterwillintroduceadvancedfunctionalprogrammingfeaturesthatwerepreviouslynotpresentinstandardAndroiddevelopment.SomeofthemwereintroducedinJava8(inAndroidthroughtheRetrolambdaplugin),butKotlinintroducesmanymorefunctionalprogrammingfeatures.

Thischapterisabouthigh-levelfunctionsandfunctionsasfirst-classcitizens.Mostoftheconceptsaregoingtobefamiliartoreaderswhohaveusedfunctionallanguagesinthepast.

Inthischapter,wewillcoverthefollowingtopics:

FunctiontypesAnonymousfunctionsLambdaexpressionsImplicitnameofasingleparameterinalambdaexpressionHigher-orderfunctionsLastlambdainargumentconventionJavaSingleAbstractMethod(SAM)lambdainterfaceJavamethodswithJavaSingleAbstractMethodonparametersusageNamedparametersinfunctiontypesTypealiasesInlinefunctionsFunctionreferences

Page 243: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

FunctiontypeKotlinsupportsfunctionalprogramming,andfunctionsarefirst-classcitizensinKotlin.Afirst-classcitizen,inagivenprogramminglanguage,isatermthatdescribesanentitythatsupportsalltheoperationsgenerallyavailabletootherentities.Theseoperationstypicallyincludebeingpassedasanargument,returnedfromafunction,andassignedtoavariable.Thesentence"afunctionisafirst-classcitizeninKotlin"shouldthenbeunderstoodas:itispossibleinKotlintopassfunctionsasanargument,returnthemfromfunctions,andassignthemtovariables.WhileKotlinisastaticallytypedlanguage,thereneedstobeafunctiontypedefinedtoallowtheseoperations.InKotlin,thenotationusedtodefineafunctiontypeisfollowing:

(typesofparameters)->returntype

Herearesomeexamples:

(Int)->Int:AfunctionthattakesIntasanargumentandreturnsInt()->Int:AfunctionthattakesnoargumentsandreturnsInt(Int)->Unit:AfunctionthattakesIntanddoesnotreturnanything(onlyUnit,whichdoesnotneedtobereturned)

Herearesomeexamplesofpropertiesthatcanholdfunctions:

lateinitvara:(Int)->Int

lateinitvarb:()->Int

lateinitvarc:(String)->Unit

Thetermfunctiontypeismostoftendefinedasthetypeofavariableorparametertowhichafunctioncanbeassigned,ortheargumentorresulttypeofahigher-orderfunctiontakingorreturningafunction.InKotlin,thefunctiontypecanbetreatedlikeaninterface.

WewillseelaterinthischapterthatKotlinfunctionscantakeotherfunctionsinarguments,orevenreturnthem:

funaddCache(function:(Int)->Int):(Int)->Int{

//code

Page 244: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

}

valfibonacciNumber:(Int)->Int=//functionimplementation

valfibonacciNumberWithCache=addCache(fibonacciNumber)

Ifafunctioncantakeorreturnafunction,thenthefunctiontypealsoneedstobeabletodefinefunctionsthattakeafunctionasanargument,orreturnafunction.Thisisdonebysimplyplacingafunctiontypenotationasaparameterorareturntype.Herearesomeexamples:

(String)->(Int)->Int:AfunctionthattakesStringandreturnsafunctionthattakesInttypeandreturnsInt.(()->Int)->String:Afunctionthattakesanotherfunctionasanargument,andreturnsStringtype.FunctioninargumenttakesnoargumentsandreturnsInt.

Eachpropertywithafunctiontypecanbecalledlikeafunction:

vali=a(10)

valj=b()

c("SomeString")

Functionscannotonlybestoredinvariables,theycanalsobeusedasageneric.Forexample,wecankeepthefunctionsinthelist:

vartodoList:List<()->Unit>=//...

for(taskintodoList)task()

Theprecedinglistcanstorefunctionswiththe()->Unitsignature.

Page 245: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Whatisfunctiontypeunderthehood?Underthehood,functiontypesarejustasyntacticsugarforgenericinterfaces.Let'slookatsomeexamples:

The()->UnitsignatureisaninterfaceforFunction0<Unit>.TheexpressionisFunction0,becauseithaszeroparameters,andUnitbecauseitisthereturntype.The(Int)->UnitsignatureisinterfaceforFunction1<Int,Unit>.TheexpressionisFunction1becauseithasoneparameter.The()->(Int,Int)->StringsignatureisaninterfaceforFunction0<Function2<Int,Int,String>>.

Alloftheseinterfaceshaveonlyonemethod,invoke,whichisanoperator.Itallowsanobjecttobeusedlikeafunction:

vala:(Int)->Unit=//...

a(10)//1

a.invoke(10)//1

1. Thesetwostatementshavethesamemeaning

Functioninterfacesarenotpresentinastandardlibrary.Theyaresyntheticcompiler-generatedtypes(theyaregeneratedduringcompilation).Becauseofthis,thereisnoartificiallimitinnumberoffunctiontypearguments,andthestandardlibrarysizeisnotincreased.

Page 246: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

AnonymousfunctionsOnewayofdefiningafunctionasanobjectisbyusinganonymousfunctions.Theyworkthesamewayasnormalfunctions,buttheyhavenonamebetweenthefunkeywordandtheparametersdeclaration,sobydefaulttheyaretreatedasobjects.Hereareafewexamples:

vala:(Int)->Int=fun(i:Int)=i*2//1

valb:()->Int=fun():Int{return4}

valc:(String)->Unit=fun(s:String){println(s)}

1. Thisisananonymoussingleexpressionfunction.Notethatlikeinanormalsingleexpressionfunction,thereturntypedoesnotneedtobespecifiedwhenitisinferredfromtheexpressionreturntype.

Considerthefollowingusage:

//Usage

println(a(10))//Prints:20

println(b())//Prints:4

c("Kotlinrules")//Prints:Kotlinrules

Inthepreviousexamples,functiontypesweredefinedexplicitly,butwhileKotlinhasagoodtypeinferencesystem,thefunctiontypecanalsobeinferredfromtypesdefinedbyananonymousdefaultfunction:

vara=fun(i:Int)=i*2

varb=fun():Int{return4}

varc=fun(s:String){println(s)}

Italsoworksintheoppositeway.Whenwedefinethetypeofaproperty,thenwedon'tneedtosetparametertypesinanonymousfunctionsexplicitly,becausetheyareinferred:

vara:(Int)->Int=fun(i)=i*2

varc:(String)->Unit=fun(s){println(s)}

Ifwecheckoutthemethodsoffunctiontypes,thenwewillseethatthereisonlytheinvokemethodinside.Theinvokemethodisanoperatorfunction,anditcanbeusedinthesamewayasfunctioninvocation.Thisiswhythesameresultcanbeachievedbyusingtheinvokecallinsidebrackets:

Page 247: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

println(a.invoke(4))//Prints:8

println(b.invoke())//Prints:4

c.invoke("Hello,World!")//Prints:Hello,World!

Thisknowledgecanbehelpful,forexample,whenwearekeepingfunctioninanullablevariable.Wecan,forexample,usetheinvokemethodbyusingthesafecall:

vara:((Int)->Int)?=null//1

if(false)a=fun(i:Int)=i*2

print(a?.invoke(4))//Prints:null

1. Variableaisnullable,weareusinginvokebyasafecall.

Let'slookatanAndroidexample.Weoftenwanttodefineasingleerrorhandlerthatwillincludemultipleloggingmethodsandpassittodifferentobjectsasanargument.Hereishowwecanimplementitusinganonymousfunctions:

valTAG="MainActivity"

valerrorHandler=fun(error:Throwable){

if(BuildConfig.DEBUG){

Log.e(TAG,error.message,error)

}

toast(error.message)

//Othermethods,like:Crashlytics.logException(error)

}

//Usageinproject

valadController=AdController(errorHandler)

valpresenter=MainPresenter(errorHandler)

//Usage

valerror=Error("ExampleError")

errorHandler(error)//Logs:MainActivity:ExampleError

Anonymousfunctionsaresimpleanduseful.Theyareasimplewayofdefiningfunctionsthatcanbeusedandpassedasobjects.Butthereisasimplerwayofachievingsimilarbehavior,anditiscalledlambdaexpressions.

Page 248: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

LambdaexpressionsThesimplestwaytodefineanonymousfunctionsinKotlinisbyusingafeaturecalledlambdaexpressions.TheyaresimilartoJava8lambdaexpressions,butthebiggestdifferenceisthatKotlinlambdasareactuallyclosures,sotheyallowustochangevariablesfromthecreationcontext.ThisisnotallowedinJava8lambdas.Wewilldiscussthisdifferencelaterinthissection.Let'sstartwithsomesimpleexamples.LambdaexpressionsinKotlinhavethefollowingnotation:

{arguments->functionbody}

Insteadofreturn,resultofthelastexpressionisreturned.Herearesomesimplelambdaexpressionexamples:

{1}:Alambdaexpressionthattakesnoargumentsandreturns1.Itstypeis()->Int.{s:String->println(s)}:AlambdaexpressionthattakesoneargumentoftypeString,andprintsit.ItreturnsUnit.Itstypeis(String)->Unit.{a:Int,b:Int->a+b}:AlambdaexpressionthattakestwoIntargumentsandreturnsthesumofthem.Itstypeis(Int,Int)->Int.

Functionswedefinedinthepreviouschaptercanbedefinedusinglambdaexpressions:

vara:(Int)->Int={i:Int->i*2}

varb:()->Int={4}

varc:(String)->Unit={s:String->println(s)}

Whilethereturnedvalueistakenfromthelaststatementinlambdaexpressions,returnisnotallowedunlessithasareturnstatementqualifiedbyalabel:

vara:(Int)->Int={i:Int->returni*2}

//Error:Returnisnotallowedthere

varl:(Int)->Int=l@{i:Int->return@li*2}

Lambdaexpressionscanbemultiline:

valprintAndReturn={i:Int,j:Int->

println("Icalculate$i+$j")

Page 249: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

i+j//1

}

1. Thisisthelaststatement,sotheresultofthisexpressionwillbeareturnedvalue.

Multiplestatementscanalsobedefinedinasinglelinewhentheyareseparatedbysemicolons:

valprintAndReturn={i:Int,j:Int->println("Icalculate$i+$j");

i+j}

Alambdaexpressiondoesnotneedtoonlyoperateonvaluesprovidedbyarguments.LambdaexpressionsinKotlincanuseallpropertiesandfunctionsfromthecontextwheretheyarecreated:

valtext="Text"

vara:()->Unit={println(text)}

a()//Prints:Text

a()//Prints:Text

ThisisthebiggestdifferencebetweenKotlinandJava8lambdausage.BothJavaanonymousobjectsandJava8lambdaexpressionsallowustousefieldsfromthecontext,butJavadoesnotallowustoassigndifferentvaluestothesevariables(Javavariablesusedinlambdamustbefinal):

Kotlinhasgoneastepaheadbyallowinglambdaexpressionsandanonymousfunctionstomodifythesevariables.Lambdaexpressionsthatencloselocalvariablesandallowustochangetheminsidethefunctionbodyarecalledclosures.Kotlinfullysupportsclosuredefinition.Toavoidconfusionbetweenlambdasandclosures,inthisbook,wewillalwayscallbothofthemlambdas.Let'slookatanexample:

vari=1

vala:()->Int={++i}

println(i)//Prints:1

Page 250: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

println(a())//Prints:2

println(i)//Prints:2

println(a())//Prints:3

println(i)//Prints:3

Lambdaexpressionscanuseandmodifyvariablesfromthelocalcontext.Hereisanexampleofcounter,wherethevalueiskeptinalocalvariable:

funsetUpCounter(){

varvalue:Int=0

valshowValue={counterView.text="$value"}

counterIncView.setOnClickListener{value++;showValue()}

//1

counterDecView.setOnClickListener{value--;showValue()}

//1

}

1. HereishowViewonClickListenercanbesetinKotlinusingalambdaexpression.ThiswillbedescribedintheJavaSAMsupportinKotlinsection.

Thankstothisfeature,itissimplertouselambdaexpressions.Notethat,intheprecedingexample,theshowValuetypewasnotspecified.ThisisbecauseinKotlinlambdas,typingargumentsisoptionalwhenthecompilercaninferitfromthecontext:

vala:(Int)->Int={i->i*2}//1

valc:(String)->Unit={s->println(s)}//2

1. TheinferredtypeofiisInt,becausethefunctiontypedefinesanIntparameter.

2. TheinferredtypeofsisString,becausethefunctiontypedefinesaStringparameter.

Aswecanseeinthefollowingexample,wedon'tneedtospecifythetypeofparameterbecauseitisinferredfromthetypeoftheproperty.Typeinferencealsoworksintheanotherway--wecandefinethetypeofalambdaexpression'sparametertoinferthepropertytype:

valb={4}//1

valc={s:String->println(s)}//2

vala={i:Int->i*2}//3

1. Theinferredtypeis()->Int,because4isIntandthereisnoparametertype.2. Theinferredtypeis(String)->Unit,becausetheparameteristypedasString,

andthereturntypeoftheprintlnmethodisUnit.

Page 251: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

3. Theinferredtypeis(Int)->Int,becauseiistypedasInt,andthereturntypeofthetimesoperationfromIntisalsoInt.

Thisinferencesimplifieslambdaexpressiondefinition.Often,whenwearedefininglambdaexpressionsasfunctionparameters,wedon'tneedtospecifyparametertypeseachtime.Butthereisalsoanotherbenefit--whiletheparametertypecanbeinferred,asimplernotationforsingleparameterlambdaexpressionscanbeused.Let'sdiscussthisinthenextsection.

Page 252: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ImplicitnameofasingleparameterWecanomitlambdaparameterdefinitionsandaccessparametersusingtheitkeywordwhentwoconditionsaremet:

ThereisonlyoneparameterParametertypecanbeinferredfromthecontext

Asanexample,let'sdefinethepropertiesaandcagain,butthistimeusingtheimplicitnameofasingleparameter:

vala:(Int)->Int={it*2}//1

valc:(String)->Unit={println(it)}//2

1. Sameas{i->i*2}.2. Sameas{s->println(s)}.

ThisnotationisreallypopularinKotlin,mostlybecauseitisshorteranditallowsustoavoidparametertypespecification.ItalsoimprovesthereadabilityofprocessingdefinedinLINQstyle.Thisstyleneedscomponentsthathavenotyetbeenintroduced,butjusttoshowtheidea,let'sseeanexample:

strings.filter{it.length=5}.map{it.toUpperCase()}

SupposingthatstringsisList<String>,thisexpressionfiltersstringswithalengthequalto5andconvertsthemtouppercase.

Notethatinthebodyoflambdaexpressions,wecanusemethodsoftheStringclass.Thisisbecausefunctiontype(suchas(String)->Booleanforthefilter)isinterredfromthemethoddefinition,whichinfersStringfromtheiterabletype(List<String>).Also,thetypeofthereturnedlist(List<String>)dependsonwhatisreturnedbythelambda(String).

LINQstyleispopularinfunctionallanguagesbecauseitmakesthesyntaxofcollectionsorStringprocessingreallysimpleandconcise.ItwillbediscussedinmuchmoredetailinChapter7,ExtensionFunctionsandProperties.

Page 253: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Higher-orderfunctionsAhigher-orderfunctionisafunctionthattakesatleastonefunctionasanargument,orreturnsafunctionasitsresult.ItisfullysupportedinKotlin,asfunctionsarefirst-classcitizens.Let'sseeitinanexample.Let'ssupposethatweneedtwofunctions:afunctionthatwilladdallBigDecimalnumbersfromlist,andafunctionthatwillgettheproduct(theresultofmultiplicationbetweenalltheelementsinthislist)ofallthesenumbers:

funsum(numbers:List<BigDecimal>):BigDecimal{

varsum=BigDecimal.ZERO

for(numinnumbers){

sum+=num

}

returnsum

}

funprod(numbers:List<BigDecimal>):BigDecimal{

varprod=BigDecimal.ONE

for(numinnumbers){

prod*=num

}

returnprod

}

//Usage

valnumbers=listOf(

BigDecimal.TEN,

BigDecimal.ONE,

BigDecimal.valueOf(2)

)

print(numbers)//[10,1,2]

println(prod(numbers))//20

println(sum(numbers))//13

Thesearereadablefunctions,butalsothesefunctionsarenearlythesame.Theonlydifferenceisname,accumulator(BigDecimal.ZEROorBigDecimal.ONE),andoperation.IfweusetheDRY(Don'tRepeatYourself)rulethenweshouldn'tleavetwopartsofsimilarcodeintheproject.Whileitiseasytodefineafunctionthatwillhavesimilarbehaviorandjustdifferintheobjectsused,itishardertodefineafunctionthatwilldifferintheoperationperformed(here,functionsdifferbytheoperationusedtoaccumulate).Solutioncomeswiththefunctiontype,becausewecanpasstheoperationasanargument.Inthisexample,itispossibletoextractthecommonmethodthisway:

funsum(numbers:List<BigDecimal>)=

fold(numbers,BigDecimal.ZERO){acc,num->acc+num}

Page 254: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

funprod(numbers:List<BigDecimal>)=

fold(numbers,BigDecimal.ONE){acc,num->acc*num}

privatefunfold(

numbers:List<BigDecimal>,

start:BigDecimal,

accumulator:(BigDecimal,BigDecimal)->BigDecimal

):BigDecimal{

varacc=start

for(numinnumbers){

acc=accumulator(acc,num)

}

returnacc

}

//Usage

funBD(i:Long)=BigDecimal.valueOf(i)

valnumbers=listOf(BD(1),BD(2),BD(3),BD(4))

println(sum(numbers))//Prints:10

println(prod(numbers))//Prints:24

Thefoldfunctioniteratesthroughnumbersandupdatesaccusingeachelement.Notethatthefunctionparameterisdefinedlikeanyothertype,anditcanbeusedlikeanyotherfunction.Forexample,wecanhavethevarargfunctiontypeparameter:

funlongOperation(varargobservers:()->Unit){

//...

for(oinobservers)o()

}

InlongOperation,forisusedtoiterateoveralltheobserversandinvokesthemoneafteranother.Thisfunctionallowsmultiplefunctionstobeprovidedasarguments.Here'sanexample:

longOperation({notifyMainView()},{notifyFooterView()})

FunctionsinKotlincanalsoreturnfunctions.Forexample,wecandefineafunctionthatwillcreatecustomerrorhandlerswiththesameerrorloggingbutdifferenttags:

funmakeErrorHandler(tag:String)=fun(error:Throwable){

if(BuildConfig.DEBUG)Log.e(tag,error.message,error)

toast(error.message)

//Othermethods,like:Crashlytics.logException(error)

}

//Usageinproject

valadController=AdController(makeErrorHandler("AdinMainActivity"))

valpresenter=MainPresenter(makeErrorHandler("MainPresenter"))

//Usage

Page 255: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

valexampleHandler=makeErrorHandler("ExampleHandler")

exampleHandler(Error("SomeError"))//Logs:ExampleHandler:SomeError

Thethreemostcommoncaseswhenfunctionsinargumentsareusedare:

ProvidingoperationstofunctionsTheobserver(listener)patternCallbackafterathreadedoperation

Let'slookatthemindetail.

Page 256: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ProvidingoperationstofunctionsAswesawintheprevioussection,sometimeswewanttoextractcommonfunctionalityfromfunctions,buttheydifferinanoperationtheyuse.Insuchsituations,wecanstillextractthisfunctionality,butweneedtoprovideanargumentwithoperationthatdistinguishesthem.Thisway,anycommonpatterncanbeextractedandreused.Forexample,weoftenonlyneedelementsofthelistthatmatchsomepredicate,suchaswhenweonlywanttoshowelementsthatareactive.Classically,thiswouldbeimplementedlikethis:

varvisibleTasks=emptyList<Task>()

for(taskintasks){

if(task.active)

visibleTasks+=task

}

Whileitisacommonoperation,wecanextractthefunctionalityofonlyfilteringsomeelementsaccordingtothepredicatetoseparatethefunctionanduseitmoreeasily:

fun<T>filter(list:List<T>,predicate:(T)->Boolean){

varvisibleTasks=emptyList<T>()

for(eleminlist){

if(predicate(elem))

visibleTasks+=elem

}

}

varvisibleTasks=filter(tasks,{it.active})

Thiswayofusinghigher-orderfunctionsisveryimportantanditwillbedescribedoftenthroughoutthebook,butthisisnottheonlywaythathigher-orderfunctionsareoftenused.

Page 257: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Observer(Listener)patternWeusetheObserver(Listener)patternwhenwewanttoperformoperationswhenaneventoccurs.InAndroiddevelopment,observersareoftensettoviewelements.Commonexamplesareon-clicklisteners,on-touchlisteners,ortextwatchers.InKotlin,wecansetlistenerswithnoboilerplate.Forexample,settinglisteneronbuttonclicklooksasfollows:

button.setOnClickListener({someOperation()})

NotethatthesetOnClickListenerisaJavamethodfromtheAndroidlibrary.Later,wewillseeindetailwhywecanuseitwithlambdaexpression.Thecreationoflistenersisverysimple.Hereisanexample:

varlisteners:List<()->Unit>=emptyList()//1

funaddListener(listener:()->Unit){

listeners+=listener//2

}

funinvokeListeners(){

for(listenerinlisteners)listener()//3

}

1. Here,wecreateanemptylisttoholdalllisteners.2. Wecansimplyaddalistenertothelistenerslist.3. Wecaniteratethroughthelistenersandinvokethemoneafteranother.

Itishardtoimagineasimplerimplementationofthispattern.Thereisanothercommonusecasewhereparameterswithfunctiontypesarecommonlyused--callbackafterathreadedoperation.

Page 258: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

CallbackafterathreadedoperationIfweneedtodoalongoperation,andwedon'twanttomaketheuserwaitforit,thenwehavetostartitinanotherthread.Tobeabletousecallbackafterlongoperationcalledinseparatethread,weneedtopassitasanargument.Here'sanexamplefunction:

funlongOperationAsync(longOperation:()->Unit,callback:()->Unit){

Thread({//1

longOperation()//2

callback()//3

}).start()//4

}

//Usage

longOperationAsync(

longOperation={Thread.sleep(1000L)},

callback={print("Aftersecond")}

//5,Prints:Aftersecond

)

println("Now")//6,Prints:Now

1. Here,wecreateThread.Wealsopassalambdaexpressionthatwewouldliketoexecuteontheconstructorargument.

2. Here,weareexecutingalongoperation.3. Here,westartthecallbackoperationprovidedintheargument.4. startisamethodthatstartsthedefinedthread.5. Isprintedafteroneseconddelay.6. Isprintedimmediately.

Actually,therearesomepopularalternativestousingcallbacks,suchasRxJava.Still,classiccallbacksareincommonuse,andinKotlintheycanbeimplementedwithnoboilerplate.

Thesearethemostcommonusecaseswherehigher-orderfunctionsareused.Allofthemallowustoextractcommonbehavioranddecreaseboilerplate.Kotlinallowsafewmoreimprovementsregardinghigher-orderfunctions.

Page 259: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

CombinationofnamedargumentsandlambdaexpressionsUsingdefaultnamedargumentsandlambdaexpressionscanbereallyusefulinAndroid.Let'slookatsomeotherpracticalAndroidexamples.Let'ssupposewehaveafunctionthatdownloadselementsandshowsthemtouser.Wewilladdafewparameters:

onStart:ThiswillbecalledbeforethenetworkoperationonFinish:Thiswillbecalledafterthenetworkoperation

fungetAndFillList(onStart:()->Unit={},

onFinish:()->Unit={}){

//code

}

Then,wecanshowandhideloadingspinnerinonStartandonFinish:

getAndFillList(

onStart={view.loadingProgress=true},

onFinish={view.loadingProgress=false}

)

IfwestartitfromswipeRefresh,thenwejustneedtohideitwhenitfinishes:

getAndFillList(onFinish={view.swipeRefresh.isRefreshing=

false})

Ifwewanttomakeaquietrefresh,thenwejustcallthis:

getAndFillList()

Namedargumentsyntaxandlambdaexpressionsareaperfectmatchformulti-purposefunctions.Thisconnectsboththeabilitytochoosetheargumentswewanttoimplementandtheoperationsthatshouldbeimplemented.Ifafunctioncontainsmorethanonefunctiontypeparameter,theninmostcases,itshouldbeusedbynamedargumentsyntax.Thisisbecauselambdaexpressionsarerarelyself-explanatorywhenmorethanoneisusedasarguments.

Page 260: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

LastlambdainargumentconventionInKotlin,higher-orderfunctionsarereallyimportant,andsoitisalsoimportanttomaketheirusageascomfortableaspossible.ThisiswhyKotlinintroducedaspecialconventionthatmakeshigher-orderfunctionsmoresimpleandclear.Itworksthisway:ifthelastparameterisafunction,thenwecandefinealambdaexpressionoutsideofthebrackets.Let'sseehowitlooksifweuseitwiththelongOperationAsyncfunction,whichisdefinedasfollows:

funlongOperationAsync(a:Int,callback:()->Unit){

//...

}

Thefunctiontypeisinthelastpositioninthearguments.Thisiswhywecanexecuteitthisway:

longOperationAsync(10){

hideProgress()

}

Thankstothelastlambdainargumentconvention,wecanlocatethelambdaafterthebrackets.Itlooksasifitisoutsidethearguments.

Asanexample,let'sseehowtheinvocationofcodeinanotherthreadcanbedoneinKotlin.ThestandardwayofstartinganewthreadinKotlinisbyusingthethreadfunctionfromKotlinstandardlibrary.Itsdefinitionisasfollows:

publicfunthread(

start:Boolean=true,

isDaemon:Boolean=false,

contextClassLoader:ClassLoader?=null,

name:String?=null,

priority:Int=-1,

block:()->Unit):Thread{

//implementation

}

Aswecansee,theblockparameter,whichtakesoperationsthatshouldbeinvokedasynchronously,isinthelastposition.Allotherparametershaveadefaultargumentdefined.Thatiswhywecanusethethreadfunctioninthisway:

thread{/*code*/}

Page 261: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Thethreaddefinitionhaslotsofotherarguments,andwecansetthemeitherbyusingnamedargumentsyntaxorjustbyprovidingthemoneafteranother:

thread(name="SomeThread"){/*...*/}

thread(false,false){/*...*/}

Thelastlambdainargumentconventionissyntacticsugar,butitmakesitmucheasiertousehigher-orderfunctions.Thesearethetwomostcommoncaseswherethisconventionreallymakesadifference:

NamedcodesurroundingProcessingdatastructuresusingLINQ-style

Let'slookatthemclosely.

Page 262: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

NamedcodesurroundingSometimesweneedtomarksomepartofthecodetobeexecutedindifferentway.Thethreadfunctionisthiskindofsituation.Weneedsomecodetobeexecutedasynchronously,sowesurrounditwithbracketstartingfromthethreadfunction:

thread{

operation1()

operation2()

}

Fromtheoutside,itlooksasifitisapartofcodethatissurroundedbyablocknamedthread.Let'slookatanotherexample.Let'ssupposethatwewanttologtheexecutiontimeofacertaincodeblock.Asahelper,wewilldefinetheaddLogsfunction,whichwillprintlogstogetherwiththeexecutiontime.Wewilldefineitinthefollowingway:

funaddLogs(tag:String,f:()->Unit){

println("$tagstarted")

valstartTime=System.currentTimeMillis()

f()

valendTime=System.currentTimeMillis()

println("$tagfinished.Ittook"+(endTime-startTime))

}

Thefollowingistheusageofthefunction:

addLogs("Someoperations"){

//Operationswearemeasuring

}

Here'sanexampleofitsexecution:

addLogs("Sleeper"){

Thread.sleep(1000)

}

Onexecutingtheprecedingcode,thefollowingoutputispresented:

Sleeperstarted

Sleeperfinished.Ittook1001

Theexactnumberofprintedmillisecondsmaydifferalittlebit.

Page 263: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ThispatternisreallyusefulinKotlinprojectsbecausesomepatternsareconnectedtoblocksofcode.Forexample,itiscommontocheckwhethertheversionoftheAPIisafterAndroid5.xLollipopbeforetheexecutionoffeaturesthatneedatleastthisversiontowork.Tocheckit,weusedthefollowingcondition:

if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP){

//Operations

}

ButinKotlin,wecanjustextractthefunctioninthefollowingway:

funifSupportsLolipop(f:()->Unit){

if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP)

{

f()

}

}

//Usage

ifSupportsLollipop{

//Operation

}

Thisisnotonlycomfortable,butalsoitisloweringredundancyinthecode.Thisisoftenreferredasverygoodpractice.Alsonotethatthisconventionallowsustodefinecontrolstructuresthatworkinasimilarwaytostandardones.Wecan,forexample,defineasimplecontrolstructurethatisrunningaslongasthestatementinthebodydoesnotreturnanerror.Hereisthedefinitionandusage:

funrepeatUntilError(code:()->Unit):Throwable{

while(true){

try{

code()

}catch(t:Throwable){

returnt

}

}

}

//Usage

valtooMuchAttemptsError=repeatUntilError{

attemptLogin()

}

Anadditionaladvantageisthatourcustomdatastructurecanreturnavalue.Theimpressivepartisthatisdoesn'tneedanyextralanguagesupport,andwecandefinenearlyanycontrolstructurewewant.

Page 264: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ProcessingdatastructuresusingLINQstyleWe'vealreadymentionedthatKotlinallowsLINQ-styleprocessing.Thelastlambdainargumentconventionisanothercomponentthataidsitsreadability.Forexample,lookatthefollowingcode:

strings.filter{it.length==5}.map{it.toUpperCase()}

Itismorereadablethannotationthatdoesnotusethelastlambdainargumentconvention:

strings.({s->s.length==5}).map({s->s.toUpperCase()})

Again,thisprocessingwillbediscussedindetaillater,inChapter7,ExtensionFunctionsandProperties,butfornowwehavelearnedabouttwofeaturesthatimproveitsreadability(thelastlambdainargumentconventionandtheimplicitnameofasingleparameter).

ThelastlambdainargumentconventionisoneoftheKotlinfeaturesthatwasintroducedtoimprovetheuseoflambdaexpressions.Therearemoresuchimprovements,andhowtheyworktogetherisimportanttomaketheuseofhigher-orderfunctionssimple,readable,andefficient.

Page 265: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

JavaSAMsupportinKotlinItisreallyeasytousehigher-orderfunctionsinKotlin.TheproblemisthatweoftenneedtointeroperatewithJava,whichnativelydoesn'tsupportit.Itachievessubstitutionbyusinginterfaceswithonlyonemethod.ThiskindofinterfaceiscalledaSingleAbstractMethod(SAM)orfunctionalinterface.Thebestexampleofsituationinwhichweneedtosetupafunctionthisway,iswhenweareusingsetOnClickListeneronaViewelement.InJava(until8)therewasnosimplerwaythanbyusingananonymousinnerclass:

//Java

button.setOnClickListener(newOnClickListener(){

@OverridepublicvoidonClick(Viewv){

//Operation

}

});

Intheprecedingexample,theOnClickListenermethodistheSAM,becauseitcontainsonlyasinglemethod,onClick.WhileSAMsarereallyoftenusedasareplacementforfunctiondefinitions,Kotlinalsogeneratesaconstructorforthemthatcontainsthefunctiontypeasaparameter.ItiscalledaSAMconstructor.ASAMconstructorallowsustocreateaninstanceofaJavaSAMinterfacejustbycallingitsnameandpassingafunctionliteral.Here'sanexample:

button.setOnClickListener(OnClickListener{

/*...*/

})

Afunctionliteralisanexpressionthatdefinesunnamedfunction.InKotlin,therearetwokindsoffunctionliteral:

1.Anonymousfunctions2.Lambdaexpressions

BothKotlinfunctionliteralhavealreadybeendescribed:

vala=fun(){}//Anonymousfunction

valb={}//Lambdaexpression

Page 266: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Evenbetter,foreachJavamethodthattakesaSAM,theKotlincompilerisgeneratingaversionthatinsteadtakesafunctionasanargument.ThisiswhywecansetOnClickListenerasfollows:

button.setOnClickListener{

//Operations

}

RememberthattheKotlincompilerisgeneratingSAMconstructorsandfunctionmethodsonlyforJavaSAMs.ItisnotgeneratingSAMconstructorsforKotlininterfaceswithasinglemethod.ItisbecausetheKotlincommunityispushingtousefunctiontypesandnotSAMsinKotlincode.WhenafunctioniswritteninKotlinandincludesaSAM,thenwecannotuseitasJavamethodswithSAMonparameter:

interfaceOnClick{

funcall()

}

funsetOnClick(onClick:OnClick){

//...

}

setOnClick{}//1.Error

1. ThisdoesnotworkbecausethesetOnClickfunctioniswritteninKotlin.

InKotlin,interfacesshouldn'tbeusedthisway.ThepreferredwayistousefunctiontypesinsteadofSAMs:

funsetOnClick(onClick:()->Unit){

//...

}

setOnClick{}//Works

TheKotlincompilergeneratesaSAMconstructorforeverySAMinterfacedefinedinJava.ThisinterfaceonlyincludesthefunctiontypethatcansubstituteaSAM.Lookatthefollowinginterface:

//Java,insideViewclass

publicinterfaceOnClickListener{

voidonClick(Viewv);

}

WecanuseitinKotlinthisway:

valonClick=View.OnClickListener{toast("Clicked")}

Page 267: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Orwecanprovideitasfunctionargument:

funaddOnClickListener(d:View.OnClickListener){}

addOnClickListener(View.OnClickListener{v->println(v)})

HerearemoreexamplesoftheJavaSAMlambdainterfaceandmethodsfromAndroid:

view.setOnLongClickListener{/*...*/;true}

view.onFocusChange{view,b->/*...*/}

valcallback=Runnable{/*...*/}

view.postDelayed(callback,1000)

view.removeCallbacks(callback)

Andhere'ssomeexamplesfromRxJava:

observable.doOnNext{/*...*/}

observable.doOnEach{/*...*/}

Now,let'slookathowaKotlinalternativetoSAMdefinitioncanbeimplemented.

Page 268: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

NamedKotlinfunctiontypesKotlindoesnotsupportSAMconversionsoftypesdefinedinKotlin,becausethepreferredwayistousefunctiontypesinstead.ButSAMhassomeadvantagesoverclassicfunctiontypes:namedandnamedparameters.Itisgoodtohavethefunctiontypenamedwhenitsdefinitionislongoritispassedmultipletimesasanargument.Itisgoodtohavenamedparameterswhenitisnotclearwhateachparametermeansjustbyitstype.

Intheupcomingsections,wearegoingtoseethatitispossibletonameboththeparametersandthewholedefinitionofafunctiontype.Itcanbedonewithtypealiasesandnamedparametersinthefunctiontype.Thisway,itispossibletohavealltheadvantagesofSAMwhilestickingwithfunctiontypes.

Page 269: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

NamedparametersinfunctiontypeUntilnow,we'veonlyseendefinitionsoffunctiontypeswhereonlythetypeswerespecified,butnotparameternames.Parameternameshavebeenspecifiedinfunctionliterals:

funsetOnItemClickListener(listener:(Int,View,View)->Unit){

//code

}

setOnItemClickListener{position,view,parent->/*...*/}

Theproblemcomeswhentheparametersarenotself-explanatory,andthedeveloperdoesnotknowwhattheparametersmean.WithSAMsthereweresuggestions,whileinthefunctiontypedefinedinthepreviousexample,theyarenotreallyhelpful:

Thesolutionistodefinefunctiontypewithnamedparameters.Hereiswhatitlookslike:

(position:Int,view:View,parent:View)->Unit

ThebenefitofthisnotationisthattheIDEsuggeststhesenamesasthenamesoftheparametersinthefunctionliteral.Becauseofthis,programmercanavoidanyconfusion:

Page 270: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Theproblemoccurswhenthesamefunctiontypeisusedmultipletimes,thenitisnoteasytodefinethoseparametersforeachdefinition.Inthatsituation,adifferentKotlinfeatureisused-theonewedescribeinnextsection--typealias.

Page 271: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

TypealiasFromversion1.1,Kotlinhashadafeaturecalledtypealias,whichallowsustoprovidealternativenamesforexistingtypes.HereisanexampleofatypealiasdefinitionwherewehavemadealistofUsers:

dataclassUser(valname:String,valsurname:String)

typealiasUsers=List<User>

Thisway,wecanaddmoremeaningfulnamestoexistingdatatypes:

typealiasWeight=Double

typealiasLength=Int

Typealiasesmustbedeclaredatthetoplevel.Avisibilitymodifiercanbeappliedtoatypealiastoadjustitsscope,buttheyarepublicbydefault.Thismeansthatthetypealiasesdefinedpreviouslycanbeusedwithoutanylimitations:

valusers:Users=listOf(

User("Marcin","Moskala"),

User("Igor","Wojda")

)

funcalculatePrice(length:Length){

//...

}

calculatePrice(10)

valweight:Weight=52.0

vallength:Length=34

Keepinmindthataliasesareusedtoimprovecodereadability,andtheoriginaltypescanstillbeusedinterchangably:

typealiasLength=Int

varintLength:Int=17

vallength:Length=intLength

intLength=length

Anotherapplicationoftypealiasistoshortenlonggenerictypesandgivethemmoremeaningfulnames.Thisimprovescodereadabilityandconsistencywhenthesametypeisusedinmultipleplacesinthecode:

typealiasDictionary<V>=Map<String,V>

Page 272: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

typealiasArray2D<T>=Array<Array<T>>

Typealiasesareoftenusedtonamefunctiontypes:

typealiasAction<T>=(T)->Unit

typealiasCustomHandler=(Int,String,Any)->Unit

Wecanusethemtogetherwithfunctiontypeparameternames:

typealiasOnElementClicked=(position:Int,view:View,parent:View)->Unit

Andthenwegetparametersuggestions:

Let'slookatanexampleofhowfunctiontypesnamedbytypealiascanbeimplementedbyclass.Parameternamesfromfunctiontypesarealsosuggestedasmethodparametersnamesinthisexample:

typealiasOnElementClicked=(position:Int,view:View,parent:View)->Unit

classMainActivity:Activity(),OnElementClicked{

overridefuninvoke(position:Int,view:View,parent:View){

//code

}

}

Thesearethemainreasonswhyweareusingnamedfunctiontypes:

Namesareoftenshorterandeasierthanwholefunctiontypedefinitions

Page 273: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Whenwearepassingfunctions,afterchangingtheirdefinitions,wedon'thavetochangeiteverywhereifweareusingtypealiasesItiseasiertohavedefinedparameternameswhenweusetypealiases

Thesetwofeatures(namedparameterinfunctiontypesandtypealiases)combinedarethereasonswhythereisnoneedtodefineSAMsinKotlin--alltheadvantagesofSAMsoverfunctiontypes(nameandnamedparameters)canbeachievedwithnamedparametersinfunctiontypedefinitionsandtypealiases.ThisisanotherexampleofhowKotlinsupportsfunctionalprogramming.

Page 274: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

UnderscoreforunusedvariablesInsomecases,wearedefiningalambdaexpressionthatdoesnotuseallitsparameters.Whenweleavethemnamed,thentheymightbedestructingaprogrammerwhoisreadingthislambdaexpressionandtryingtounderstanditspurpose.Let'slookatthefunctionthatisfilteringeverysecondelement.Thesecondparameteristheelementvalue,anditisunusedinthisexample:

list.filterIndexed{index,value->index%2==0}

Topreventmisunderstanding,therearesomeconventionsused,suchastheignoringtheparameternames:

list.filterIndexed{index,ignored->index%2==0}

Becausetheseconventionswereunclearandproblematic,Kotlinintroducedunderscorenotation,whichisusedasareplacementforthenamesofparametersthatarenotused:

list.filterIndexed{index,_->index%2==0}

Thisnotationissuggested,andthereisawarningdisplayedwhenalambdaexpressionparameterisunused:

Page 275: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

DestructuringinlambdaexpressionsInChapter4,ClassesandObjects,we'veseenhowobjectscanbedestructuredintomultiplepropertiesusingdestructuringdeclarations:

dataclassUser(valname:String,valsurname:String,valphone:String)

val(name,surname,phone)=user

Sinceversion1.1,Kotlincanusedestructuringdeclarationssyntaxforlambdaparameters.Tousethem,youshoulduseparenthesesthatincludealltheparametersthatwewanttodestructureinto:

valshowUser:(User)->Unit={(name,surname,phone)->

println("$name$surnamehavephonenumber:$phone")

}

valuser=User("Marcin","Moskala","+48123456789")

showUser(user)

//MarcinMoskalahavephonenumber:+48123456789

Kotlin'sdestructingdeclarationisposition-based,asopposedtothepropertyname-baseddestructuringdeclarationthatcanbefound,forexample,inTypeScript.Inposition-baseddestructingdeclarations,theorderofpropertiesdecideswhichpropertyisassignedtowhichvariable.Inpropertyname-baseddestructuring,itisdeterminedbythenamesofvariables:

//TypeScript

constobj={first:'Jane',last:'Doe'};

const{last,first}=obj;

console.log(first);//Prints:Jane

console.log(last);//Prints:Doe

Bothsolutionshaveitsprosandcons.Position-baseddestructingdeclarationsaresecuredforrenamingaproperty,buttheyarenotsafeforpropertyreordering.Name-baseddestructuringdeclarationsaresafeforpropertyreorderingbutarevulnerableforpropertyrenaming.

Destructuringdeclarationscanbeusedmultipletimesinasinglelambdaexpression,anditcanbeusedtogetherwithnormalparameters:

Page 276: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

valf1:(Pair<Int,String>)->Unit={(first,second)->

/*code*/}//1

valf2:(Int,Pair<Int,String>)->Unit={index,(f,s)->

/*code*/}//2

valf3:(Pair<Int,String>,User)->Unit={(f,s),(name,

surname,tel)->/*code*/}//3

1. DeconstructionofPair2. DeconstructionofPairandotherelement3. Multipledeconstructionsinsinglelambdaexpression

Notethatwecandestructureaclassintolessthanallcomponents:

valf:(User)->Unit={(name,surname)->/*code*/}

Underscorenotationisallowedindestructuringdeclarations.Itismostoftenusedtogettothefurthercomponents:

valf:(User)->Unit={(name,_,phone)->/*code*/}

valthird:(List<Int>)->Int={(_,_,third)->third}

Itispossibletospecifythetypeofthedestructuredparameter:

valf={(name,surname):User->/*code*/}//1

1. Thetypeisinferredfromthelambdaexpression

Also,parametersdefinedbydestructuringdeclaration:

valf={(name:String,surname:String):User->

/*code*/}//1

valf:(User)->Unit={(name,surname)->

/*code*/}//2

1. Thetypeisinferredfromthelambdaexpression.2. Thetypecannotbeinferredbecausethereisnotenoughinformationabout

typesinsidethelambdaexpression.

Thisallmakesdestructuringinlambdasareallyusefulfeature.Let'slookatsomemostcommonusecasesinAndroidwheredeconstructioninlambdasisused.ItisusedtoprocesstheelementsofMapbecausetheyareoftypeMap.Entry,whichcanbedestructedtothekeyandvalueparameters:

valmap=mapOf(1to2,2to"A")

valtext=map.map{(key,value)->"$key:$value"}

println(text)//Prints:[1:2,2:A]

Page 277: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Similarly,listsofpairscanbedestructed:

vallistOfPairs=listOf(1to2,2to"A")

valtext=listOfPairs.map{(first,second)->

"$firstand$second"}

println(text)//Prints:[1and2,2andA]

Destructuringdeclarationsarealsousedwhenwewanttosimplifydataobjectsprocessing:

funsetOnUserClickedListener(listener:(User)->Unit){

listView.setOnItemClickListener{_,_,position,_->

listener(users[position])

}

}

setOnUserClickedListener{(name,surname)->

toast("Clickedto$name$surname")

}

Thisisespeciallyusefulinlibrariesthatareusedtoasynchronouslyprocesselements(suchasRxJava).Theirfunctionsaredesignedtoprocesssingleelements,andifwewantmultipleelementstobeprocessed,thenweneedtopacktheminPair,Triple,orsomeotherdataclassanduseadestructuringdeclarationoneachstep:

getQuestionAndAnswer()

.flatMap{(question,answer)->

view.showCorrectAnswerAnimationObservable(question,answer)

}

.subscribe({(question,answer)->/*code*/})

Page 278: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

InlinefunctionsHigher-orderfunctionsareveryhelpfulandtheycanreallyimprovethereusabilityofcode.However,oneofthebiggestconcernsaboutusingthemisefficiency.Lambdaexpressionarecompiledtoclasses(oftenanonymousclasses),andobjectcreationinJavaisaheavyoperation.Wecanstillusehigher-orderfunctionsinaneffectivewaywhilekeepingallthebenefitsbymakingfunctionsinline.

Theconceptofinlinefunctionsisprettyold,anditismostlyrelatedtoC++orC.Whenafunctionismarkedasinline,duringcodecompilationthecompilerwillreplaceallthefunctioncallswiththeactualbodyofthefunction.Also,lambdaexpressionsprovidedasargumentsarereplacedwiththeiractualbody.Theywillnotbetreatedasfunctions,butasactualcode.Thisismakesbytecodelonger,butruntimeexecutionismuchmoreefficient.Later,wewillseethatnearlyallhigher-orderfunctionsfromstandardlibraryaremarkedasinline.Let'slookattheexample.SupposewemarkedtheprintExecutionTimefunctionwiththeinlinemodifier:

inlinefunprintExecutionTime(f:()->Unit){

valstartTime=System.currentTimeMillis()

f()

valendTime=System.currentTimeMillis()

println("Ittook"+(endTime-startTime))

}

funmeasureOperation(){

printExecutionTime{

longOperation()

}

}

WhenwecompileanddecompilemeasureOperation,wearegoingtofindoutthatthefunctioncallisreplacedwithitsactualbody,andtheparameterfunctioncallisreplacedbythelambdaexpression'sbody:

funmeasureOperation(){

valstartTime=System.currentTimeMillis()//1

longOperation()//2

valendTime=System.currentTimeMillis()

println("Ittook"+(endTime-startTime))

}

Page 279: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

1. CodefromprintExecutionTimewasaddedtomeasureOperationfunctionbody.2. Codelocatedinsidethelambdawaslocatedonitscall.Ifthefunctionused

itmultipletimes,thenthecodewouldreplaceeachcall.

ThebodyofprintExecutionTimecanstillbefoundinthecode.Itwasskippedtomaketheexamplemorereadable.Itiskeptinthecodebecauseitmightbeusedaftercompilation,forexample,ifthiscodeisaddedtoaprojectasalibrary.Whatismore,thisfunctionwillstillworkasinlinewhenusedbyKotlin.

Whilethereisnoneedtocreateclassesforlambdaexpressions,inlinefunctionscanspeeduptheexecutionoffunctionswithfunctionparameters.Thisdifferenceissoimportantthatitisrecommendedtousetheinlinemodifierforallshortfunctionswithatleastonefunctionparameter.Unfortunately,usingtheinlinemodifieralsohasitsbadsides.Thefirst,we'vealreadymentioned--theproducedbytecodeislonger.Thisisbecausefunctioncallsarereplacedbyfunctionbodiesandbecauselambdacallsinsidethisbodyarereplacedwiththebodyofthefunctionliteral.Also,inlinefunctionscannotberecursiveandtheycannotusefunctionsorclassesthathavemorerestrictivevisibilitymodifierthanthislambdaexpression.Forexample,publicinlinefunctionscannotuseprivatefunctions.Thereasonisthatitcouldleadtotheinjectionofcodeintofunctionsthatcannotusethem.Thiswouldleadtoacompilationerror.Topreventit,Kotlindoesnotpermittheuseofelementswithlessrestrictivemodifiersthanthelambdaexpressioninwhichtheyareplaced.Here'sanexample:

internalfunsomeFun(){}

inlinefuninlineFun(){

someFun()//ERROR

}

Infact,itispossibleinKotlintouseelementswithmorerestrictivevisibilityininlinefunctionsifwesuppressthiswarning,butthisisbadpracticeanditshouldneverbeusedthisway:

//Tester1.kt

funmain(args:Array<String>){a()}

//Tester2.kt

inlinefuna(){b()}

privatefunb(){print("B")}

Howisitpossible?Fortheinternalmodifieritissimpler,because

Page 280: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

theinternalmodifierispublicunderthehood.Forprivatefunctions,thereisanadditionalaccess$bfunctioncreatedthathaspublicvisibilityandthatisonlyinvokingthebfunction:

publicstaticfinalvoidaccess$b(){b();}

Thisbehaviorispresentedherejusttoexplainwhylessrestrictivemodifierscansometimesbeusedinsideinlinefunctions(thesesituationscanbefoundinKotlinstandardlibraryinKotlin1.1).Intheprojects,weshoulddesignelementsinsuchawaythatthereisnoneedtousesuchsuppressions.

Anotherproblemislessintuitive.Whilenolambdahasbeencreated,wecannotpassparametersthatareofthefunctiontypetoanotherfunction.Hereisanexample:

funboo(f:()->Int){

//...

}

inlinefunfoo(f:()->Int){

boo(f)//ERROR,1

}

Whenfunctionisinline,thenitsfunctionargumentscannotbepassedtofunctionthatarenotinline.

Thisdoesn'tworkbecausenofparameterhasbeencreated.Ithasjustbeendefinedtobereplacedbythefunctionliteralbody.Thisiswhyitcannotbepassedtoanotherfunctionasanargument.

Thesimplestwaytodealwithitisbymakingtheboofunctioninlineaswell.ThenitwillbeOK.Inmostcases,wecannotmaketoomanyfunctionsinline.Hereareafewreasonswhy:

Theinlinefunctionsshouldbeusedforsmallerfunctions.Ifwearemakinginlinefunctionsthatareusingotherinlinefunctions,thenitcanleadtoalargestructurebeinggeneratedaftercompilation.Thisisaproblembothbecauseofcompilationtimeandbecauseoftheresultingcode'ssize.Whileinlinefunctionscannotuseelementwithvisibilitymodifiersmorestrictthantheonetheyhave,itwouldbeaproblemifwewouldliketouse

Page 281: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

theminlibrarieswhereasmanyfunctionsaspossibleshouldbeprivatetoprotecttheAPI.

Thesimplestwaytodealwiththisproblemisbymakingfunctionparametersthatwedowanttopasstoanotherfunctionnoinline.

Page 282: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ThenoinlinemodifierThenoinlineisamodifierforfunctiontypeparameters.Itmakesaspecificargumenttreatedasnormalfunctiontypeparameter(itscallsarenotreplacedwiththefunctionliteralbody).Let'slookatanoinlineexample:

funboo(f:()->Unit){

//...

}

inlinefunfoo(before:()->Unit,noinlinef:()->Unit){//1

before()//2

boo(f)//3

}

1. Thenoinlineannotationmodifierbeforeparameterf.2. Thebeforefunctionwillbereplacedbythebodyofthelambdaexpression

usedasanargument.3. fisnoinlinesoitcanbepassedtotheboofunction.

Twomainreasonstousenoinlinemodifierareasfollows:

WhenweneedtopassaspecificlambdatosomeotherfunctionWhenwearecallingthelambdaintensivelyandwedon'twanttoswellthecodetoomuch

Notethatwhenwemakeallfunctionparametersnoinline,thentherewillbenearlynoperformanceimprovementfrommakingthefunctionsinline.Whileitisunlikelythatusinginlinewillbebeneficial,thecompilerwillshowawarning.Thisiswhy,inmostcases,noinlineisonlyusedwhentherearemultiplefunctionparametersandweonlyapplyittosomeofthem.

Page 283: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Non-localreturnsFunctionswithfunctionparametersmightactsimilarlytonativestructures(suchasloops).We'vealreadyseentheifSupportsLolipopfunctionandtherepeatUntilErrorfunction.AnevenmorecommonexampleistheforEachmodifier.Itisanalternativetotheforcontrolstructure,anditcallsaparameterfunctionwitheachelementoneafteranother.Thisishowitcouldbeimplemented(thereisaforEachmodifierinKotlinstandardlibrary,butwewillseeitlaterbecauseitincludeselementsthathavenotyetbeenpresented):

funforEach(list:List<Int>,body:(Int)->Unit){

for(iinlist)body(i)

}

//Usage

vallist=listOf(1,2,3,4,5)

forEach(list){print(it)}//Prints:12345

ThebigproblemisthatinsidetheforEachfunctiondefinedthiswaywecannotreturnfromouterfunction.Forexample,thisishowwecouldimplementthemaxBoundedfunctionusingaforloop:

funmaxBounded(list:List<Int>,upperBound:Int,lowerBound:Int):

Int{

varcurrentMax=lowerBound

for(iinlist){

when{

i>upperBound->returnupperBound

i>currentMax->currentMax=i

}

}

returncurrentMax

}

IfwewanttotreatforEachasanalternativetoaforloop,thensimilarpossibilityshouldbeallowedthere.Theproblemisthatthesamecode,butwithforEachusedinsteadofforloop,wouldnotcompile:

Page 284: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Thereasonisrelatedtohowthecodeiscompiled.Wehavealreadydiscussedthatlambdaexpressionsarecompiledtoclassofanonymousobjectswithamethodthatincludesthedefinedcode,andovertherewecannotreturnfromthemaxBoundedfunctionbecauseweareinadifferentcontext.

WeencounterasituationwhentheforEachfunctionismarkedasinline.Aswehavealreadymentioned,thebodyofthisfunctionreplacesitscallsduringcompilation,andallofthefunctionsfromtheparametersarereplacedwiththeirbody.So,thereisnoproblemwithusingthereturnmodifierthere.Then,ifwemakeforEachinline,wecanusereturninsidethelambdaexpression:

inlinefunforEach(list:List<Int>,body:(Int)->Unit){

for(iinlist)body(i)

}

funmaxBounded(list:List<Int>,upperBound:Int,

lowerBound:Int):Int{

varcurrentMax=lowerBound

forEach(list){i->

when{

i>upperBound->returnupperBound

i>currentMax->currentMax=i

}

}

returncurrentMax

}

ThisishowthemaxBoundedfunctionhascompiledinKotlin,andthecodelookslikethis(aftersomeclean-upandsimplification)whenitisdecompiledtoJava:

publicstaticfinalintmaxBounded(@NotNullListlist,

intupperBound,intlowerBound){

intcurrentMax=lowerBound;

Iteratoriter=list.iterator();

while(iter.hasNext()){

inti=((Number)iter.next()).intValue();

if(i>upperBound){

returnupperBound;//1

}

Page 285: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

if(i>currentMax){

currentMax=i;

}

}

returncurrentMax;

}

Intheprecedingcode,returnisimportant--itwasdefinedinthelambdaexpression,anditisreturningfromthemaxBoundedfunction.

Thereturnmodifierusedinsidethelambdaexpressionoftheinlinefunctioniscalledanon-localreturn.

Page 286: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

LabeledreturninlambdaexpressionsLet'slookatacaseinwhichweneedtoreturnfromalambdaexpressionandnotfromafunction.Wecandothisusinglabels.Hereisanexampleofareturnfromalambdaexpressionusinglabels:

inlinefun<T>forEach(list:List<T>,body:(T)->Unit){//1

for(iinlist)body(i)

}

funprintMessageButNotError(messages:List<String>){

forEach(messages)messageProcessor@{//2

if(it=="ERROR")return@messageProcessor//3

print(it)

}

}

//Usage

vallist=listOf("A","ERROR","B","ERROR","C")

processMessageButNotError(list)//Prints:ABC

1. ThisisgenericimplementationofforEachfunction,wherelistwithanytypecanbeprocessed.

2. WedefinelabelforlambdaexpressioninsideforEachargument.3. Wereturnfromlambdaexpressionspecifiedbylabel.

AnotherKotlinfeatureisthatlambdaexpressionsthataredefinedasfunctionargumentshaveadefaultlabelwhosenameisthesameasthefunctioninwhichtheyaredefined.Thislabeliscalledanimplicitlabel.WhenwewanttoreturnfromalambdaexpressiondefinedinaforEachfunction,[email protected]'slookatanexample:

inlinefun<T>forEach(list:List<T>,body:(T)->Unit){//1

for(iinlist)body(i)

}

funprocessMessageButNotError(messages:List<String>){

forEach(messages){

if(it=="ERROR")return@forEach//1

process(it)

}

}

//Usage

vallist=listOf("A","ERROR","B","ERROR","C")

processMessageButNotError(list)//Prints:ABC

Page 287: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

1. Implicitlabelnameistakenfromfunctionname.

NotethatwhiletheforEachfunctionisinline,wecanalsouseanon-localreturntoreturnfromtheprocessMessageButNotErrorfunction:

inlinefun<T>forEach(list:List<T>,body:(T)->Unit){

for(iinlist)body(i)

}

funprocessMessageButNotError(messages:List<String>){

forEach(messages){

if(it=="ERROR")return

process(it)

}

}

//Usage

vallist=listOf("A","ERROR","B","ERROR","C")

processMessageButNotError(list)//Prints:A

Let'smoveontoamorecomplexexampleofusingnon-localreturnlabels.Let'ssupposethatwehavetwoforEachloops,oneinsideanother.Whenweuseanimplicitlabel,itwillreturnfromthedeeperloop.Inourexample,wecanuseittoskiptheprocessingofthespecificmessage:

inlinefun<T>forEach(list:List<T>,body:(T)->Unit){//1

for(iinlist)body(i)

}

funprocessMessageButNotError(conversations:List<List<String>>){

forEach(conversations){messages->

forEach(messages){

if(it=="ERROR")return@forEach//1.

process(it)

}

}

}

//Usage

valconversations=listOf(

listOf("A","ERROR","B"),

listOf("ERROR","C"),

listOf("D")

)

processMessageButNotError(conversations)//ABCD

1. ThiswillreturnfromthelambdadefinedintheforEachfunctionthatalsotakesmessagesasanargument.

Wecannotreturnfromanotherlambdaexpressioninthesamecontextusingimplicitlabel,becauseitisshadowedbyadeeperimplicitlabel.

Page 288: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Inthesesituations,weneedtouseanon-localimplicitlabelreturn.Itisonlypermissiblewithinlinefunctionparameters.Inourexample,whileforEachisinline,wecanreturnfromafunctionliteralthisway:

inlinefun<T>forEach(list:List<T>,body:(T)->Unit){//1

for(iinlist)body(i)

}

funprocessMessageButNotError(conversations:List<List<String>>){

forEach(conversations)conv@{messages->

forEach(messages){

if(it=="ERROR")return@conv//1.

print(it)

}

}

}

//Usage

valconversations=listOf(

listOf("A","ERROR","B"),

listOf("ERROR","C"),

listOf("D")

)

processMessageButNotError(conversations)//AD

1. ThiswillreturnfromthelambdadefinedinforEachcalledonconversations.

Wecanalsojustuseanon-localreturn(areturnwithoutanylabels)tofinishtheprocessing:

inlinefun<T>forEach(list:List<T>,body:(T)->Unit){//1

for(iinlist)body(i)

}

funprocessMessageButNotError(conversations:List<List<String>>){

forEach(conversations){messages->

forEach(messages){

if(it=="ERROR")return//1.

process(it)

}

}

}

1. ThiswillreturnfromtheprocessMessageButNotErrorfunctionandfinishtheprocessing.

Page 289: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

CrossinlinemodifierSometimesweneedtousefunctiontypeparametersfrominlinefunctionsnotdirectlyinthefunctionbody,butinanotherexecutioncontext,suchasalocalobjectoranestedfunction.Butstandardfunctiontypeparametersofinlinefunctionsarenotallowedtobeusedthisway,becausetheyareallowingnon-localreturns,anditshouldnotbeallowedifthisfunctioncouldbeusedinsideanotherexecutioncontext.Toinformthecompilerthatnon-localreturnsarenotallowed,thisparametermustbeannotatedascrossinline.Thenitwillactlikeasubstitutionthatweareexpectinginaninlinefunction,evenwhenitisusedinsideanotherlambdaexpression:

funboo(f:()->Unit){

//...

}

inlinefunfoo(crossinlinef:()->Unit){

boo{println("A");f()}

}

funmain(args:Array<String>){

foo{println("B")}

}

Thiswillbecompiledasfollows:

funmain(args:Array<String>){

boo{println("A");println("B")}

}

Whilenopropertyhasbeencreatedwiththefunction,itisnotpossibletopassthecrossinlineparametertoanotherfunctionasanargument:

Let'slookatapracticalexample.InAndroid,wedon'tneedContexttoexecuteanoperationonthemainthreadoftheapplicationbecausewecangetamainloopusingthegetMainLooperstaticfunctionfromtheLooperclass.Therefore,wecanwriteatop-levelfunctionthatwillallowasimplethreadchangeintothemainthread.Tooptimizeit,wearefirstcheckingifthecurrentthreadisnotthemain

Page 290: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

thread.Whenitis,thentheactionisjustinvoked.Whenitisnot,thenwecreateahandlerthatoperatesonthemainthreadandapostoperationtoinvokeitfromthere.Tomaketheexecutionofthisfunctionfaster,wearegoingtomaketherunOnUiThreadfunctioninline,butthentoallowtheactioninvocationfromanotherthread,weneedtomakeitcrossinline.Hereisanimplementationofthisdescribedfunction:

inlinefunrunOnUiThread(crossinlineaction:()->Unit){

valmainLooper=Looper.getMainLooper()

if(Looper.myLooper()==mainLooper){

action()

}else{

Handler(mainLooper).post{action()}//1

}

}

1. Wecanrunactioninsidealambdaexpressionthankstothecrossinlinemodifier.

Thecrossinlineannotationisusefulbecauseitallowstousefunctiontypesinthecontextoflambdaexpressionsorlocalfunctionswhilemaintainingtheadvantagesofmakingthefunctioninline(there'snoneedforlambdacreationinthiscontext).

Page 291: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

InlinepropertiesSinceKotlin1.1,theinlinemodifiercanbeusedonpropertiesthatdonothaveabackingfield.Itcanbeeitherappliedtoseparateaccessors,whichwillresultintheirbodyreplacingusage,oritcanbeusedforwholeproperty,whichwillhavethesameresultasmakingbothaccessorsinline.Let'smakeaninlinepropertythatwillbeusedtocheckandchangeanelement'svisibility.Hereisanimplementationwherebothaccessorsareinline:

varviewIsVisible:Boolean

inlineget()=findViewById(R.id.view).visibility==View.VISIBLE

inlineset(value){

findViewById(R.id.view).visibility=if(value)View.VISIBLE

elseView.GONE

}

Wecanachievethesameresultifweannotatethewholepropertyasinline:

inlinevarviewIsVisible:Boolean

get()=findViewById(R.id.view).visibility==View.VISIBLE

set(value){

indViewById(R.id.view).visibility=if(value)View.VISIBLE

elseView.GONE

}

//Usage

if(!viewIsVisible)

viewIsVisible=true

Theprecedingcodebecompiledasfollows:

if(!(findViewById(R.id.view).getVisibility()==View.VISIBLE))

{

findViewById(R.id.view).setVisibility(true?View.VISIBLE:View.GONE);

}

Thisway,wehaveomittedthesetterandgetterfunctioncalls,andweshouldexpectaperformanceimprovementwiththecostofincreasedcompiledcodesize.Still,formostproperties,itshouldbeprofitabletousetheinlinemodifier.

Page 292: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

FunctionReferencesSometimes,functionsthatwewanttopassasanargumentarealreadydefinedasaseparatefunction.Thenwecanjustdefinethelambdawithitscall:

funisOdd(i:Int)=i%2==1

list.filter{isOdd(it)}

ButKotlinalsoallowsustopassafunctionasavalue.Tobeabletouseatop-levelfunctionasavalue,weneedtouseafunctionreference,whichisusedasadoublecolonandthefunctionname(::functionName).Hereisanexamplehowitcanbeusedtoprovideapredicatetofilter:

list.filter(::isOdd)

Hereisanexample:

fungreet(){

print("Hello!")

}

funsalute(){

print("Haveaniceday")

}

valtodoList:List<()->Unit>=listOf(::greet,::salute)

for(taskintodoList){

task()

}

//Prints:Hello!Haveaniceday

Functionreferenceisexampleofreflection,andthisiswhytheobjectreturnedbythisoperationalsocontainsinformationaboutthereferredfunction:

funisOdd(i:Int)=i%2==1

valannotations=::isOdd.annotations

valparameters=::isOdd.parameters

println(annotations.size)//Prints:0

println(parameters.size)//Prints:1

Butthisobjectalsoimplementsthefunctiontype,anditcanbeusedthisway:

valpredicate:(Int)->Boolean=::isOdd

Page 293: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Itisalsopossibletoreferencetomethods.Todoit,weneedtowritethetypename,twocolons,andthemethodname(Type::functionName).Hereisanexample:

valisStringEmpty:(String)->Boolean=String::isEmpty

//Usage

valnonEmpty=listOf("A","","B","")

.filter(String::isNotEmpty)

print(nonEmpty)//Prints:["A","B"]

Asintheprecedingexample,whenwearereferencinganon-staticmethod,thereneedstobeaprovidedinstanceoftheclassasanargument.TheisEmptyfunctionisaStringmethodthattakesnoarguments.ThereferencetoisEmptyhasaStringparameterthatwillbeusedasanobjectonwhichthefunctionisinvoked.Thereferencetotheobjectisalwayslocatedasthefirstparameter.Hereisanotherexample,wherethemethodhasthepropertyfoodalreadydefined:

classUser{

funwantToEat(food:Food):Boolean{

//...

}

}

valfunc:(User,Food)->Boolean=User::wantToEat

ThereisadifferentsituationwhenwearereferencingaJavastaticmethod,becauseitdoesnotneedinstanceoftheclassonwhichitisdefined.Thisissimilartomethodsofobjectsorcompanionobjects,wheretheobjectisknowninadvanceanddoesnotneedtobeprovided.Inthesesituations,thereisafunctioncreatedwiththesameparametersasthereferencedfunctionandthesamereturntype:

objectMathHelpers{

funisEven(i:Int)=i%2==0

}

classMath{

companionobject{

funisOdd(i:Int)=i%2==1

}

}

//Usage

valevenPredicate:(Int)->Boolean=MathHelpers::isEven

valoddPredicate:(Int)->Boolean=Math.Companion::isOdd

valnumbers=1..10

valeven=numbers.filter(evenPredicate)

valodd=numbers.filter(oddPredicate)

println(even)//Prints:[2,4,6,8,10]

println(odd)//Prints:[1,3,5,7,9]

Page 294: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Infunctionreferenceusage,therearecommonusecaseswherewewanttousefunctionreferencestoprovidemethodfromaclasswehavereferenceto.Commonexampleiswhenwewanttoextractsomeoperationsasmethodofthesameclass,orwhenwewanttoreferencetofunctionsfromreferencememberfunctionfromclasswehavereferenceto.Asimpleexampleiswhenwedefinewhatshouldbedoneafteranetworkoperation.ItisdefinedinaPresenter(suchasMainPresenter),butitisreferencingalltheViewoperations,thataredefinedbytheviewproperty(whichis,forexample,oftypeMainView):

getUsers().smartSubscribe(

onStart={view.showProgress()},//1

onNext={user->onUsersLoaded(user)},//2

onError={view.displayError(it)},//1

onFinish={view.hideProgress()}//1

)

1. showProgress,displayError,andhideProgressaredefinedinMainView.2. onUsersLoadedismethoddefinedinMainPresenter.

Tohelpinthiskindofsituation,Kotlinintroducedinversion1.1featurecalledboundreferences,whichprovidereferencesthatareboundtoaspecificobject.Thankstothat,thisobjectdoesnotneedtobeprovidedbyanargument.Usingthisnotation,wecanreplacethepreviousdefinitionthisway:

getUsers().smartSubscribe(

onStart=view::showProgress,

onNext=this::onUsersLoaded,

onError=view::displayError,

onFinish=view::hideProgress

)

Anotherfunctionthatwemightwanttoreferenceisaconstructor.Anexampleusecaseiswhenweneedtomapfromadatatransferobject(DTO)toaclassthatispartofamodel:

funtoUsers(usersDto:List<UserDto>)=usersDto.map{User(it)}

Here,UserneedstohaveaconstructorthatdefineshowitisconstructedfromUserDto.

ADTOisanobjectthatcarriesdatabetweenprocesses.Itisusedbecauseclassesusedduringcommunicationsbetweenasystem(inanAPI)aredifferentthanactualclassesusedinsidethesystem(amodel).

Page 295: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

InKotlin,constructorsareusedandtreatedsimilarlytofunctions.Wecanalsoreferencetothemwithadoublecolonandaclassname:

valmapper:(UserDto)->User=::User

Thisway,wecanreplacethelambdawithaconstructorcallwithaconstructorreference:

funtoUsers(usersDto:List<UserDto>)=usersDto.map(::User)

Usingfunctionreferencesinsteadoflambdaexpressionsgivesusshorterandoftenmorereadablenotation.Itisalsoespeciallyusefulwhenwearepassingmultiplefunctionsasparameters,orfunctionsthatarelongandneedtobeextracted.Inothercases,thereistheusefulboundedreference,whichprovidesareferencethatisboundtoaspecificobject.

Page 296: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

SummaryInthischapter,we'vediscussedusingfunctionsasfirst-classcitizens.We'veseenhowfunctiontypesareused.Wehaveseenhowtodefinefunctionliterals(anonymousfunctionsandlambdaexpressions),andthatanyfunctioncanbeusedasanobjectthankstofunctionreferences.We'vealsodiscussedhigher-orderfunctionsanddifferentKotlinfeaturesthatsupportthem:theimplicitnameofasingleparameter,thelastlambdainargumentconvention,JavaSAMsupport,usinganunderscoreforunusedvariables,anddestructuringdeclarationsinlambdaexpressions.Thisfeaturesprovidegreatsupportforhigher-orderfunctions,andtheymakefunctionsevenmorethanfirst-classcitizens.

Inthenextchapter,wearegoingtoseehowgenericsworkinKotlin.Thiswillallowustodefinemuchmorepowerfulclassesandfunctions.Wewillalsoseehowwelltheycanbeusedwhenconnectedtohigher-orderfunctions.

Page 297: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

GenericsAreYourFriendsInthepreviouschapter,wediscussedconceptsrelatedtofunctionalprogrammingandfunctionsasfirst-classcitizensinKotlin.

Inthischapter,wewilldiscussconceptofgenerictypesandgenericfunctionsknownasgenerics.Wewilllearnwhytheyexistandhowtousethem-wewilldefinegenericclasses,interfaces,andfunctions.Wewilldiscusshowtodealwithgenericsatruntime,takelookatsubtypingrelations,anddealwithgenericsnullability

Inthischapter,wewilldiscusstheconceptsofgenerictypesandgenericfunctions,knownasgenerics.Wewilllearnwhytheyexistandhowtousethemandalsohowtodefinegenericclasses,interfaces,andfunctions.Wewilldiscusshowtodealwithgenericsatruntime,takealookatsubtypingrelations,anddealwithgenericnullability.

Inthischapter,wewillcoverthefollowingtopics:

GenericclassesGenericinterfacesGenericfunctionsGenericconstraintsGenericnullabilityVarianceUse-sitetargetversusdeclaration-sitetargetDeclaration-sitetargetTypeerasureReifiedanderasedtypeparametersStar-projectionsyntaxVariance

Page 298: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

GenericsGenericisaprogrammingstylewhereclasses,functions,datastructures,oralgorithmsarewritteninsuchawaythattheexacttypecanbespecifiedlater.Ingeneral,genericsprovidetypesafetytogetherwiththeabilitytoreuseaparticularcodestructureforvariousdatatypes.

GenericsarepresentinbothJavaandKotlin.Theyworkinasimilarway,butKotlinoffersafewimprovementsovertheJavagenerictypesystem,suchasuse-sitevariance,start-projectionsyntax,andreifiedtypeparameters.Wewilldiscusstheminthischapter.

Page 299: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

TheneedforgenericsProgrammersoftenneedawaytospecifythatacollectioncontainsonlyelementsofparticulartype,suchasInt,Student,orCar.Withoutgenerics,wewouldneedseparateclassesforeachdatatype(IntList,StudentList,CarList,andsoon).Thoseclasseswouldhaveaverysimilarinternalimplementation,whichwouldonlydifferinthestoreddatatype.Thismeansthatwewouldneedtowritethesamecode(suchasaddingorremovinganitemfromacollection)multipletimesandmaintaineachclassseparately.Thisisalotofwork,sobeforegenericswereimplemented,programmersusuallyoperatedonauniversallist.Thisforcedthemtocastelementseachtimetheywereaccessed:

//Java

ArrayListlist=newArrayList();

list.add(1);

list.add(2);

intfirst=(int)list.get(0);

intsecond=(int)list.get(1);

Castingaddsboilerplate,andthereisnotypevalidationwhenanelementisaddedtoacollection.Genericsarethesolutionforthisproblem,becauseagenericclassdefinesandusesaplaceholderinsteadofarealtype.Thisplaceholderiscalledatypeparameter.Let'sdefineourfirstgenericclass:

classSimpleList<T>//Tistypeparameter

Thetypeparametermeansthatourclasswilluseacertaintype,butthistypewillbespecifiedduringclasscreation.Thisway,ourSimpleListclasscanbeinstantiatedforavarietyoftypes.Wecanparametrizeagenericclasswithvariousdatatypesusingtypearguments.Thisallowsustocreatemultipledatatypesfromsingleclass:

//Usage

varintList:SimpleList<Int>

varstudentList:SimpleList<Student>

varcarList:SimpleList<Car>

TheSimpleListclassisparametrizedwithtypearguments(Int,Student,andCar)thatdefinewhatkindofdatacanbestoredinthegivenlist.

Page 300: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

TypeparametersversustypeargumentsFunctionshaveparameters(variablesdeclaredinsideafunctiondeclaration)andarguments(actualvaluethatispassedtoafunction).Similarterminologyappliesforgenerics.Atypeparameterisablueprintorplaceholderforatypedeclaredinagenericandatypeargumentisanactualtypeusedtoparametrizeageneric.

Wecanuseatypeparameterinamethodsignature.Thisway,wecanmakesurethatwewillbeabletoadditemsofacertaintypetoourlistandretrieveitemsofacertaintype:

classSimpleList<T>{

funadd(item:T){//1

//code

}

funget(intex:Int):T{//2

//code

}

}

1. GenerictypeparameterTusedastypeforitem2. Typeparameterusedasreturntype

Thetypeofitemthatcanbeaddedtoalistorretrievedfromalistdependsonthetypeargument.Let'sseeanexample:

classStudent(valname:String)

valstudentList=SimpleList<Student>()

studentList.add(Student("Ted"))

println(studentList.getItemAt(0).name)

WecanonlyaddandgetitemsoftypeStudentfromthelist.Thecompilerwillautomaticallyperformallnecessarytypechecks.Itisguaranteedthatthecollectionwillonlycontainobjectsofaparticulartype.Passinganobjectofincompatibletypetotheaddmethodwillresultinacompile-timeerror:

varstudentList:SimpleList<Student>

studentList.add(Student("Ted"))

studentList.add(true)//error

Page 301: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

WecannotaddBoolean,becauseexpectedtypeisStudent.

TheKotlinstandardlibrarydefinesvariousgenericcollectionsinthekotlin.collectionspackage,suchasList,Set,andMap.WewilldiscusstheminChapter7,ExtensionFunctionsandProperties.

InKotlin,genericsareoftenusedincombinationwithhigher-orderfunctions(discussedinChapter5,FunctionsasAFirstClassCitizen)andextensionfunctions(whichwewilldiscussinChapter7,ExtensionFunctionsandProperties).Examplesofsuchconnectionsarefunctions:map,filter,takeUntil,andsoon.Wecanperformcommonoperationsthatwilldifferinthedetails.Forexample,wecanfindmatchingelementsinthecollectionusingtheoperationfilterfunctionandspecifyinghowmatchingelementswillbedetected:

valfruits=listOf("Babana","Orange","Apple","Blueberry")

valbFruits=fruits.filter{it.startsWith("B")}//1

println(bFruits)//Prints:[Babana,Blueberry]

1. WecancallthestartsWithmethod,becausethecollectioncancontainonlyStrings,sothelambdaparameter(it)hasthesametype.

Page 302: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

GenericconstraintsBydefault,wecanparametrizeagenericclasswithanytypeoftypeargument.However,wecanlimitthepossibletypesthatcanbeusedastypearguments.Tolimitthepossiblevaluesoftypeargument,weneedtodefineatypeparameterbound.Themostcommontypeofconstraintisanupperbound.Bydefault,alltypeparametershaveAny?asanimplicitupperbound.Thisiswhyboththefollowingdeclarationsareequivalent:

classSimpleList<T>

classSimpleList<T:Any?>

TheprecedingboundsmeanthatwecanuseanytypewewantastypeargumentforourSimpleListclass(includingnullabletypes).Thisispossiblebecauseallnullableandnon-nullabletypesaresubtypesofAny?:

classSimpleList<T>

classStudent

//usage

varintList=SimpleList<Int>()

varstudentList=SimpleList<Student>()

varcarList=SimpleList<Boolean>()

Insomesituations,wewanttolimitthedatatypesthatcanbeusedastypearguments.Tomakeithappen,weneedtoexplicitlydefineatypeparameterupperbound.Let'sassumethatwewanttobeabletouseonlynumerictypesastypeargumentsforourSimpleListclass:

classSimpleList<T:Number>

//usage

varnumberList=SimpleList<Number>()

varintList=SimpleList<Int>()

vardoubleList=SimpleList<Double>()

varstringList=SimpleList<String>()//error

TheNumberclassisanabstractclass,thatis,asuperclassofKotlinnumerictypes(Byte,Short,Int,Long,Float,andDouble).WecanusetheNumberclassandallitssubclasses(Int,Double,andsoon)asatypeargument,butwecan'tusetheStringclass,becauseit'snotasubclassofNumber.AnyattempttoaddanincompatibletypewillberejectedbytheIDEandcompiler.Typeparametersalsoincorporate

Page 303: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Kotlintypesystemnullability.

Page 304: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

NullabilityWhenwedefineaclasswithanunboundedtypeparameter,wecanusebothnon-nullableandnullabletypesastypearguments.Occasionally,weneedtomakesurethataparticulargenerictypewillnotbeparametrizedwithnullabletypearguments.Toblocktheabilitytousenullabletypesastypearguments,weneedtoexplicitlydefineanon-nullabletypeparameterupperbound:

classAction(valname:String)

classActionGroup<T:Action>

//non-nullabletypeparameterupperbound

varactionGroupA:ActionGroup<Action>

varactionGroupB:ActionGroup<Action?>//Error

Nowwecan'tpassanullabletypeargument(Action?)totheActionGroupclass.

Let'sconsideranotherexample.ImaginethatwewanttoretrievethelastActionintheActionGroup.Asimpledefinitionofthelastmethodwouldlooklikethis:

classActionGroup<T:Action>(privatevallist:List<T>){

funlast():T=list.last()

}

Let'sanalyzewhatwillhappenwhenwepassanemptylisttotheconstructor:

valactionGroup=ActionGroup<Action>(listOf())

//...

valaction=actionGroup.last

//error:NoSuchElementException:Listisempty

println(action.name)

Ourapplicationcrashes,becausethemethodlastisthrowinganerrorwhenthereisnoelementwithsuchanindexonthelist.Insteadofanexception,wemightpreferanullvaluewhenthelistisempty.TheKotlinstandardlibraryalreadyhasacorrespondingmethodthatwillreturnanullvalue:

classActionGroup<T:Action>(privatevallist:List<T>){

funlastOrNull():T=list.lastOrNull()//error

}

Thecodewillnotcompilebecausethereisapossibilitythatthelastmethodwill

Page 305: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

returnnullirrespectiveoftypeargumentnullability(theremaybenoelementsinthelisttoreturn).Tosolvethisproblem,weneedtoenforceanullablereturntypebyaddingaquestionmarktothetypeparameteruse-site(T?):

classActionGroup<T:Action>(privatevallist:List<T>){//1

funlastOrNull():T?=list.lastOrNull()//2

}

1. Typeparameterdeclaration-site(placeincodewheretypeparameterisdeclared)

2. Typeparameteruse-site(placeincodewheretypeparameterisused)

TheT?parametermeansthatthelastOrNullmethodwillalwaysbenullableregardlessofpotentialtypeargumentnullability.NoticethatwerestoredthetypeparameterTboundasnon-nullabletypeAction,becausewewanttostorenon-nullabletypesanddealwithnullabilityonlyforcertainscenarios(suchasanon-existinglastelement).Let'suseourupdatedActionGroupclass:

valactionGroup=ActionGroup<Action>(listOf())

valactionGroup=actionGroup.lastOrNull()

//InferredtypeisAction?

println(actionGroup?.name)//Prints:null

NoticethattheactionGroupinferredtypeisnullableevenifweparameterizedthegenericwithanon-nullabletypeargument.

Anullabletypeattheuse-sitedoesnotstopusfromallowingnon-nulltypesinthedeclaration-site:

openclassAction

classActionGroup<T:Action?>(privatevallist:List<T>){

funlastOrNull():T?=list.lastOrNull()

}

//Usage

valactionGroup=ActionGroup(listOf(Action(),null))

println(actionGroup.lastOrNull())//Prints:null

Let'ssumuptheabovesolution.Wespecifiedanon-nullableboundfortypeparametertodisallowparameterizingtheActionGroupclasswithnullabletypesastypearguments.WeparameterizedtheActionGroupclasswiththenon-nullabletypeargumentAction.Finally,weenforcedtypeparameternullabilityattheuse-site(T?),becausethelastpropertycanreturnnulliftherearenoelementsinthelist.

Page 306: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

VarianceSubtypingisapopularconceptintheOOPparadigm.Wedefineinheritancebetweentwoclassesbyextendingtheclass:

openclassAnimal(valname:String)

classDog(name:String):Animal(name)

TheclassDogextendstheclassAnimal,sothetypeDogisasubtypeofAnimal.ThismeansthatwecanuseanexpressionoftypeDogwheneveranexpressionoftypeAnimalisrequired;forexample,wecanuseitasafunctionargumentorassignavariableoftypeDogtoavariableoftypeAnimal:

funpresent(animal:Animal){

println("Thisis${animal.name}")

}

present(Dog("Pluto"))//Prints:ThisisPluto

Beforewemoveon,weneedtodiscussthedifferencebetweenclassandtype.Typeisamoregeneralterm--itcanbedefinedbyclassorinterface,oritcanbebuiltintothelanguage(primitivetype).InKotlin,foreachclass(forexample,Dog),wehaveatleasttwopossibletypes--non-nullable(Dog)andnullable(Dog?).Whatismore,foreachgenericclass(forexample,classBox<T>)wecandefinemultipledatatypes(Box<Dog>,Box<Dog?>,Box<Animal>,Box<Box<Dog>>,andsoon).

Thepreviousexampleappliesonlytosimpletypes.Variancespecifieshowsubtypingbetweenmorecomplextypes(forexample,Box<Dog>andBox<Animal>)relatestosubtypingbetweentheircomponents(forexample,Animal,andDog).

InKotlin,genericsareinvariantbydefault.ThismeansthatthereisnosubtypingrelationbetweenthegenerictypesBox<Dog>andBox<Animal>.TheDogcomponentissubtypeofAnimal,butBox<Dog>isneitherasubtypenorasupertypeofBox<Animal>:

classBox<T>

openclassAnimal

classDog:Animal()

varanimalBox=Box<Animal>()

vardogBox=Box<Dog>()

//oneofthelinesbelowlinemustbecommentedout,

Page 307: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

//otherwiseAndroidStudiowillshowonlyoneerror

animalBox=dogBox//2,error

dogBox=animalBox//1,error

1. ErrorTypemismatch.RequiredBox<Animal>,foundBox<Dog>.2. ErrorTypemismatch.RequiredBox<Dog>,foundBox<Animal>.

TheBox<Dog>typeisneitherasubtypenorasupertypeofBox<Animal>,sowecan'tuseanyoftheassignmentsshownintheprecedingcode.

WecandefinesubtypingrelationsbetweenBox<Dog>andBox<Animal>.InKotlin,asubtypingrelationofgenerictypecanbepreserved(co-variant),reversed(contra-variant),orignored(invariant).

Whenasubtypingrelationisco-variant,itmeansthatsubtypingispreserved.Thegenerictypewillhavethesamerelationasthetypearguments.IfDogisasubtypeofAnimal,thenBox<Dog>isasubtypeofBox<Animal>.

Contra-variantistheexactoppositeofco-variant,wheresubtypingisreversed.Thegenerictypewillhavereversedrelationwithrespecttotypearguments.IfDogisasubtypeofAnimal,thenBox<Animal>isasubtypeofBox<Dog>.Thefollowingdiagrampresentalltypesofvariance:

Todefineco-variantorcontra-variantbehavior,weneedtousevariancemodifiers.

Page 308: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

VariancemodifiersGenericsinKotlinareinvariantbydefault.Thismeansthatweneedtousetypeasthetypeofdeclaredvariableorfunctionparameter:

publicclassBox<T>{}

funsum(list:Box<Number>){/*...*/}

//Usage

sum(Box<Any>())//Error

sum(Box<Number>())//Ok

sum(Box<Int>())//Error

Wecan'tuseagenerictypeparametrizedwithInt,whichisasubtypeofNumber,andAny,whichisasupertypeofNumber.Wecanrelaxthisrestrictionandchangethedefaultvariancebyusingvariancemodifiers.InJava,thereisquestionmark(?)notation(wildcardnotation)usedtorepresentanunknowntype.Usingit,wecandefinetwotypesofwildcardbounds--upperboundandlowerbound.InKotlin,wecanachievesimilarbehaviorusinginandoutmodifiers.

InJava,theupperboundwildcardallowsustodefineafunctionthatacceptsanyargumentthatisacertaintypeofitssubtype.Inthefollowingexample,thesumfunctionwillacceptanyListthatwasparametrizedwiththeNumberclassorasubtypeoftheNumberclass(Box<Integer>,Box<Double>,andsoon):

//Java

publicvoidsum(Box<?extendsNumber>list){/*...*/}

//Usage

sum(newBox<Any>())//Error

sum(newBox<Number>())//Ok

sum(newBox<Int>())//Ok

WecannowpassBox<Number>tooursumfunctionandallthesubtypes,forexample,Box<Int>.ThisJavabehaviorcorrespondstotheKotlinoutmodifier.Itrepresentscovariance,whichrestrictsthetypetobeaspecifictypeorasubtypeofthattype.ThismeansthatwecansafelypassinstancesoftheBoxclassthatareparametrizedwithanydirectorindirectsubclassofNumber:

classBox<T>

funsum(list:Box<outNumber>){/*...*/}

//usage

sum(Box<Any>())//Error

Page 309: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

sum(Box<Number>())//Ok

sum(Box<Int>())//Ok

InJava,thelowerboundwildcardallowsustodefineafunctionthatacceptsanyargumentthatisacertaintypeoritssupertype.Inthefollowingexample,thesumfunctionwillacceptanyListthatwasparametrizedwiththeNumberclassorasupertypeoftheNumberclass(Box<Number>andBox<Object>):

//Java

publicvoidsum(Box<?superNumber>list){/*...*/}

//usage

sum(newBox<Any>())//Ok

sum(newBox<Number>())//Ok

sum(newBox<Int>())//Error

WecannowpassBox<Any>tooursumfunctionandallthesubtypes,forexample,Box<Any>.ThisJavabehaviorcorrespondstotheKotlininmodifier.Itrepresentscontra-variance,whichrestrictsthetypetobeaspecifictypeorasupertypeofthattype:

classBox<T>

funsum(list:Box<inNumber>){/*...*/}

//usage

sum(Box<Any>())//Ok

sum(Box<Number>())//Ok

sum(Box<Int>())//Error

It'sforbiddentouseaninandoutmodifiertogether.Wecandefinevariancemodifiersintwodifferentways.Let'slookatthemintheupcomingsection.

Page 310: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Use-sitevarianceversusdeclaration-sitevarianceUse-sitevarianceanddeclaration-sitevariancebasicallydescribestheplaceinthecode(site)wherethevariancemodifierisspecified.Let'sconsidertheViewandPresenterexample:

interfaceBaseView

interfaceProductView:BaseView

classPresenter<T>

//Usage

varpreseter=Presenter<BaseView>()

varproductPresenter=Presenter<ProductView>()

preseter=productPresenter

//Error:Typemismatch

//Required:Presenter<BaseView>

//Found:Presenter<ProductView>

TheclassPresenterisinvariantonitstypeparameterT.Tofixtheproblem,wecanexplicitlydefinethesubtypingrelation.Wecandoitintwoways(use-siteanddeclaration-site).First,let'sdefinethevarianceattheuse-site:

varpreseter:Presenter<outBaseView>=Presenter<BaseView>()//1

varproductPresenter=Presenter<ProductView>()

preseter=productPresenter

1. Variancemodifierdefinedattypeargumentuse-site

NowthepresetervariablecanstoresubtypesofPresenter<BaseView>,includingPresenter<ProductView>.Oursolutionworks,butourimplementationcanbeimproved.Therearetwoproblemswiththisapproach.Nowweneedtospecifythisoutvariancemodifiereachtimewewanttouseagenerictype,forexample,useitinmultiplevariablesacrossdifferentclasses:

//VariabledeclaredinsideclassAandclassB

varpreseter=Presenter<BaseView>()

varpreseter:Presenter<outBaseView>=Presenter<ProductView>()

preseter=productPresenter

BothclassesAandBcontainsthepresetervariablethathasvariancemodifier.We

Page 311: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

losetheabilitytousetypeinferenceandinresultthecodeismoreverbose.Toimproveourcodewecanspecifyvariancemodifierattypeparameterdeclaration-site:

interfaceBaseView

interfaceProductView:BaseView

classPresenter<outT>//1

//usage

//VariabledeclaredinsideclassAandB

varpreseter=Presenter<BaseView>()

varproductPresenter=Presenter<ProductView>()

preseter=productPresenter

1. Variancemodifierdefinedattypeparameterdeclaration-site

WeonlyneedtodefinevariancemodifieronceinsidePresenterclass.Infact,bothprecedingimplementationsaretheequivalent,althoughdeclaration-sitevarianceismoreconciseanditcanbeeasierusedbyexternalclientsoftheclass

Page 312: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

CollectionvarianceInJava,arraysareco-variant.Bydefault,wecanpassanarrayofString[]evenifanarrayofObject[]isexpected:

publicclassComputer{

publicComputer(){

String[]stringArray=newString[]{"a","b","c"};

printArray(stringArray);//PassinstanceofString[]

}

voidprintArray(Object[]array){

//DefineparameteroftypeObject[]

System.out.print(array);

}

}

ThisbehaviorwasimportantinearlyversionsofJava,becauseitallowedustousedifferenttypesofarraysasarguments:

//Java

staticvoidprint(Object[]array){

for(inti=0;i<=array.length-1;i++)

System.out.print(array[i]+"");

System.out.println();

}

//Usage

String[]fruits=newString[]{"Pineapple","Apple","Orange",

"Banana"};

print(fruits);//Prints:PineappleAppleOrangeBanana

Arrays.sort(fruits);

print(fruits);//Prints:AppleBananaOrangePineapple

Butthisbehavioralsomayleadtopotentialruntimeerrors:

publicclassComputer{

publicComputer(){

Number[]numberArray=newNumber[]{1,2,3};

updateArray(numberArray);

}

voidupdateArray(Object[]array){

array[0]="abc";

//Error,java.lang.ArrayStoreException:java.lang.String

}

}

ThefunctionupdateArrayacceptsparametersoftypeObject[]andwearepassingString[].WearecallingtheaddmethodwithaStringparameter.WecandosobecausearrayitemsareoftypeObject,sowecanuseString,whichisanewvalue.

Page 313: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Finally,wewanttoaddString,intothegenericarraythatmayonlycontainitemsofStringtype.Duetodefaultco-variantbehavior,thecompilercan'tdetectthisproblemandthiswillleadtoanArrayStoreExceptionexception.

ThecorrespondingcodewouldnotcompileinKotlin,becausetheKotlincompilertreatsthisbehavioraspotentiallydangerous.ThisisthereasonwhyarraysinKotlinareinvariant.Therefore,passingtypeotherthanArray<Number>whenArray<Any>isrequiredwillresultincompiletimeerror:

publicclassArray<T>{/*...*/}

Therefore,passingatypeotherthanArray<Number>whenArray<Any>isrequiredwillresultinacompile-timeerror:

publicclassArray<T>{/*...*/}

classComputer{

init{

valnumberArray=arrayOf<Number>(1,2,3)

updateArray(numberArray)

}

internalfunupdateArray(array:Array<Any>){

array[0]="abc"

//error,java.lang.ArrayStoreException:java.lang.String

}

}

Noticethatapotentialruntimeexceptionmayonlyoccurwhenwecanmodifytheobject.VarianceisalsoappliedtoKotlincollectioninterfaces.IntheKotlinstandardlibrary,wehavetwolistinterfacesthataredefinedindifferentways.TheKotlinListinterfaceisdefinedasco-variant,becauseitisimmutable(itdoesnotcontainanymethodsthatwouldallowustochangetheinnerstate),whiletheKotlinMutableListinterfaceisinvariant.Herearethedefinitionsoftheirtypeparameters:

interfaceList<outE>:Collection<E>{/*...*/}

publicinterfaceMutableList<E>:List<E>,MutableCollection<E>{

/*...*/

}

Let'sseetheconsequencesofsuchdefinitionsinaction.Itmakesmutablelistssafefromtherisksofcovariance:

funaddElement(mutableList:MutableList<Any>){

mutableList.add("Cat")

}

//Usage

valmutableIntList=mutableListOf(1,2,3,4)

Page 314: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

valmutableAnyList=mutableListOf<Any>(1,'A')

addElement(mutableIntList)//Error:Typemismatch

addElement(mutableAnyList)

Thelistissafe,becauseithasnomethodsusedtochangeitsinnerstate,anditscovariancebehaviorallowsmoregeneralusageoffunctions:

funprintElements(list:List<Any>){

for(einlist)print(e)

}

//Usage

valintList=listOf(1,2,3,4)

valanyList=listOf<Any>(1,'A')

printElements(intList)//Prints:1234

printElements(anyList)//Prints:1A

WecanpassList<Any>oranyofitssubtypestotheprintElementsfunction,becausetheListinterfaceisco-variant.WecanonlypassMutableList<Any>totheaddElementfunctionbecausetheMutableListinterfaceisinvariant.

Usinginandoutmodifiers,wecanmanipulatevariancebehavior.Weshouldalsobeawarethatvariancehassomelimitations.Let'sdiscussthem.

Page 315: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Varianceproducer/consumerlimitationByapplyingavariancemodifier,wegainco-variant/contra-variantbehaviorforacertaintypeparameteroftheclass/interface(declaration-sitevariance)ortypeargument(use-sitevariance).However,thereisalimitationthatweneedtobeawareof.Tomakeitsafe,theKotlincompilerlimitsthepositionswheretypeparameterscanbeused.

Withinvariant(defaultnovariancemodifierontypeparameter)wecanuseatypeparameteronbothin(typeoffunctionparameter)andout(functionreturntype)positions:

interfaceStack<T>{

funpush(t:T)//Generictypeatinposition

funpop():T//Generictypeatoutposition

funswap(t:T):T//Generictypeatinandoutpositions

vallast:T//Generictypeatoutposition

varspecial:T//Generictypeatoutposition

}

Withvariancemodifier,weareonlylimitedtoasingleposition.Thismeansthatwecanuseatypeparameteronlyasatypeformethodparameters(in)ormethodreturnvalue(out).Ourclasscanbeproducerorconsumer,butneverboth.Wecansaythattheclasstakesinparametersorgivesoutparameters.

Let'slookathowthisrestrictionrelatestovariancemodifiersspecifiedatthedeclaration-site.HereareallthecorrectandincorrectusagesforthetwotypeparametersRandT:

classConsumerProducer<inT,outR>{

funconsumeItemT(t:T):Unit{}//1

funconsumeItemR(r:R):Unit{}//2,error

funproduceItemT():T{//3,error

//ReturninstanceoftypeT

}

funproduceItemR():R{//4

//ReturninstanceoftypeR

}

}

Page 316: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

1. OKtypeparameterTatinposition2. Error,typeparameterRatinposition3. Error,typeparameterTatoutposition4. OK,typeparameterRatoutposition

Aswecansee,thecompilerwillreportanerroriftheconfigurationisprohibited.NoticethatwecanadddifferentmodifiersforthetwotypeparametersRandT.

Positionrestrictionappliesonlyformethodsaccessible(visible)outsidetheclass.Thismeansnotonlyallpublicmethods(publicisthedefaultmodifier)asusedpreviously,butalsomethodsmarkedwithprotectedorinternal.Whenwechangemethodvisibilitytoprivate,thenwecanuseourtypeparameters(RandT)onanyposition,justlikeinvarianttypeparameters:

classConsumerProducer<inT,outR>{

privatefunconsumeItemT(t:T):Unit{}

privatefunconsumeItemR(r:R):Unit{}

privatefunproduceItemT():T{

//ReturninstanceoftypeT

}

privatefunproduceItemR():R{

//ReturninstanceoftypeR

}

}

Let'slookatthefollowingtable,whichpresentsallallowedpositionsfortypeparametersusedastype:

Visibilitymodifier Invariance Covariance(out) Contravariance(in)

public,protected,internal in/out out in

private in/out in/out in/out

Page 317: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

InvariantconstructorThereisoneimportantexceptionfortheinandoutpositionrulesdescribedintheprevioussection:constructorparametersarealwaysinvariant:

classProducer<outT>(t:T)

//Usage

valstringProducer=Producer("A")

valanyProducer:Producer<Any>=stringProducer

Theconstructorispublic,typeparameterTisdeclaredasout,butwecanstilluseitasconstructorparametertypeattheinposition.Thereasonisthattheconstructormethodcan'tbecalledafteraninstanceiscreated,soitisalwayssafetocallit.

AswediscussedinChapter4,ClassesandObjects,wecanalsodefineapropertydirectlyintheclassconstructorusingavalorvarmodifier.Whencovarianceisspecified,wecanonlydefinearead-onlyproperty(val)intheconstructorthathasco-varianttype.Itissafebecauseonlythegetterwillbegenerated,sothevalueofthispropertycan'tchangeafterclassinstantiation:

classProducer<outT>(valt:T)//Ok,safe

Withvar,bothgetterandsetteraregeneratedbythecompiler,sothepropertyvaluecanpotentiallychangeatsomepoint.That'swhywecan'tdeclarearead-write(var)propertyofco-varianttypeintheconstructor:

classProducer<outT>(vart:T)//Error,notsafe

Wealreadysaidthatvariancerestrictiononlyappliesforexternalclients,sowecouldstilldefineaco-variantread-writepropertybyaddingaprivatevisibilitymodifier:

classProducer<outT>(privatevart:T)

Anotherpopulargenerictyperestriction,knownfromJava,relatestotypeerasure.

Page 318: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

TypeerasureTypeerasurewasintroducedintoJVMtomakeJVMbytecodebackwardcompatiblewithversionsthatpredatetheintroductionofgenerics.OntheAndroidplatform,bothKotlinandJavaarecompiledtoJVMbytecode,sotheybotharevulnerabletotypeerasure.

Typeerasureistheprocessofremovingatypeargumentfromagenerictype,sothatthegenerictypelosessomeofitstypeinformation(typeargument)atruntime:

packagetest

classBox<T>

valintBox=Box<Int>()

valstringBox=Box<String>()

println(intBox.javaClass)//prints:test.Box

println(stringBox.javaClass)//prints:test.Box

Thecompilercandistinguishbothtypesandguaranteetypesafety.However,duringcompilation,theparameterizedtypesBox<Int>andBox<String>aretranslatedbythecompilertoaBox(rawtype).ThegeneratedJavabytecodedoesnotcontainanyinformationrelatedtotypearguments,sowecan'tdistinguishgenerictypesatruntime.

Typeerasureleadstoafewproblems.InJVM,wecan'tdeclaretwooverloadsofthesamemethod,withthesameJVMsignature:

/*

java.lang.ClassFormatError:Duplicatemethodname&signature...

*/

funsum(ints:List<Int>){

println("Ints")

}

funsum(strings:List<String>){

println("Ints")

}

Whenthetypeargumentisremoved,thosetwomethodswillhaveexactlythesamedeclaration:

/*

Page 319: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

java.lang.ClassFormatError:Duplicatemethodname&signature...

*/

funsum(ints:List){

println("Ints")

}

funsum(strings:List){

println("Ints")

}

WecanalsosolvethisproblembychangingtheJVMnameofthegeneratedfunction.WecandoitusingJvmNameannotationtochangethenameofoneofthemethodswhenthecodeiscompiledtoJVMbytecode:

@JvmName("intSum")funsum(ints:List<Int>){

println("Ints")

}

funsum(strings:List<String>){

println("Ints")

}

NothingchangedinthisfunctionusagefromKotlin,butsincewechangedtheJVMnameofthefirstfunction,weneedtouseanewnametouseitfromJava:

//Java

TestKt.intSum(listOfInts);

Sometimeswewanttopreservethetypeargumentatruntimeandthisiswherereifiedtypeparametersarequitehandy.

Page 320: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ReifiedtypeparametersTherearesomecaseswhereaccessingthetypeparameteratruntimewouldbeuseful,buttheyarenotallowedbecauseoftypeerasure:

fun<T>typeCheck(s:Any){

if(sisT){

//Error:cannotcheckforinstanceoferasedtype:T

println("Thesametypes")

}else{

println("Differenttypes")

}

}

TobeabletoovercomeJVMlimitation,Kotlinallowsustouseaspecialmodifierthatcanpreserveatypeargumentatruntime.Weneedtomarkthetypeparameterwiththereifiedmodifier:

interfaceView

classProfileView:View

classHomeView:View

inlinefun<reifiedT>typeCheck(s:Any){//1

if(sisT){

println("Thesametypes")

}else{

println("Differenttypes")

}

}

//Usage

typeCheck<ProfileView>(ProfileView())//Prints:Thesametypes

typeCheck<HomeView>(ProfileView())//Prints:Differenttypes

typeCheck<View>(ProfileView())//Prints:Thesametypes

1. Typeparametermarkedasrefinedandfunctionmarkedasinline.

Nowwecansafelyaccessthetypeargumenttypeatruntime.Reifiedtypeparametersworkonlywithinlinefunctions,becauseduringcompilation(inlining),theKotlincompilerreplacesreifiedtypeargumentactualclass.Thisway,thetypeargumentwillnotberemovedbytypeerasure.

Wecanalsousereflectiononareifiedtypetoretrievemoreinformationaboutthetype:

inlinefun<reifiedT>isOpen():Boolean{

returnT::class.isOpen

}

Page 321: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

OccurrencesofareifiedtypeparameterarerepresentedatJVMbytecodelevelasanactualtypeorawrappertypeforprimitivetypes.That'swhyreifiedtypeparametersarenotaffectedbytypeerasure.

Usingreifiedtypeparametersallowsustowritemethodsinawholenewway.TostartanewActivityinJava,weneedcodelikethis:

//Java

startActivity(Intent(this,ProductActivity::class.java))

InKotlin,wecandefinethestartActivitymethodthatwillallowustonavigatetoActivityinmuchsimplerway:

inlinefun<reifiedT:Activity>startActivity(context:Context){

context.startActivity(Intent(context,T::class.java))

}

//Usage

startActivity<UserDetailsActivity>(context)

WedefinedthestartActivitymethodandwepassedinformationabouttheActivitywewanttostart(ProductActivity)byusingatypeargument.WealsodefinedanexplicitreifiedtypeparameterboundtomakesurethatwecanonlyuseActivity(anditssubclasses)astypeargument.

Page 322: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ThestartActivitymethodTomakeproperuseofthestartActivitymethod,weneedawaytopassparameterstotheActivitybeingstarted(Bundle).Itispossibletoupdatetheprecedingimplementationtosupportargumentslikethis:

startActivity<ProductActivity>("id"to123,"extended"totrue)

Intheprecedingexample,argumentsarefilledusingakeyandvalueprovidedbypairs(definedbytheinlinetofunction).Thisfunctionimplementationis,however,outsideofthescopeofthisbook.Wecan,however,useanexistingone.TheAnkolibrary(https://github.com/Kotlin/anko)alreadyimplementsthestartActivitymethodwithalltherequiredfunctionality.WejustneedtoimportAppcompat-v7-commonsdependency.

compile"org.jetbrains.anko:anko-appcompat-v7-commons:$anko_version"

AnkodefinesextensionsforContextandFragmentclassessowecanusethismethodinanyActivityorFragmentjustlikeanyothermethoddefinedintheclasswithouttheneedtodefinethemethodintheclass.WewilldiscussextensionsinChapter7,ExtensionFunctionsandProperties.

Beawarethatreifiedtypeparametershaveonemainlimitation:wecan'tcreateaninstanceofaclassfromareifiedtypeparameter(withoutreflectionusage).Thereasonbehindthisisthataconstructorisalwaysonlyassociatedtoaconcreteinstance(itisneverinherited)sothereisnoconstructorthatcouldbesafelyusedforallpossibletypeparameters.

Page 323: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Star-projectionsBecauseoftypeerasure,incompletetypeinformationisavailableatruntime.Forexample,typeparametersofgenerictypesarenotavailable:

vallist=listOf(1,2,3)

println(list.javaClass)//Prints:classjava.util.Arrays$ArrayList

Thisleadstoafewproblems.Wecan'tperformanychecktoverifywhattypesofelementsListcontains:

/*

Compiletimeerror:cannotcheckinstanceoferasedtype:

List<String>

*/

if(collectionisList<Int>){

//...

}

Theproblemoccursbecauseacheckisperformedatruntimewhereinformationabouttypeparametersisnotavailable.Kotlin,however,asopposedtoJava,doesnotallowustodeclarerawtype(generictypethatisnotparametrizedwithtypeargument):

SimpleList<>//Java:ok

SimpleList<>//Kotlin:error

Kotlinallowsustousestar-projectionsyntaxinstead,whichisbasicallyawaytosaythatinformationabouttypeargumentismissingoritisnotimportant:

if(collectionisList<*>){

//...

}

Byusingstar-projectionsyntax,wesaythatBoxstoresargumentsofacertaintype:

classBox<T>

valanyBox=Box<Any>()

valintBox=Box<Int>()

valstringBox=Box<String>()

varunknownBox:Box<*>

unknownBox=anyBox//Ok

unknownBox=intBox//Ok

Page 324: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

unknownBox=stringBox//Ok

NoticethatthereisadifferencebetweenBox<*>andBox<Any>.IfwewanttodefinelistcontainsitemsofAnywewoulduseBox<Any>.Howeverifwewanttodefinelistthatcontainstermsofcertaintype,butthistypeisunknown(itmaybeAny,Int,String,andsoon.Butwedon’thaveinformationaboutthistype),whileBox<Any>meansthatlistcontainsitemsofAnytype.WewilluseBox<*>:

valanyBox:Box<Any>=Box<Int>//Error:Typemismatch

Ifagenerictypedefineswithmultipletypeparameters,weneedtouseastar(*)foreachmissingtypeargument:

classContainer<T,T2>

valcontainer:Container<*,*>

Star-projectionisalsohelpfulwhenwewanttoperformanoperationonthetype,butinformationabouttypeargumentisnotimportant:

funprintSize(list:MutableList<*>){

println(list.size)

}

//usage

valstringList=mutableListOf("5","a","2","d")

valintList=mutableListOf(3,7)

printSize(stringList)//prints:4

printSize(intList)//prints:2

Intheprecedingexample,theinformationabouttypeargumentisnotrequiredtodeterminecollectionsize.Usingstar-projectionsyntaxreducestheneedforvariancemodifiersaslongaswedon'tuseanymethodsthatdependonatypeargument.

Page 325: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

TypeparameternamingconventionsTheofficialJavatypeparameternamingconvention(https://docs.oracle.com/javase/tutorial/java/generics/types.html)definesthefollowingguidelinesforparameternaming:

Byconvention,typeparameternamesaresingle,uppercaseletters.Thisstandsinsharpcontrasttothevariablenamingconventionsthatyoualreadyknowabout,andwithgoodreason.Withoutthisconvention,itwouldbedifficulttotellthedifferencebetweenatypevariableandanordinaryclassorinterfacename.Themostcommonlyusedtypeparameternamesare:

E:Element(usedextensivelybytheJavaCollectionsFramework)K:KeyN:NumberT:TypeV:ValueS,U,V,andsoon:2nd,3rd,4thtypes

ManyclassesintheKotlinstandardlibraryfollowthisconvention.Itworksfineforpopularkindsofclassessuchascommonclasses(List,Mat,Set,andsoon)orclassesthatdefineasimpletypeparameter(Box<T>class).However,withcustomclassesandmultipletypeparameters,wequicklyrealizethatasingleletterdoesnotcontainasufficientamountofinformationandsometimesit'shardtoquicklytellwhatkindofdatathetypeparameterrepresents.Thereareafewsolutionsforthisproblem.

Wecouldmakesurethatgenericsareproperlydocumentedand,yes,thiswoulddefinitelyhelp,butwestillwouldn'tbeabletodeterminethemeaningofatypeparameterjustbylookingatthecode.Documentationisimportant,butweshouldtreatdocumentationasanauxiliarysourceofinformationandstriveforthehighestpossiblecodereadability.

Overtheyears,programmershavestartedtomigrateintomoremeaningfulnamingconventions.TheGoogleJavaStyleGuide(https://google.github.io/styleguide/javaguide.html#s5.2.8-type-variable-names)brieflydescribesamixoftheofficialJavatypeparameternamingconventionandcustomnamingconventions.Theypromote

Page 326: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

twodistinctstyles.Thefirstistouseasinglecapitalletter,optionallyfollowedbyasinglenumeral(asopposedtotheS,U,VnamesdescribedbyJava):

classBox<T,T2>

Thesecondstyleismoredescriptivebecauseitaddsameaningfulprefixfortypeparameter:

classBox<RequestT>

Unfortunately,thereisnosinglestandardfortypeparameternames.Themostcommonsolutionistheuseofasingleuppercaseletter.Thosearesimplifiedexamples,butkeepinmindthatclassesusuallyusegenericsinmultipleplaces,sopropernamingwillimproveyourcodereadability.

Page 327: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

SummaryInthischapter,wehavelearnedwhygenericsexistandwehavediscussedvariouswaysofdefiningagenericclassandinterface,anddeclaringgenerictypes.Weknowhowtodealwithsubtypingrelationsbyusinguse-siteanddeclaration-sitevariancemodifiers.Welearnedhowtodealwithtypeerasureandhowtopreservegenerictypeatruntimeusingreifiedtypeparameters.

Inthenextchapter,wewilldiscussoneofthemostexcitingKotlinfeatures-extensions.Thisfeatureallowsustoaddnewbehaviortoanexistingclass.Wewilllearnhowwecanimplementnewmethodsandpropertiesforanygivenclass,includingfinalclassesfromtheAndroidframeworkandthird-partylibraries.

Page 328: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ExtensionFunctionsandPropertiesInpreviouschapters,mostoftheconceptswerefamiliartoJavadevelopers.Inthischapter,weareintroducingafeaturethatwasnotknowninJavaatall--extensions.ItisoneofthebestKotlinfeatures,andlotsofKotlindevelopersarementioningitastheirfavoriteone.ExtensionsaremakingabigimprovementinAndroiddevelopment.

Inthischapter,wewillcoverthefollowingtopics:

ExtensionfunctionsExtensionpropertiesMemberextensionfunctionsGenericextensionfunctionsCollectionprocessingFunctiontypewithreceiverandfunctionliteralwithreceiverKotlingenericextensionfunctionstoanyobjectKotlindomain-specificlanguage

Page 329: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ExtensionfunctionsAllbiggerJavaprojectshaveutilityclasses,suchasStringUtils,ListUtils,AndroidUtils,andsoon.Itissopopularbecauseutilfunctionscapturecommonpatternsandallowthemtobetestedandusedinasimplerway.TheproblemwasthatJavareallypoorlysupportsthecreationandusageofsuchfunctions,becausetheyhavetobeimplementedasstaticfunctionsofsomeclass.Let'sdiscussthisproblemwithanexample.EveryJavaAndroiddeveloperknowswellthefollowingcodeusedtoshowToast:

Toast.makeText(context,text,Toast.LENGTH_SHORT).show();

ItiscommonlyusedinAndroidprojectsforshowingerrorsorshortmessages,andoftenitispresentedatthebeginningofmostAndroidtutorials.Codethatimplementsthisfunctionalityisverbose,becauseofhowitisusingastaticfunctionthatisusedlikeabuilder.ProbablyeveryJavaAndroiddeveloperatleastoncehasforgottentoinvoketheshowmethodonareturnedobject,whichmadehimcheckallsurroundingconditionstofindoutwhythisisnotworking.Thisallmakesthissimplefunctionalityaperfectcandidatetobepackedasanutilfunction.Butitisreallyrarelyusedthisway.Why?Tounderstandit,let'sfirstlookathowitcouldbeimplementedinJava:

publicclassAndroidUtils{

publicstaticvoidtoast(Contextcontext,Stringtext){

Toast.makeText(context,text,Toast.LENGTH_SHORT).show();

}

}

//Usage

AndroidUtils.toast(context,"Sometoast");

Whenaprogrammerwantstousethefollowingfunction,theyneedstorememberthatthereissuchfunction,inwhichclassitislocalized,andwhatitsnameis.Therefore,itsusageisnotsimplerthanprevious.ItisimpossibletoimplementitasamethodofContext(asuperclassofActivity)withoutchangingtheAndroidSDKimplementation,butinKotlin,itispossibletocreateanextensionfunction,whichactssimilarlytoanactualmethoddefinedinsideaclass.HereishowwecanimplementtoastasanextensiontoContext:

funContext.toast(text:String){//1

Page 330: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Toast.makeText(this,text,LENGTH_LONG).show()//2

}

//Usage

context.toast("Sometoast")

1. Contextisnotontheargumentlist,butbeforethefunctionname.Thisishowwedefinewhattypeweareextending.

2. Insidethefunctionbody,wecanusethethiskeywordtoreferencetheobjectonwhichtheextensionfunctionisinvoked.

Theonlydifferenceinthegeneralstructurebetweenanextensionfunctionandastandardfunctionisthatthereisareceivertypespecifiedbeforethefunctionname.Alessvisiblechangeisinsidethebody--there,wecanaccessthereceiverobject(theobjectonwhichanextensioniscalled)bythethiskeyword,ordirectlycallitsfunctionsorproperties.Withsuchadefinition,thetoastfunctionactslikeamethoddefinedinContext:

context.toast("Sometoast")

Alternatively:

classMainActivity:Activity(){

overridefunonCreate(savedInstanceState:Bundle?){

super.onCreate(savedInstanceState)

toast("Sometext")

}

}

Thismakesusageofthetoastfunctionmucheasierthanimplementationofthewholetoast-displayingcode.WealsogetsuggestionsfromtheIDE,thatwecaninvokethisfunctionwhenweareinsideContext(likeinsideActivity)oronaninstanceofContext:

Page 331: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Intheprecedingexample,Contextisareceivertypeofthetoastfunction,andthethisinstanceisareferencetothereceiverobject.Allfunctionsandpropertiesofthereceiverobjectcanbeaccessedexplicitly,sowecantakethefollowingdefinition:

funCollection<Int>.dropPercent(percent:Double)

=this.drop(floor(this.size*percent)

Wecanthenreplaceitwiththefollowing:

funCollection<Int>.dropPercent(percent:Double)

=drop(floor(size*percent))

Therearemultipleusecaseswhereextensionfunctionsareuseful.SimilarextensionfunctionscanbedefinedforView,List,String,andotherclassesdefinedintheAndroidframeworkorathird-partylibraryandcustomclassesdefinedbythedeveloper.Extensionfunctionscanbeaddedtoanyaccessibletype,eventotheAnyobject.Hereisanextensionfunctionthatcanbecalledoneveryobject:

funAny?.logError(error:Throwable,message:String="error"){

Log.e(this?.javaClass?.simpleName?:"null",message,error)

}

Herearesomecallexamples:

user.logError(e,"NameError")//Logs:User:NameError...

"String".logError(e)//String:error...

logError(e)//1,MainActivity:error...

1. SupposingthatweareinvokingthisinMainActivity.

Wecansimplyaddanymethodtoanyclasswewant.ThisisagreatimprovementforAndroiddevelopment.Withit,wehaveawaytoaddmissingmethodsorpropertiestotypes.

Page 332: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ExtensionfunctionsunderthehoodWhileKotlinextensionfunctionsmightlookmagical,theyarereallysimpleunderthehood.Atop-levelextensionfunctioniscompiledtoastaticfunctionwithareceiverobjectonthefirstargument.Let'slookatthealreadypresentedtoastfunction:

//ContextExt.kt

funContext.toast(text:String){

Toast.makeText(this,text,LENGTH_LONG).show()

}

Thisfunction,aftercompilationanddecompilationtoJava,wouldlooksimilartothefollowingfunction:

//Java

publicclassContextExtKt{

publicstaticvoidtoast(Contextreceiver,Stringtext){

Toast.makeText(receiver,text,Toast.LENGTH_SHORT).show();

}

}

Kotlintop-levelextensionfunctionsarecompiledtostaticfunctionswithareceiverobjectonthefirstparameter.ThisiswhywecanstilluseextensionsfromJava:

//Java

ContextExtKt.toast(context,"Sometoast")

Also,thismeansthatfromaJVMbytecodeperspective,themethodisnotreallyadded,butduringcompilationallextensionfunctionusagesarecompiledtostaticfunctioncalls.Whileextensionfunctionsarejustfunctions,functionmodifierscanbeappliedtothemthesameastheycanbealsoappliedtoanyotherfunction.Forexample,anextensionfunctioncanbemarkedasinline:

inlinefunContext.isPermissionGranted(permission:String):Boolean=ContextCompat.checkSelfPermission(this,permission)==PackageManager.PERMISSION_GRANTED

Aswithotherinlinefunctions,thefunctioncallwillbereplacedwithanactualbodyduringapplicationcompilation.Wecandowithextensionfunctionspracticallyeverythingwecandowithotherfunctions.Theycanbesingleexpression,havedefaultarguments,beusedbynamedparameters,andsoon.

Page 333: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Buttherearealsoother,lessintuitiveconsequencesofsuchimplementation.Inthenextsections,wearegoingtodescribethem.

Page 334: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

NomethodoverridingWhenthereisamemberfunctionandanextensionfunctionwiththesamenameandparameters,thememberfunctionalwayswins.Hereisanexample:

classA{

funfoo(){

println("foofromA")

}

}

funA.foo(){

println("foofromExtension")

}

A().foo()//Prints:foofromA

Thisisalwaystrue.Evenmethodsfromasuperclasswinwithextensionfunctions:

openclassA{

funfoo(){

println("foofromA")

}

}

classB:A()

funB.foo(){

println("foofromExtension")

}

A().foo()//foofromA

Thepointisthattheextensionfunctionisnotallowingtomodifythebehaviorofarealobject.Wecanonlyaddextrafunctionalities.Thiskeepsussecured,becauseweknowthatnoonewillchangethebehaviorofobjectsthatweareusing,whichmightleadtoerrorsthatarehardtotrack.

Page 335: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

AccesstoreceiverelementsAnextensionfunctioniscompiledtoastaticfunctionwithareceiverobjectonthefirstparameter,sowehavenoextraaccessprivilege.Theprivateandprotectedelementsarenotaccessible,andelementswithJavadefault,Javapackage,orKotlininternalmodifiersareaccessedthesameasifwewouldjustoperateonstandardobject.

Thankstothat,theseelementsareprotectedastheyshouldbe.Rememberthatextensionfunctions,whilebeingreallypowerfulanduseful,arejustsyntacticsugar,andthereisnomagicthere.

Page 336: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ExtensionsareresolvedstaticallyExtensionfunctionsarejustfunctionswithareceiverasthefirstparameter,sotheircallsareresolvedatcompiletimebythetypeonwhichthefunctionisinvoked.Forexample,whenthereareextensionfunctionsforbothsuperclassandsubclass,thentheextensionfunctionsthatwillbechosenduringinvocationdependonthetypeofpropertyonwhichweareoperating.Hereisanexample:

abstractclassA

classB:A()

funA.foo(){println("foo(A)")}

funB.foo(){println("foo(B)")}

valb=B()

b.foo()//prints:foo(B)

(basA).foo()//1,prints:foo(A)

vala:A=b

a.foo()//1,prints:foo(A)

1. Herewewouldexpectfoo(B),whiletheobjectis,infact,oftypeB,butwhileextensionsareresolvedstatically,itisusinganextensionfunctionforA,becausethevariableisoftypeAandthereisnoinformationastowhatobjectisthereduringcompilation.

Thisfactissometimesproblematic,because,whenwedefineanextensionfunctiontothetypewearemostoftencastto,thenweshouldnotimplementextensionfunctionstoitssubclasses.

Thisisanimportantlimitation,andshouldbekeptinmind,especiallyduringpubliclibraryimplementation,becausethisway,someextensionfunctionscanblockothersandcauseunexpectedbehavior.

Page 337: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

CompanionobjectextensionsIfaclasshasacompanionobjectdefined,thenyoucanalsodefineextensionfunctions(andproperties)forthiscompanionobject.Todistinguishbetweenanextensiontoaclassandanextensiontoacompanionobject,thereneedstobe.Companionaddedbetweentheextensiontypeandfunctionname:

classA{

companionobject{}

}

funA.Companion.foo(){print(2)}

Whenitisdefined,thefoomethodcanbeusedasifitweredefinedinsidetheAcompanionobject:

A.foo()

Notethatwearecallingthisextensionusingclasstype,notclassinstance.Toallowthecreationofanextensionfunctionforacompanionobject,thereneedstobeacompanionobjectexplicitlydefinedinsidetheclass.Evenanemptyone.Withoutit,itisimpossibletodefineanextensionfunction:

Page 338: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

OperatoroverloadingusingextensionfunctionsOperatoroverloadingisabigKotlinfeature,butoftenweneedtouseJavalibrariesandoperatorsthatarenotdefinedthere.Forexample,inRxJava,weusetheCompositeDisposablefunctiontomanagesubscriptions.Thiscollectionusestheaddmethodtoaddnewelements.ThisisanexamplesubscriptionaddedtoCompositeDisposable:

valsubscriptions=CompositeDisposable()

subscriptions.add(repository

.getAllCharacters(qualifiedSearchQuery)

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.subscribe(this::charactersLoaded,view::showError))

ThestandardKotlinwaytoaddanewelementtoamutablecollectionisbyusingtheplusAssignoperator(+=).Itisnotonlymoreuniversal,butalsocleaner,whilewecanomitbrackets:

vallist=mutableListOf(1,2,3)

list.add(1)

list+=1

Toapplyitinourexample,wecanaddthefollowingextension:

operatorfunCompositeDisposable.plusAssign(disposable:Disposable)

{

add(disposable)

}

AndnowwecanusetheplusAssignmethodonCompositeDisposable:

subscriptions+=repository

.getAllCharacters(qualifiedSearchQuery)

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.subscribe(this::charactersLoaded,view::showError)

Page 339: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Whereshouldtop-levelextensionfunctionsbeused?Extensionfunctionsaremostoftenusedwhenwefeelthataclassdefinedbyotherprogrammersismissingsomemethod.Forexample,ifwethinkthatViewshouldcontainshowandhidemethods,usageforwhichwouldbeeasierthanvisibilityfieldsetting,thenwecanjustimplementitourselves:

funView.show(){visibility=View.VISIBLE}

funView.hide(){visibility=View.GONE}

Thereisnoneedtorememberthenamesofclassesthatholdutilfunctions.IntheIDE,wejustputadotaftertheobject,andwecansearchthroughallmethodsthatareprovidedtogetherwiththisobjectextensionfunctionsfromtheprojectandlibraries.Invocationlooksgood,whileitlookslikeanoriginalobjectmember.Thisisthebeautyofextensionfunctions,butitisalsoadanger.Rightnow,therearealreadytonsofKotlinlibrariesthatarejustpacksofextensionfunctions.Whenweuselotsofextensionfunctions,wecanmakeourAndroidcodeunlikenormalAndroidcode.Thishasbothprosandcons.Herearethepros:

CodeisshortandmorereadableCodepresentsmorelogicinsteadofAndroidboilerplateExtensionfunctionsaremostoftentested,oratleastusedinmultipleplaces,soitissimplertofindoutiftheyareworkingcorrectlyWhenweuseextensionfunctions,thereisasmallerchancethatwewillmakeastupiderrorthatwillleadtohoursofcodedebugging

Toillustratethelasttwopoints,wewillgobacktothetoastfunction.Itishardtomakeanerrorinwritingthefollowing:

toast("Sometext")

Whileitismucheasiertomakeanerrorinthefollowing:

Toast.makeText(this,"Sometext",Toast.LENGTH_LONG).show()

Page 340: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Thebiggestproblemwithstrongextensionusageinaprojectisthatweare,infact,makingourownAPI.Wearenamingandimplementingfunctionsandwedecidewhatargumentsshouldbethere.Whensomedeveloperjoinstheteam,heneedtolearntheentireAPI,we'vecreated.TheAndroidAPIhaslotsofshortcoming,butitsstrengthisthatitisuniversalanditisknowntoallAndroiddevelopers.

Doesthismeanweshouldresignfromextensions?Absolutelynot!Thisisagreatfeaturethatishelpingustomakecodeshortandclean.Thepointisthatweshouldusetheminasmartway:

Avoidmultipleextensionsthataredoingthesamething.Shortandsimplefunctionalityoftendoesn'tneedtobeanextension.Keeponecodingstylearoundtheproject.Talktoyourteamandspecifysomestandards.Becarefulwhenyouareusingpubliclibrarieswithextensions.KeepthemasthecodethatyoucannotchangeandmatchyourextensionstothemtokeeptheAPIclear.

Page 341: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ExtensionpropertiesInthissection,wewillfirstunderstandwhatextensionpropertiesare,andthenwewillmoveontolearnwherethesepropertiescanbeused.Aswealreadyknow,propertiesinKotlinaredefinedbytheiraccessors(getterandsetter):

classUser(valname:String,valsurname:String){

valfullName:String

get()="$name$surname"

}

Wecandefinealsoextensionproperty.Theonlylimitationisthatthispropertycan’thavebackingfield.Thereasonforthisisthatextensioncan’tstorestate,sothereisnogoodplacetostorethisfield.HereisanexampleofextensionpropertydefinitionforTextView:

valTextView.trimmedText:String

get()=text.toString().trim()

//Usage

textView.trimmedText

Aswithextensionfunctions,theaboveimplementationwillbecompiledasanaccessorfunctionwithareceiveronthefirstparameter.HereisthesimplifiedresultinJava:

publicclassAndroidUtilsKt{

StringgetTrimmedText(TextViewreceiver){

returnreceiver.getText().toString().trim();

}

}

Ifitwerearead-writeproperty,thenbothsetterandgetterwouldbeimplemented.Rememberthatonlypropertiesthatdon'tneedaJavafieldareallowedtobedefinedasanextensionproperty.Forexample,thisisillegal:

Page 342: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava
Page 343: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Whereshouldextensionpropertiesbeused?Extensionpropertiescanoftenbeusedinterchangeablywithextensionfunctions.Theyarebothmostoftenusedastop-levelutils.Extensionpropertiesareusedwhenwewouldlikeanobjecttohavesomepropertythatwasnotdevelopednatively.Thedecisionastowhetherweshoulduseanextensionfunctionoranextensionpropertyisnearlythesameasthedecisionastowhetherweshoulduseafunctionorpropertywithoutabackingfieldinsideaclass.Justtoremindyou,accordingtoconventions,oneshouldpreferapropertyoverafunctionwhentheunderlyingalgorithmfulfillsthefollowingconditions:

DoesnotthrowerrorsHasO(1)complexityIscheaptocalculate(orcaсhedonthefirstrun)Returnsthesameresultoverinvocations

Let'slookatasimpleproblem.WeoftenneedtogetsomeservicesinAndroid,butthecodeusedtogetthemiscomplicated:

PreferenceManager.getDefaultSharedPreferences(this)

getSystemService(Context.LAYOUT_INFLATER_SERVICE)asLayoutInflater

getSystemService(Context.ALARM_SERVICE)asAlarmManager

TouseaservicesuchasAlarmManagerorLayoutInflater,theprogrammerhastorememberthefollowingforeachofthem:

Thenameofthefunctionthatisprovidingit(suchasgetSystemService)andwhatclasscontainsit(suchasContext)Thenameofthefieldthatisspecifyingthisservice(suchasContext.ALARM_SERVICE)Thenameoftheclassthattheserviceshouldbecastto(suchasAlarmManager)

Thisiscomplex,andthisistheperfectplacewherewecanoptimizeusagethankstoextensionproperties.Wecandefineextensionpropertiesthisway:

valContext.preferences:SharedPreferences

Page 344: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

get()=PreferenceManager

.getDefaultSharedPreferences(this)

valContext.inflater:LayoutInflater

get()=getSystemService(Context.LAYOUT_INFLATER_SERVICE)

asLayoutInflater

valContext.alarmManager:AlarmManager

get()=getSystemService(Context.ALARM_SERVICE)

asAlarmManager

Andfromnowon,wecanusepreferences,inflater,andalarmManagerasiftheyarepropertiesofContext:

context.preferences.contains("SomeKey")

context.inflater.inflate(R.layout.activity_main,root)

context.alarmManager.setRepeating(ELAPSED_REALTIME,triggerAt,

interval,pendingIntent)

Theseareperfectexamplesofgoodread-onlyextensionfunctionusage.Let'sfocusontheinflaterextensionproperty.Itishelpingtogetelementsthatareoftenneeded,buthardtogetwithoutextensions.Itishelpful,becausetheprogrammerjustneedstorememberthatwhattheyneedisaninflaterandthattheyneedContexttohaveit,andhedoesnotneedtorememberthenameofmethodthatisprovidingsystemservices(getSystemService),thenameofthekeyusedtogettheinflaterproperty(ALARM_SERVICE),whereitislocated(inContext),andwhatthisserviceshouldbecastto(AlarmManager).Inotherwords,thisextensionissavingalotofworkandprogrammermemory.Also,itiscorrectaccordingtoguidelines,becausethetimeofpropertygetterexecutionisshortanditscomplexityisO(1),itisnotthrowinganyerrors,anditisalwaysreturningthesameinflater(infact,itmightbeadifferentinstance,butfromaprogrammerperspective,itsusageisalwaysthesame,andthisiswhatisimportant).

We'veseenread-onlyextensionproperties,butwehavenotseenread-writeextensionproperties.Hereisagoodexample,thatisanalternativetothehideandshowfunctionsthatwesawintheExtensionfunctionssection:

varView.visible:Boolean

get()=visibility==View.VISIBLE

set(value){

visibility=if(value)View.VISIBLEelseView.GONE

}

Wecanchangethevisibilityoftheviewelementusingthisproperty:

button.visible=true//thesameasshow()

button.visible=false//thesameashide()

Page 345: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Also,wecancheckviewelementvisibility:

if(button.visible){/*...*/}

Oncewedefineit,wecantreatisasifitreallywereaViewproperty.Itisalsoimportantthatwhatwearesettingisconsistentwithwhatwearegetting.Sosupposingthatthereisnootherthreadwhichischangingelementvisibility,wecansetsomepropertyvalue:

view.visible=true

Thenthegetterwillalwaysprovidethesamevalue:

println(view.visible)//Prints:true

Finally,thereisnootherlogicinsidethegetterandsetter--onlyachangeinspecificproperties.Sootherconventionswe'vepresentedbeforearesatisfiedtoo.

Page 346: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

MemberextensionfunctionsandpropertiesWe'veseentop-levelextensionfunctionsandproperties,butitisalsopossibletodefinetheminsideaclassorobject.Extensionsdefinedtherearecalledmemberextensions,andtheyaremostoftenusedfordifferentkindsofproblemsthantop-levelextensions.

Let'sstartfromthesimplestusecasewherememberextensionsareused.Let'ssupposethatweneedtodropeverythirdelementofalistofString.Hereistheextensionfunctionthatallowsustodropeveryithelement:

funList<String>.dropOneEvery(i:Int)=

filterIndexed{index,_->index%i==(i-1)}

Theproblemwiththatfunctionisthatitshouldnotbeextractedasautilextension,becauseofthefollowing:

Itisnotpreparedfordifferenttypesoflists(suchasalistofUser,orInt)Itisararelyusefulfunction,soprobablyitwon'tbeusedanywhereelseintheproject

Thisiswhywewouldwanttokeepitasprivate,anditisagoodideatokeepitinsidetheclasswhereweareusingit,asanmemberextensionfunction:

classUsersItemAdapter:ItemAdapter(){

lateinitvarusersNames:List<String>

funprocessList(){

usersNames=getUsersList()

.map{it.name}

.dropOneEvery(3)

}

funList<String>.dropOneEvery(i:Int)=

filterIndexed{index,_->index%i==(i-1)}

//...

}

Thisisthefirstreasonweusememberextensionfunctions,toprotecttheaccessibilityoffunctions.Inthiscase,itcouldbedonebydefiningafunctionon

Page 347: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

thetoplevel,inthesamefile,andwithaprivatemodifier.Butmemberextensionfunctionsactdifferentlytotop-levelfunctions.Thefunctionusedintheprecedingcodeispublic,butitcanonlybecalledonList<String>andonlyinUsersItemAdapter.SoitcanbeusedonlyinsidetheUsersItemAdapterclassanditssubclassesorinsideanextensionfunctiontoUsersItemAdapter:

funUsersItemAdapter.updateUserList(newUsers:List<User>){

usersNames=newUsers

.map{it.name}

.dropOneEvery(3)

}

Notethattouseamemberextensionfunction,weneedboththeobjectinwhichitisimplementedandtheobjectonwhichthisextensionfunctionswillbecalled.Itisthiswaybecausewecanuseelementsofbothoftheseobjects.Thisisimportantinformationaboutmemberextensions:theycanusebothelementsfromreceivertypeandfrommembertypewithoutaqualifier.Let'sseehowitmightbeused.Hereisanotherexample,whichissimilartothepreviousone,butitisusingtheprivatepropertycategory:

classUsersItemAdapter(

privatevalcategory:Category

):ItemAdapter(){

lateinitvarusersNames:List<String>

funprocessList(){

usersNames=getUsersList()

.fromSameCategory()

.map{it.name}

}

funList<User>.fromSameCategory()=

filter{u->u.category.id==category.id}

privatefungetUsersList()=emptyList<User>()

}

InsidethememberextensionfunctionfromSameCategory,weareoperatingonanextensionreceiver(List<User>),butwearealsousingthecategorypropertyfromUsersItemAdapter.Weseeherethatafunctiondefinedthiswayneedstobeamethodanditcanbeusedsimilarlytoothermethods.TheadvantageoverthestandardmethodisthatwecancallafunctiononList,sowecankeepcleanstreamprocessing,insteadofnon-extensionmethodusage:

//fromSameCategorydefinedasstandardmethod

usersNames=fromSameCategory(newUsers)

.dropLast(3)

Page 348: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

//fromSameCategorydefinedasmemberextensionfunction

usersNames=newUsers

.fromSameCategory()

.dropLast(3)

Anothercommonusageisthememberextensionfunctionsorpropertiescanbeusedlikenormalmethods,butweareusingthefactthatinsidememberfunctionswecanusereceiverpropertiesandmethodswithoutnamingthem,thiswaywecanhaveshortersyntax,andthatweareactuallycallingthemonareceiverinsteadofcallingthemwiththesametypeasanargument.Asanexample,wecantakethefollowingmethod:

privatefunsetUpRecyclerView(recyclerView:RecyclerView){

recyclerView.layoutManager

=LinearLayoutManager(recyclerView.context)

recyclerView.adapter

=MessagesAdapter(mutableListOf())

}

//Usage

setUpRecyclerView(recyclerView)

Thenwecanreplaceitwiththefollowingmemberextensionfunction:

privatefunRecyclerView.setUp(){

layoutManager=LinearLayoutManager(context)

adapter=MessagesAdapter(mutableListOf())

}

//Usage

recyclerView.setUp()

Usingmemberextensionfunctions,wecanachievebothasimplercallandasimplerfunctionbody.ThebiggestproblemwiththisattemptisthatitisnotclearwhichfunctionsweareusingaremembersofRecyclerView,andwhicharemembersoftheActivityandRecyclerViewextensions.Thisproblemwillberaisedinthenextpages.

Page 349: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

TypeofreceiversWhenwehaveamemberextensionfunction,thenitbecomesmorecomplicatedtoadministerwhichelementswearecalling.Insideamemberextension,wehaveimplicitaccesstothefollowing:

Memberfunctionsandproperties,bothfromthisclassandsuperclassesReceivertypefunctionsandproperties,bothfromthereceivertypeanditssupertypesTop-levelfunctionsandproperties

SoinsidethesetUpextensionfunction,wecanusebothmemberandreceivermethodsandproperties:

classMainActivity:Activity(){

overridefunonCreate(savedInstanceState:Bundle?){

super.onCreate(savedInstanceState)

setContentView(R.layout.main_activity)

valbuttonView=findViewById(R.id.button_view)asButton

buttonView.setUp()

}

privatefunButton.setUp(){

setText("Clickme!")//1,2

setOnClickListener{showText("Hello")}//2

}

privatefunshowText(text:String){

toast(text)

}

}

1. setTextistheButtonclassmethod.2. WecanusetheButtonclassandMainActivityclassmembersalternately.

Itmightbetricky--probablymostpeoplewouldn'tnoticeiftherewereanerrorandthesetTextcallwouldbeswappedwiththeshowTextcall.

Whilewecanuseinsidememberextensionelementsfromdifferentreceivers,toallowdistinctionbetweenthem,allkindsofreceiverswerenamed.Firstofall,allobjectsthatcanbeusedbythethiskeywordarecalledimplicitreceivers.They'rememberscanbeaccessedwithoutaqualifier.InsidesetUpfunctions,

Page 350: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

therearetwoimplicitreceivers:

Extensionreceiver:Aninstanceoftheclassthattheextensionisdefinedfor(Button)DispatchReceiver:Aninstanceoftheclassinwhichtheextensionisdeclared(MainActivity)

Notethatwhilemembersofboththeextensionreceiveranddispatchreceiverareimplicitreceiversinthesamebody,itispossibletohaveasituationwhereweusemembersthathavethesamesignatureinbothofthem.Forexample,ifwechangethepreviousclasstoshowtextintextViewinsteadofshowingitinthetoastfunction,andchangethemethodnametosetText,thenwearegoingtohavemethodsofdispatchandextensionreceiverwiththesamesignature(onedefinedintheButtonclass,theotherdefinedintheMainActivityclass):

classMainActivity:Activity(){

overridefunonCreate(savedInstanceState:Bundle?){

super.onCreate(savedInstanceState)

setContentView(R.layout.main_activity)

valbuttonView=findViewById(R.id.button_view)asButton

buttonView.setUp()

}

privatefunButton.setUp(){

setText("Clickme!")

setOnClickListener{setText("Hello")}//1

}

privatefunsetText(text:String){

textView.setText(text)

}

}

1. setTextisboththemethodofthedispatchreceiverandtheextensionreceiver.Whichonewillbecalled?

Asaresult,thesetTextfunctionwillbeinvokedfromtheextensionreceiver,andasaresult,abuttonclickwillchangethetextoftheclickedbutton!Thisisbecausetheextensionreceiveralwaystakesprecedenceoverthedispatchreceiver.Still,itispossibletouseadispatchreceiverinthissituationbyusingqualifiedthissyntax(thethiskeywordwithlabel,thatis,distinguishingwhichreceiverwewanttoreference):

privatefunButton.setUp(){

setText("Clickme!")

setOnClickListener{

Page 351: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

[email protected]("Hello")

}

}

Thisway,wecansolvetheproblemofdistinguishingbetweenthedispatchandextensionreceiver.

Page 352: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

MemberextensionfunctionsandpropertiesunderthehoodMemberextensionfunctionsandpropertiesarecompiledthesamewayastop-levelextensionfunctionsandpropertieswiththeonlydifferencebeingthattheyareinsideaclassandtheyarenotstatic.Hereisasimpleexampleofanextensionfunction:

classA{

funboo(){}

funInt.foo(){

boo()

}

}

Thisiswhatitiscompiledto(aftersimplification):

publicfinalclassA{

publicfinalvoidboo(){

...

}

publicfinalvoidfoo(int$receiver){

this.boo();

}

}

Notethatwhiletheyarejustmethodswithareceiverasthefirstparameter,wecandowiththemeverythingwecanwithotherfunctions.Accessmodifiersareworkingthesameway,andifwedefinethememberextensionfunctionasopen,thenwecanoverrideitinitssubclasses.

Page 353: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

GenericextensionfunctionsWhenwearewritingutilityfunctions,oftenwewantthemtobegeneric.Themostcommonexamplesareextensionsforcollections:List,Map,andSet.HereisanexampleofanextensionpropertyforList:

val<T>List<T>.lastIndex:Int

get()=size-1

Theprecedingexampledefinesanextensionpropertyforagenerictype.Thiskindofextensionisusedforlotsofdifferentproblems.Asanexample,startinganotherActivityisarepetitivetaskthatmostoftenneedstobeimplementedinmultipleplacesintheproject.ThemethodsprovidedbytheAndroidIDEforActivitystartingdonotmakeiteasy.HereisthecodeusedtostartanewActivitycalledSettingsActivity:

startActivity(Intent(this,SettingsActivity::class.java))

Notethatthissimpleandrepetitivetaskneedsalotofcodethatisnotreallyclear.ButwecandefineextensionfunctionsthatwillmakeIntentcreationandActivitywithoutargumentsstartmuchmoresimplyusingagenericinlineextensionfunctionwithreifiedtype:

inlinefun<reifiedT:Any>Context.getIntent()

=Intent(this,T::class.java)

inlinefun<reifiedT:Any>Context.startActivity()

=startActivity(getIntent<T>())

NowwecanstartActivitybysimplyusingthefollowing:

startActivity<SettingsActivity>()

Orwecancreateintentthisway:

valintent=getIntent<SettingsActivity>()

Thisway,wecanmakethiscommontaskeasieratlowcost.Togofurther,librariessuchasAnko(https://github.com/Kotlin/anko)provideextensionfunctionsthatprovideasimplewaytostartanActivitywithadditionalparametersorflags,asinthisexample:

Page 354: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

startActivity<SettingsActivity>(userKeytouser)

Internalimplementationofthelibraryisoutsidethescopeofthisbook,butwecanusethisextensionsimplybyaddingAnkolibrarydependencytoourproject.Thepointofthisexampleisthatnearlyallrepetitivecodecanbereplacedwithsimplercodeusingextensions.TherearealsoalternativewaystostartanActivity,suchastheActivityStarterlibrary(https://github.com/MarcinMoskala/ActivityStarter),whichisbasedonparameterinjection,andthatstronglysupportsKotlin.Itallowsclassicargumentinjection:

classStudentDataActivity:BaseActivity(){

lateinit@Argvarstudent:Student

@Arg(optional=true)varlesson:Lesson=Lesson.default()

}

Or,asanalternative,itallowslazyinjectioninKotlinpropertydelegates(whicharedescribedinChapter8,Delegates):

classStudentDataActivity:BaseActivity(){

@get:Argvalstudent:StudentbyargExtra()

@get:Arg(optional=true)

varlesson:LessonbyargExtra(Lesson.default())

}

Activitywithsuchargumentscanbestartedusinggeneratedstaticfunctions:

StudentDataActivityStarter.start(context,student,lesson)

StudentDataActivityStarter.start(context,student)

Let'sseeanotherexample.InAndroid,weoftenneedtostoreobjectsinJSONformat.Forexample,whenweneedtosendthemtoanAPIortostoretheminafile.ThemostpopularlibraryusedforserializinganddeserializingobjectsintoJSONisGson.Let'slookatstandardwayofusingtheGsonlibrary:

valuser=User("Marcin","Moskala")

valjson:String=globalGson.toJson(user)

valuserFromJson=globalGson.fromJson(json,User::class.java)

WecanimproveitinKotlinthankstoextensionfunctionswithaninlinemodifier.HereisanexampleofextensionfunctionsthatareusingGSONtopackandunpackobjectstoStringinJSONformat:

inlinefunAny.toJson()=globalGson.toJson(this)!!

inlinefun<reifiedT:Any>String.fromJson()

=globalGson.fromJson(this,T::class.java)

Page 355: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

//Usage

valuser=User("Marcin","Moskala")

valjson:String=user.toJson()

valuserFromJson:User=json.fromJson<User>()

TheglobalGsoninstanceisaglobalinstanceofGson.Itiscommonpractice,whileweoftendefinesomeserializersanddeserializers,anditisasimplerandmoreeffectivewaytodefinethemandbuildaninstanceofGsononce.

Examplesareshowingwhatpossibilitiesaregenericextensionfunctionsgivingtothedeveloper.Theyarelikethenextlevelofcodeextraction:

Theyaretop-level,butalsoinvokedonanobject,sotheyaresimpletomanageTheyaregeneric,soareuniversalandmightbeappliedtoanythingWheninline,theyallowustodefinereifiedtypeparameters

ThisiswhygenericextensionfunctionsarecommonlyusedinKotlin.Also,standardlibraryprovideslotsofgenericextensions.Inthenextsection,wewillseesomecollectionextensionfunctions.Thispartisimportant,notonlybecauseitprovidesknowledgeaboutgenericextensionfunctionusage,butalsobecauseitisultimatelydescribinghowlistprocessinginKotlinworksandhowitcanbeused.

Page 356: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

CollectionprocessingCollectionprocessingisoneofthemostcommontasksinprogramming.Thisiswhyoneofthefirstthingsthatdeveloperslearnishowtoiterateoveracollectiontooperateonelements.Youngdevelopersaskedtoprintallusersfromalistwillmostprobablyuseaforloop:

for(userinusers){

println(user)

}

Ifweaskedthemtoshowonlyusersthatarepassinginschool,thentheywouldmostprobablyaddanifconditioninsidethisloop:

for(userinusers){

if(user.passing){

println(user)

}

}

Thisisstillcorrectimplementation,buttherealproblemstartswhentaskbecomesmorecomplex.Whatiftheywereaskedtoprintthethreebeststudentsthatarepassing?Itisreallycomplextoimplementitinloops,whileitistrivialtoimplementitusingKotlinstreamprocessing.Let'sseeitontheexample.Hereisexamplelistofstudents:

dataclassStudent(

valname:String,

valgrade:Double,

valpassing:Boolean

)

valstudents=listOf(

Student("John",4.2,true),

Student("Bill",3.5,true),

Student("John",3.2,false),

Student("Aron",4.3,true),

Student("Jimmy",3.1,true)

)

Let'sfilteroutstudentsusinganimperativeapproachknownfromJava(usingloopsandsortingmethod):

valfilteredList=ArrayList<Student>()

for(studentinstudents){

if(student.passing)filteredList+=student

Page 357: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

}

Collections.sort(filteredList){p1,p2->

if(p1.grade>p2.grade)-1else1

}

for(iin0..2){

valstudent=filteredList[i]

println(student)

}

//Prints:

//Student(name=Aron,grade=4.3,passing=true)

//Student(name=John,grade=4.2,passing=true)

//Student(name=Bill,grade=3.5,passing=true)

WecanachievethesameresultinamuchsimplerwayusingKotlinstreamprocessing:

students.filter{it.passing}//1

.sortedByDescending{it.grade}//2

.take(3)//3

.forEach(::println)//4

1. Takeonlystudentswhopassed.2. Sortstudentsaccordingtotheirgrade(descendingtohavestudentswith

bettergradeinhigherposition).3. Takeonlyfirstthreeofthem.4. Printeachofthem.

Thekeyisthateachstreamprocessingfunction,suchassortedByDescending,take,andforEachfromtheprecedingexample,isextractingasmallfunctionalityandthepowercomesfromthecompositionofthem.Andtheresultismuchsimplerandmorereadablethenusageofclassicloops.

Streamprocessingisactuallyaprettycommonlanguagefeature.ItisknowninC#,JavaScript,Scala,andmanyotherlanguages,includingJavasinceversion8.Popularreactiveprogramminglibraries,suchasRxJava,alsoheavilyutilizethisconcepttoprocessdata.Inthissection,wearegoingtogodeeperintoKotlincollectionprocessing.

Page 358: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

KotlincollectiontypehierarchyKotlintypehierarchyisreallywelldesigned.Standardcollectionsareactuallycollectionsfromanativelanguage(suchasJava),whicharehiddenbehindinterfaces.Creationofthemismadebystandardtop-levelfunctions(listOf,setOf,mutableListOf,andsoon),sotheycanbecreatedandusedincommonmodules(modulescompiledtomorethanoneplatform).AlsoKotlininterfacescanactliketheirequivalentinterfacesfromJava(likeList,Set,andsoon),thismakesKotlincollectionsefficientandhighlycompatiblewithexternallibraries.Atthesametime,Kotlincollectioninterfaceshierarchy,canbeusedincommonmodules.Thishierarchyissimpleanditisprofitabletounderstandit:

Kotlincollectioninterfaceshierarchy

ThemostgeneralinterfaceisIterable.Itrepresentsasequenceofelementsthatcanbeiteratedover.Anyobjectthatimplementsiterablecanbeusedinaforloop:

for(iiniterable){/*...*/}

Lotsofdifferenttypesimplementaniterableinterface:allcollections,progressions(1..10,'a'..'z'),andevenString.Theyallallowustoiterateovertheirelements:

for(charin"Text"){print("($char)")}//Prints:(T)(e)(x)(t)

Page 359: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

TheCollectioninterfacerepresentsacollectionofelementsandextendsIterable.Itaddspropertysizeandthemethodscontains,containsAll,andisEmpty.

TwomaininterfacesthatinheritfromCollectionareListandSet.ThedifferencebetweenthemisthatSetisunorderedanddoesnotcontainrepetitiveelements(accordingtotheequalsmethod).BothListandSetinterfacesdonotcontainanymethodsthatwouldallowustomutatetheobjectstate.Thisiswhy,bydefault,Kotlincollectionsaretreatedasimmutable.WhenwehaveaninstanceofList,thenitismostoftenArrayListinAndroid.ArrayListisamutablecollection,butwhileitishiddenbehindtheinterfaceList,itisactuallyactinglikeimmutable,becauseitisnotexposinganymethodsthatwouldallowustoapplychanges(unlessitisdowncasted).

InJava,collectionsweremutable,butKotlincollectioninterfacesprovideonlyimmutablebehaviorbydefault(notmethodsthatchangethestateofcollections,forexample,addandremoveAt):

vallist=listOf('a','b','c')

println(list[0])//Prints:a

println(list.size)//Prints:3

list.add('d')//Error

list.removeAt(0)//Error

Allimmutableinterfaces(Collection,List,andsoon)havetheirmutableequivalents(MutableCollection,MutableList,andsoon)whichinheritfromcorrespondingimmutableinterfaces.Mutablemeansthattheactualobjectcanbemodified.Thesearetheinterfacesthatrepresentmutablecollectionsfromstandardlibrary:

MutableIterableallowsiterationwithapplyingchangesMutableCollectionensuremethodsforaddingandremovingelementsMutableListandMutableSetaremutableequivalentsofListandSet

Nowwecanfixourpreviousexampleandchangethecollectionusingtheaddandremovemethods:

vallist=mutableListOf('a','b','c')

println(list[0])//Prints:a

println(list.size)//Prints:3

list.add('d')

println(list)//Prints:[a,b,c,d]

list.removeAt(0)

println(list)//Prints:[b,c,d]

Page 360: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Bothimmutableandmutableinterfacesprovideonlyafewmethods,buttheKotlinstandardlibraryprovidesmanyusefulextensionsforthem:

ThismakesdealingwithcollectionsamucheasiertaskthaninJava.

Kotlinimplementscollectionprocessingmethodsusingextensions.Thisapproachhasmanyadvantages;forexample,ifwewanttoimplementacustomcollection(suchasList),weonlyneedtoimplementaniterableinterfacecontainingonlyafewmethods.Wecanstillusealltheextensionsthatareprovidedfortheiterableinterface.

Anotherreasonishowflexiblythesefunctionscanbeusedwhentheyareextensionsforinterfaces.Forexample,mostofthesecollectionprocessingfunctionsareactuallyextensionsforIterable,whichisimplementedbymanymoretypesthanCollection,forexample,byStringorRange.Therefore,itis

Page 361: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

possibletouseallextensionfunctionstoIterablealsoonIntRange.Hereisanexample:

(1..5).map{it*2}.forEach(::print)//Prints:246810

Thismakesallthisextensionsreallyuniversal.Thereisalsothedownsideofthefactthatcollectionstreamprocessingmethodsareimplementedasextensionfunctions.Whileextensionsareresolvedstatically,itisincorrecttooverrideanextensionfunctionforaspecifictypebecauseitsbehaviorwillbedifferentwhenitisbehindaninterfacethenwhenitisaccesseddirectly.

Let'sanalyzesomeextensionfunctionsusedforcollectionprocessing.

Page 362: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Themap,filter,flatMapfunctionsWehavealreadybrieflypresentedmap,filter,andflatMap,becausetheyarethemostbasicstreamprocessingfunctions.Themapfunctionreturnsalistwithelementschangedaccordingtothefunctionfromtheargument:

vallist=listOf(1,2,3).map{it*2}

println(list)//Prints:[2,4,6]

Thefilterfunctionallowsonlytheelementsthatmatchtheprovidedpredicate:

vallist=listOf(1,2,3,4,5).map{it>2}

println(list)//Prints:[3,4,5]

TheflatMapfunctionreturnsasinglelistofallelementsyieldedbythetransformfunction,whichisinvokedoneachelementoftheoriginalcollection:

vallist=listOf(10,20).flatMap{listOf(it,it+1,it+2)}

println(list)//Prints:[10,11,12,20,21,22]

Itismostoftenusedtoflattenlistofcollections:

shops.flatMap{it.products}

schools.flatMap{it.students}

Let'slookatsimplifiedimplementationsoftheseextensionfunctions:

inlinefun<T,R>Iterable<T>.map(transform:(T)->R):List<R>{//1

valdestination=ArrayList<R>()

for(iteminthis)destination.add(transform(item))//2

returndestination

}

inlinefun<T>Iterable<T>.filter(predicate:(T)->Boolean):List<T>{//1

valdestination=ArrayList<T>()

for(iteminthis)if(predicate(item))destination.add(item)//2

returndestination

}

inlinefun<T,R>Iterable<T>.flatMap(transform:(T)->Collection<R>):List<R>{

//1

valdestination=ArrayList<R>()

for(iteminthis)destination.addAll(transform(item))//2

returndestination

}

1. Allthisfunctionsareinline.

Page 363: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

2. Allthesefunctionsinternallyuseforloop,andreturnanewlistcontainingproperelements.

MostKotlinstandardlibraryextensionfunctionswithfunctiontypeareinline,becauseitmakeslambdaexpressionusageefficient.Asaresult,wholecollectionstreamprocessingisactuallymostlycompiledatruntimetonestedloops.Asanexample,hereisthissimpleprocessing:

students.filter{it.passing}

.map{"${it.name}${it.surname}"}

AftercompilationanddecompilationtoJava,itlookslikethefollowing(cleanedup):

Collectiondestination1=newArrayList();

Iteratorit=students.iterator();

while(it.hasNext()){

Studentstudent=(Student)it.next();

if(student.getPassing()){

destination1.add(student);

}

}

Collectiondestination2=newArrayList(destination1.size());

it=destination2.iterator();

while(it.hasNext()){

Studentstudent=(Student)it.next();

Stringvar=student.getName()+""+student.getSurname();

destination2.add(var);

}

Page 364: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

TheforEachandonEachfunctionsTheforEachfunctionwasalreadydiscussedinchapteraboutfunctions.Itisanalternativetoaforloop,soitperformsanactiononeachelementofthelist:

listOf("A","B","C").forEach{print(it)}//prints:ABC

SinceKotlin1.1,thereisasimilarfunction,onEach,thatalsoinvokesanactiononeachelement.Itreturnsanextensionreceiver(thislist),sowecaninvokeanactiononeachelementinthemiddleofstreamprocessing.Commonusecasesareloggingpurposes.Hereisanexample:

(1..10).filter{it%3==0}

.onEach(::print)//Prints:369

.map{it/3}

.forEach(::print)//Prints:123

Page 365: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ThewithIndexandindexedvariantsSometimes,thewayofelementprocessingdependsonitsindexonthelist.ThemostuniversalwaytosolvethisproblemisbyusingthewithIndexfunction,whichreturnsalistofvalueswithindexes:

listOf(9,8,7,6).withIndex()//1

.filter{(i,_)->i%2==0}//2

.forEach{(i,v)->print("$vat$i,")}

//Prints:9at0,7at2,

1. FunctionwithIndexispackingeachelementintoIndexedValuewhichiscontainingboththeelementsanditsindex.

2. Inlambda,IndexedValueisdestructedintoindexandvalue,butwhilethevalueisunused,thereisanunderscoreplacedinstead.Itmightbeomitted,butthiswayofcodeismorereadable.Thislinefiltersonlyelementswithevenindex.

Also,therearevariantsfordifferentstreamprocessingmethodsthatprovideanindex:

vallist1=listOf(2,2,3,3)

.filterIndexed{index,_->index%2==0}

println(list1)//Prints:[2,3]

vallist2=listOf(10,10,10)

.mapIndexed{index,i->index*i}

println(list2)//Prints:[0,10,20]

vallist3=listOf(1,4,9)

.forEachIndexed{index,i->print("$index:$i,")}

println(list3)//Prints:0:1,1:4,2:9

Page 366: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Thesum,count,min,max,andsortedfunctionsThesumfunctioncountsthesumofallelementsinalist.ItcanbeinvokedonList<Int>,List<Long>,List<Short>,List<Double>,List<Float>,andList<Byte>:

valsum=listOf(1,2,3,4).sum()

println(sum)//Prints:10

Oftenweneedtosumsomepropertiesofelements,suchassummingpointsofallusers.Itmightbehandledbymappingthelistofuserstothelistofpointsandthencountingthesum:

classUser(valpoints:Int)

valusers=listOf(User(10),User(1_000),User(10_000))

valpoints=users.map{it.points}.sum()

println(points)//Prints:11010

Butweunnecessarilycreateanintermediatecollectionbycallingthemapfunction,anditwouldbemoreefficienttodirectlysumpoints.Todoit,wecanusesumBywithanappropriateselector:

valpoints=users.sumBy{it.points}

println(points)//Prints:11010

sumByisexpectingInttobereturnedfromtheselector,anditisreturningIntwiththesumofallelements.IfvaluesarenotIntbutDoublethenwecanusesumByDouble,whichreturnsDouble:

classUser(valpoints:Double)

valusers=listOf(User(10.0),User(1_000.0),User(10_000.0))

valpoints=users.sumByDouble{it.points}

println(points)//Prints:11010.0

Asimilarfunctionalityisprovidedbythecountfunction,thatisusedwhenweneedtocountelementsthatmatchapredicate:

valevens=(1..5).count{it%2==1}

valodds=(1..5).count{it%2==0}

println(evens)//Prints:3

println(odds)//Prints:2

Page 367: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Thecountfunctionusedwithoutanypredicatereturnsthesizeofthecollectionoriterable:

valnums=(1..4).count()

println(nums)//Prints:4

Thenextimportantfunctionsareminandmax,whicharefunctionsthatreturntheminimalandmaximalelementsinalist.Theycanbeusedonalistofelementsthathavenaturalordering(implementComparable<T>interface).Hereisanexample:

vallist=listOf(4,2,5,1)

println(list.min())//Prints:1

println(list.max())//Prints:5

println(listOf("kok","ada","bal","mal").min())//Prints:ada

Similarly,thefunctionsortedisused.Itreturnsasortedlist,butitneedstobeinvokedoncollectionsofelementsthatimplementtheComparable<T>interface.Hereisanexampleofhowsortedcanbeusedtogetalistofstringssortedalphanumerically:

valstrs=listOf("kok","ada","bal","mal").sorted()

println(strs)//Prints:[ada,bal,kok,mal]

Whatifitemsarenotcomparable?Therearetwowaystosortthem.Thefirstwayistosortaccordingtocomparablemember.We'vealreadyseenanexamplewhenweweresortingstudentsaccordingtotheirgrades:

students.filter{it.passing}

.sortedByDescending{it.grade}

.take(3)

.forEach(::println)

Intheprecedingexample,wesortstudentsusingthecomparablegradeproperty.There,sortedByDescendingisused,whichworkslikesortedBy,withtheonlydifferencebeingthattheorderisdescending(frombiggesttosmallest).Theselectorinsidethefunctioncanreturnanyvaluethatiscomparabletoitself.Hereisanexample,whereStringisusedtospecifyorder:

vallist=listOf(14,31,2)

print(list.sortedBy{"$it"})//Prints:[14,2,31]

Similarfunctionscanbeusedtofindtheminimalandmaximalelementaccordingtotheselector:

Page 368: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

valminByLen=listOf("ppp","z","as")

.minBy{it.length}

println(minByLen)//Prints:"z"

valmaxByLen=listOf("ppp","z","as")

.maxBy{it.length}

println(maxByLen)//Prints:"ppp"

ThesecondwaytospecifysortingorderistodefineaComparatorthatwilldeterminehowelementsshouldbecompared.FunctionvariantsthatacceptcomparatorsshouldhaveaWithsuffix.ComparatorscanbedefinedbyanadapterfunctionthatconvertsalambdatoSAMtype:

valcomparator=Comparator<String>{e1,e2->

e2.length-e1.length

}

valminByLen=listOf("ppp","z","as")

.sortedWith(comparator)

println(minByLen)//Prints:[ppp,as,z]

Kotlinalsoincludesstandardlibrarytop-levelfunctions(compareBy,compareByDescending)usedtosimplifyComparatorcreation.Hereishowwecancreateacomparatortosortstudentsalphanumericallybysurnameandname:

dataclassUser(valname:String,valsurname:String){

overridefuntoString()="$name$surname"

}

valusers=listOf(

User("A","A"),

User("B","A"),

User("B","B"),

User("A","B")

)

valsortedUsers=users

.sortedWith(compareBy({it.surname},{it.name}))

print(sortedUsers)//[AA,BA,AB,BB]

Notethatwecanusepropertyreferenceinsteadoflambdaexpressions:

valsortedUsers=users

.sortedWith(compareBy(User::surname,User::name))

print(sortedUsers)//[AA,BA,AB,BB]

AnotherimportantfunctionisgroupBy,whichgroupselementsaccordingtotheselector.groupByreturnsMap,thatismappingfromthechosenkeytoalistofelementsthatareselectedtomaptothefollowingkey:

valgrouped=listOf("ala","alan","mulan","malan")

.groupBy{it.first()}

println(grouped)//Prints:{'a':["ala","alan"],"m":["mulan","malan"]}

Page 369: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Let'slookatamorecomplexexample.Weneedtogetalistofthebeststudentsfromeachclass.Hereishowwecangetthemfromthelistofstudents:

classStudent(valname:String,valclassCode:String,valmeanGrade:Float)

valstudents=listOf(

Student("Homer","1",1.1F),

Student("Carl","2",1.5F),

Student("Donald","2",3.5F),

Student("Alex","3",4.5F),

Student("Marcin","3",5.0F),

Student("Max","1",3.2F)

)

valbestInClass=students

.groupBy{it.classCode}

.map{(_,students)->students.maxBy{it.meanGrade}!!}

.map{it.name}

print(bestInClass)//Prints:[Max,Donald,Marcin]

Page 370: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

OtherstreamprocessingfunctionsTherearelotsofdifferentstreamprocessingfunctionsandthereisnoneedtodescribethemallhere,whileKotlincontainsgreatdocumentationonitswebsite.Thenamesofmostoftheextensionfunctionsareself-explanatoryandthereisnoneedtoreallyreadthedocumentationtoguesswhattheyaredoing.InAndroidStudio,wecanchecktherealimplementationbypressingCtrl(commandkeyonmac)andclickingthefunctionwhoseimplementationwewanttoread.

Theimportantdifferenceincollectionprocessingcomeswhenyouareoperatingonmutablecollections,becausewhiletheycanuseadditionalextensionsdefinedformutabletypes(MutableIterable,andMutableCollection),theimportantdistinctionisthatfunctionsthatarechangingobjectareformulatedinpresentimperativeform(forexample,sort),whilefunctionsthatarereturninganewcollectionwithchangedvaluesaremostoftenformulatedinthepastformofaverb(forexample,sorted).Hereisanexample:

sort:Functionthatissortingamutableobject.ItreturnsUnit.sorted:Functionthatisreturningasortedcollection.Itisnotchangingthecollectiononwhichitisinvoked.

vallist=mutableListOf(3,2,4,1)

vallist2=list.sorted()

println(list)//[3,2,4,1]

println(list2)//[1,2,3,4]

list.sort()

println(list)//[1,2,3,4]

Page 371: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ExamplesofstreamcollectionprocessingWe'vealreadyseenafewstreamprocessingfunctions,butitneedssomeskillandcreativitytousethemforcomplexusecases.Thisiswhy,inthispart,wearegoingtodiscusssomecomplexstreamprocessingexamples.

Let'ssupposethatweagainneedtofindthebestthreestudentswhoarepassingaccordingtotheirgrade.Thekeydifferenceisthat,inthiscase,thefinalorderofstudentsmustbethesameasitwasinthebeginning.Notethatduringsortingbygradeoperation,thisorderislost.Butwecanpreserveitifwekeeptogethervalueandindex.Thankstothat,wecanlatersortelementsaccordingtothispreservedindex.Hereishowtoimplementthisprocessing:

dataclassStudent(

valname:String,

valgrade:Double,

valpassing:Boolean

)

valstudents=listOf(

Student("John",4.2,true),

Student("Bill",3.5,true),

Student("John",3.2,false),

Student("Aron",4.3,true),

Student("Jimmy",3.1,true)

)

valbestStudents=students.filter{it.passing}//1

.withIndex()//2

.sortedBy{it.value.grade}//3

.take(3)//4

.sortedBy{it.index}//5

.map{it.value}//6

//Printlistofnames

println(bestStudents.map{it.name})//[John,Bill,Jimmy]

1. Filtertokeeponlystudentsthatarepassing2. Addindextoelementstobeabletoreproduceelementorder3. Sortstudentsaccordingtotheirgrade4. Takeonlybest10students5. Reproduceorderbysortingaccordingtoindexes6. Mappingvalueswithindexestojustvalues

Page 372: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Notethatthisimplementationisconciseandeachoperationperformedonthecollectioniseasytoreadlinebyline.

Thebigadvantageofcollectionstreamprocessingisthatitiseasytomanagethecomplexityofthisprocess.Weknowthatthecomplexityofmostoperations,suchasmaporfilter,isO(n)andthecomplexityofsortingoperationsisO(n*log(n)).Thecomplexityofstreamoperationsismaximalcomplexityofeachofthesteps,sothecomplexityoftheaboveprocessingisO(n*log(n))becausesortedByisthestepwiththebiggestcomplexity.

Asthenextexample,let'ssupposethatwehavealistcontainingtheresultsofplayersindifferentcategories:

classResult(

valplayer:Player,

valcategory:Category,

valresult:Double

)

classPlayer(valname:String)

enumclassCategory{SWIMMING,RUNNING,CYCLING}

Andwehavesomeexampledata:

valresults=listOf(

Result("Alex",Category.SWIMMING,23.4),

Result("Alex",Category.RUNNING,43.2),

Result("Alex",Category.CYCLING,15.3),

Result("Max",Category.SWIMMING,17.3),

Result("Max",Category.RUNNING,33.3),

Result("Bob",Category.SWIMMING,29.9),

Result("Bob",Category.CYCLING,18.0)

)

Hereishowwecanfindthebestplayerineachcategory:

valbestInCategory=results.groupBy{it.category}//1

.mapValues{it.value.maxBy{it.result}?.player}//2

print(bestInCategory)

//Prints:{SWIMMING=Bob,RUNNING=Alex,CYCLING=Bob}

1. Wegroupresultsintocategories.ThereturntypeisMap<Category>,andList<Result>.

2. Wearemappingvaluesofthemapfunction.Inside,wefindthebestresultinthiscategoryandwearetakingtheplayerwhoisassociatedwiththisresult.ThereturnofthemapValuesfunctionisMap<Category,Player?>.

Page 373: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

TheprecedingexampleshowshowcomplexproblemsrelatedtocollectionscanbeeasilysolvedinKotlinthankstocollectionprocessingfunctions.AfterworkingwithKotlinforawhile,mostofthosefunctionsarewellknowntoprogrammers,andthencollectionprocessingproblemsarequiteeasytosolve.Ofcourse,functionsascomplicatedaspresentedabovearerare,butsimple,few-stepprocessingisquitecommonineverydayprogramming.

Page 374: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

SequenceSequenceisaninterfacethatisalsousedtorefertoacollectionofelements.ItisanalternativeforIterable.ForSequence,thereareseparateimplementationsofmostcollectionprocessingfunctions(map,flatMap,filter,sorted,andsoon).Thekeydifferenceisthatallthesefunctionsareconstructedinsuchawaythat,theyreturnsequence,whichispackagedoverprevioussequence.Duetothis,thefollowingpointsbecomestrue:

ThesizeofsequencedoesnotneedtobeknowninadvanceSequenceprocessingismoreefficient,especiallyforlargecollectionswherewewanttoperformseveraltransformations(detailswillbedescribedlater)

InAndroid,sequencesareusedforprocessingverybigcollectionsorforprocessingelementswhosesizeisnotknowninadvance(suchasforreadinglinesofpossiblylongdocument).Therearedifferentwaystocreatesequences,buttheeasiestistheasSequencefunctioncalledonIterableorbyusingthesequenceOftop-levelfunctiontomakesequencesimilarlyaslist.

Sequencesizedoesnotneedtobeknowninadvance,becausevaluesarecalculatedjustwhentheyareneeded.Hereisanexample:

val=generateSequence(1){it+1}//1.InstanceofGeneratorSequence

.map{it*2}//2.InstanceofTransformingSequence

.take(10)//3.InstanceofTakeSequence

.toList()//4.InstanceofList

println(numbers)//Prints:[2,4,6,8,10,12,14,16,18,20]

1. ThefunctiongenerateSequenceisonewayforsequencegeneration.Thissequencecontainsthenextnumbersfrom1toinfinity.

2. Themapfunctionpacksasequenceintoanotherthattakesthevaluefromthefirstsequenceandthenitcalculatesthevalueaftertransformation.

3. Thefunctiontake(10)willalsopackasequenceintoanotheronethatisfinishingonthe10thelement.Withoutthislineexecution,processingtime

Page 375: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

wouldbeinfinitivewhileweareoperatingonaninfinitesequence.4. Finally,thefunctiontoListisprocessingeachvalueanditreturnsthefinal

list.

Itisimportanttostressthatelementsareprocessedoneafteranotherinthelaststep(interminaloperation).Let'slookatanotherexample,whereeveryoperationisalsoprintingvaluesforloggingpurposes.Let'sstartwiththefollowingcode:

valseq=generateSequence(1){println("Generated${it+1}");it+1}

.filter{println("Processingoffilter:$it");it%2==1}

.map{println("Processingmap:$it");it*2}

.take(2)

Whatwouldbeprintedintheconsole?Absolutelynothing.Novalueswerecalculated.Thereasonisthatallthoseintermediateoperationsarelazy.Toretrievearesult,weneedtousesometerminaloperation,suchastoList.Let'susethefollowing:

seq.toList()

Thenwewillseethefollowingintheconsole:

Processingoffilter:1

Processingmap:1

Generated2

Processingoffilter:2

Generated3

Processingoffilter:3

Processingmap:3

Noticethatelementsarefullyprocessedoneafteranother.Instandardlistprocessing,theorderofoperationwouldbetotallydifferent:

(1..4).onEach{println("Generated$it")}

.filter{println("Processingfilter:$it");it%2==1}

.map{println("Processingmap:$it");it*2}

Theprecedingcodeprintsthefollowing:

Generated1

Generated2

Generated3

Generated4

Processingfilter:1

Processingfilter:2

Processingfilter:3

Processingfilter:4

Processingmap:1

Page 376: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Processingmap:3

Thisexplainswhysequencesaremoreefficientthanclassiccollectionprocessing--thereisnoneedtocreatecollectionsinintermediatesteps.Valuesareprocessedonebyoneondemand.

Page 377: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

FunctionliteralswithreceiverJustasfunctionshaveafunctiontype,whichallowsthemtobekeptasanobject,extensionfunctionshavetheirtypethatallowsthemtobekeptthisway.Itiscalledfunctiontypewithreceiver.Itlookslikethesimplefunctiontype,butthereceivertypeislocatedbeforearguments(likeinextensiondefinition):

varpower:Int.(Int)->Int

Theintroductionoffunctiontypewithreceivermakesfullcohesionbetweenfunctionsandtypes,becauseallfunctionscanbenowrepresentedasobjects.Itcanbedefinedusingalambdaexpressionwithreceiverorbyananonymousfunctionwithreceiver.

Inalambdaexpressionwithreceiverdefinition,theonlydifferenceisthatwecanreferencetoreceiverbythis,andwecanexplicitlyusereceiverelements.Forlambdaexpressions,thetypemustbespecifiedinaparameter,becausethereisnosyntaxtospecifyreceivertype.Hereispowerdefinedasalambdaexpressionwithreceiver:

power={n->(1..n).fold(1){acc,_->this*acc}}

Ananonymousfunctionalsoallowsustodefinethereceiver,anditstypeisplacedbeforethefunctionname.Insuchafunction,wecanusethisinsidethebodytorefertotheextensionreceiverobject.Notethatanonymousextensionfunctionsarespecifyingthereceivertype,sothepropertytypecanbeinferred.Hereispowerdefinedasananonymousextensionfunction:

power=funInt.(n:Int)=(1..n).fold(1){acc,_->this*acc}

Afunctiontypewithreceivercanbeusedasifitisamethodofreceivertype:

valresult=10.power(3)

println(result)//Prints:1000

Afunctiontypeismostoftenusedasafunctionparameter.Hereisanexample,whereaparameterfunctionisusedtoconfigureanelementafteritscreation:

funViewGroup.addTextView(configure:TextView.()->Unit){

Page 378: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

valview=TextView(context)

view.configure()

addView(view)

}

//Usage

vallinearLayout=findViewById(R.id.contentPanel)asLinearLayout

linearLayout.addTextView{//1

text="Marcin"//2

textSize=12F//2

}

1. Hereweareusingalambdaexpressionasanargument.2. Insidethelambdaexpression,wecandirectlyinvokereceivermethods.

Page 379: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

KotlinstandardlibraryfunctionsTheKotlinstdlibprovideasetofextensionfunctions(let,apply,also,with,run,andto)withgenericnon-restrictedreceiver(generictypeshavenorestrictions).Theyaresmallandhandyextensions,anditisveryprofitabletounderstandthem,becausetheyareveryusefulacrossallKotlinprojects.Oneofthesefunctions,let,wasbrieflyintroducedinChapter2,LayingaFoundation,wherewesawhowitcanbeusedasanalternativetoanullitycheck:

savedInstanceState?.let{state->

println(state.getBoolean("isLocked"))

}

Allthatletdoesisitcallsthespecifiedfunctionandreturnsitsresult.Whileintheaboveexampleitisusedtogetherwithasafecalloperator,itwillbecalledonlywhenthepropertysavedInstanceStateisnotnull.Theletfunctionisactuallyjustagenericextensionfunctionwithaparameterfunction:

inlinefun<T,R>T.let(block:(T)->R):R=block(this)

Instdlib,therearemorefunctionssimilartolet.Thesefunctionsareapply,also,with,andrun.Theyareasimilarsowearegoingtodescribethemtogether.Herearedefinitionsoftherestofthefunctions:

inlinefun<T>T.apply(block:T.()->Unit):T{

block();

returnthis

}

inlinefun<T>T.also(block:(T)->Unit):T{

block(this);

returnthis

}

inlinefun<T,R>T.run(block:T.()->R):R=block()

inlinefun<T,R>with(receiver:T,block:T.()->R):R=receiver.block()

Let'sseeusageexamples:

valmutableList=mutableListOf(1)

valmutableList=mutableListOf(1)

valletResult=mutableList.let{

it.add(2)

listOf("A","B","C")

}

println(letResult)//Prints:[A,B,C]

valapplyResult=mutableList.apply{

add(3)

Page 380: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

listOf("A","B","C")

}

println(applyResult)//Prints:[1,2,3]

valalsoResult=mutableList.also{

it.add(4)

listOf("A","B","C")

}

println(alsoResult)//Prints:[1,2,3,4]

valrunResult=mutableList.run{

add(5)

listOf("A","B","C")

}

println(runResult)//Prints:[A,B,C]

valwithResult=with(mutableList){

add(6)

listOf("A","B","C")

}

println(withResult)//Prints:[A,B,C]

println(mutableList)//Prints:[1,2,3,4,5,6]

Thedifferencesaresummarizedinthefollowingtable:

Returnedobject/parameterfunctiontype

Functionliteralwithreceiver

(receiverobjectrepresentedasthis)

Functionliteral

(receiverobjectrepresentedasit)

Receiverobject apply also

Resultoffunctionliteral run/with let

Whilethosefunctionsaresimilarand,inmanycases,itispossibletousetheminterchangeably,thereareconventionswhichdefinewhichfunctionsarepreferredforcertainusecases.

Page 381: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

TheletfunctionTheletfunctionispreferredwhenwewanttousestandardfunctionsasiftheyareextensionfunctionsinstreamprocessing:

valnewNumber=number.plus(2.0)

.let{pow(it,2.0)}

.times(2)

Likeotherextensions,itcanbecombinedwithasavecalloperator:

valnewNumber=number?.plus(2.0)

?.let{pow(it,2.0)}

Theletfunctionisalsopreferredwhenwejustwanttounpackanullableread-writeproperty.Inthissituation,itisnotpossibletosmartcastthispropertyandweneedtoshadowit,likeinthissolution:

varname:String?=null

funContext.toastName(){

valname=name

if(name!=null){

toast(name)

}

}

Thenamevariableistheshadowingpropertyname,whatisnecessaryifnameisaread-writeproperty,becausesmartcastisallowedonlyonamutableorlocalvariable.

Wecanreplacetheprecedingcodewithletusageandasafecalloperator:

name?.let{setNewName(it)}

NotethatusingElvisoperator,wecaneasilyaddareturnorexceptionthrowwhennameisnull:

name?.let{setNewName(it)}?:throwError("Nonamesetten")

Similarway,letcanbeusedasareplacementforthefollowingstatement:

valcomment=if(field==null)getComment(field)else"Nocomment

Page 382: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Implementationthatisusingtheletfunctionwouldlooklikethefollowing:

valcomment=field?.let{getComment(it)}?:"Nocomment"

Theletfunctionusedthiswayispreferredinmethodchainsthattransformthereceiver:

valtext="hello{name}"

funcorrectStyle(text:String)=text

.replace("hello","hello,")

fungreet(name:String){

text.replace("{name}",name)

.let{correctStyle(it)}

.capitalize()

.let{print(it)}

}

//Usage

greet("reader")//Prints:Hello,reader

Wecanalsousesimplersyntaxbypassingafunctionreferenceasanargument:

text.replace("{name}",name)

.let(::correctStyle)

.capitalize()

.let(::print)

Page 383: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

UsingtheapplyfunctionforinitializationSometimesweneedtocreateandinitializeanobjectbycallingsomemethodsormodifyingsomeproperties,suchaswhenwearecreatingaButton:

valbutton=Button(context)

button.text="Clickme"

button.isVisible=true

button.setOnClickListener{/*...*/}

this.button=button

Wecanreducecodeverbositybyusingtheapplyextensionfunction.Wecancallallthesemethodsfromthecontextwherebuttonisthereceiverobject:

button=Button(context).apply{

text="Clickme"

isVisible=true

setOnClickListener{/*...*/}

}

Page 384: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ThealsofunctionThealsofunctionissimilartoapply,withtheonlydifferencebeingthattheparameterfunctionacceptsanargumentasanparameterratherthanasanreceiver.Itispreferredwhenwewanttodosomeoperationsonanobject,whicharenotinitializations:

abstractclassProvider<T>{

varoriginal:T?=null

varoverride:T?=null

abstractfuncreate():T

funget():T=override?:original?:create().also{original=it}

}

Thealsofunctionisalsopreferredwhenweneedtodosomeoperationinthemiddleofprocessing,forexample,duringobjectconstructionusingtheBuilderpattern:

funmakeHttpClient(vararginterceptors:Interceptor)=

OkHttpClient.Builder()

.connectTimeout(60,TimeUnit.SECONDS)

.readTimeout(60,TimeUnit.SECONDS)

.also{it.interceptors().addAll(interceptors)}

.build()

Anothersituationwherealsoispreferrediswhenwearealreadyinanextensionfunctionandwedon'twanttoaddanotherextensionreceiver:

classSnail{

varname:String=""

vartype:String=""

fungreet(){

println("Hello,Iam$name")

}

}

classForest{

varmembers=listOf<Sneil>()

funSneil.reproduce():Sneil=Sneil().also{

it.name=name

it.type=type

members+=it

}

}

Page 385: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

TherunandwithfunctionTherunandwithfunctionsthatarebothacceptinglambdaliteralwithreceiverasanargument,andreturningitsresult.Thedifferencebetweenthemisthatrunisacceptingareceiver,whilethewithfunctionisnotanextensionfunctionandittakestheobjectweareoperatinginasparameter.Bothfunctionscanbeusedasanalternativetotheapplyfunction,whenwearesettingupanobject:

valbutton=findViewById(R.id.button)asButton

button.apply{

text="Clickme"

isVisible=true

setOnClickListener{/*...*/}

}

button.run{

text="Clickme"

isVisible=true

setOnClickListener{/*...*/}

}

with(button){

text="Clickme"

isVisible=true

setOnClickListener{/*...*/}

}

Thedifferencebetweenapply,run,andwithisthatapplyisreturningareceiverobject,whilerunandwitharereturningtheresultoffunctionliteral.Althoughwhenweneedanyofthese,weshouldchoosethefunctionthatisreturningit.Itisdebatablewhichshouldbeusedwhenwedonotneedanyreturnedvalue.Mostoften,itissuggestedtousetherunorwithfunctionthan,becausealsoismoreoftenusedinthesituationswhenreturnedvalueisneeded.

Aboutdifferencesbetweentherunandwithfunctions:therunfunctionisusedinsteadofthewithfunction,whenavalueisnullable,becausethenwecanuseasafecallornot-nullassertion:

valbutton=findViewById(R.id.button)as?Button

button?.run{

text="Clickme"

isVisible=true

setOnClickListener{/*...*/}

}

Page 386: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Thewithfunctionispreferredoverrunwhenanexpressionisshort:

valbutton=findViewById(R.id.button)asButton

with(button){

text="Clickme"

isVisible=true

setOnClickListener{/*...*/}

}

Ontheotherhand,runispreferredoverwithwhenanexpressionislong:

itemAdapter.holder.button.run{

text="Clickme"

isVisible=true

setOnClickListener{/*...*/}

}

Page 387: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ThetofunctionInfixfunctionswereintroducedinChapter4,ClassesandObjects,buttheycanbedefinednotonlyasmemberclasses,butalsoasextensionfunctions.Itmakesitpossibletocreateaninfixextensionfunctiontoanyobject.Oneofthesekindsofextensionfunctionsisto,whichwasbrieflydescribedinChapter2,LayingaFoundation.Nowwehavetheknowledgeneededtounderstanditsimplementation.Thisishowtoisdefined:

infixfun<A,B>A.to(that:B):Pair<A,B>=Pair(this,that)

ThismakesitpossibletoplacetobetweenanytwoobjectsandmakethiswayPairwiththem:

println(1to2==Pair(1,2))//Prints:true

Notethatthefactthatwecanmakeinfixextensionfunctionsmakesusallowedtodefineinfixfunctionsasanextensiontoanytype.Hereisanexample:

infixfun<T>List<T>.intersection(other:List<T>)

=filter{itinother}

listOf(1,2,3)intersectionlistOf(2,3,4)//[2,3]

Page 388: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Domain-specificlanguageFeaturessuchaslambdaliteralwithreceiverandmemberextensionfunctionsaremakingitpossibletodefinetype-safebuilders,thatareknownfromGroovy.Themostwell-knownAndroidexampleisGradleconfiguration,build.gradle,whichiscurrentlywritteninGroovy.ThesekindsofbuildersareagoodalternativetoXML,HTML,orconfigurationfiles.TheadvantageofKotlinusageinsteadisthatwecanmakesuchconfigurationsfullytype-safeandprovideabetterIDE.SuchbuildersareoneexampleofKotlindomain-specificlanguage(DSL).

ThemostpopularKotlinDSLpatterninAndroidistheimplementationofoptionalcallbackclasses.Itisusedtosolveaproblemwithalackoffunctionalsupporttocallbackinterfaceswithmultiplemethods.Classically,implementationwouldrequireobject-expressionusage,likeinfollowingexample:

searchView.addTextChangedListener(object:TextWatcher{

overridefunbeforeTextChanged(s:CharSequence,start:Int,count:Int,after:Int){}

overridefunonTextChanged(s:CharSequence,start:Int,before:Int,count:Int){

presenter.onSearchChanged(s.toString())

}

overridefunafterTextChanged(s:Editable){}

})

Themainproblemswithsuchimplementationareasfollows:

WeneedtoimplementallmethodspresentininterfaceFunctionstructureneedstobeimplementedforeachmethodWeneedtouseobjectexpression

Let'sdefinethefollowingclass,thatiskeepingcallbacksasmutableproperties:

classTextWatcherConfig:TextWatcher{

privatevarbeforeTextChangedCallback:(BeforeTextChangedFunction)?=null//1

privatevaronTextChangedCallback:(OnTextChangedFunction)?=null//1

privatevarafterTextChangedCallback:(AfterTextChangedFunction)?=null//1

funbeforeTextChanged(callback:BeforeTextChangedFunction){//2

beforeTextChangedCallback=callback

Page 389: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

}

funonTextChanged(callback:OnTextChangedFunction){//2

onTextChangedCallback=callback

}

funafterTextChanged(callback:AfterTextChangedFunction){//2

afterTextChangedCallback=callback

}

overridefunbeforeTextChanged(s:CharSequence?,start:Int,count:Int,

after:Int){//3

beforeTextChangedCallback?.invoke(s?.toString(),start,count,after)//4

}

overridefunonTextChanged(s:CharSequence?,start:Int,before:

Int,count:Int){//3

onTextChangedCallback?.invoke(s?.toString(),start,before,count)//4

}

overridefunafterTextChanged(s:Editable?){//3

afterTextChangedCallback?.invoke(s)

}

}

privatetypealiasBeforeTextChangedFunction=

(text:String?,start:Int,count:Int,after:Int)->Unit

privatetypealiasOnTextChangedFunction=

(text:String?,start:Int,before:Int,count:Int)->Unit

privatetypealiasAfterTextChangedFunction=

(s:Editable?)->Unit

1. Callbacks,whichareusedwhenanyoftheoverriddenfunctionsiscalled.2. Functionsusedtosetnewcallbacks.Theirnamescorrespondstohandler

functionnames,buttheyincludecallbackasaparameter.3. Eacheventhandlerfunctionsarejustinvokingcallbackifitexists.4. Tosimplifyusage,wealsochangedtypes,CharSequencepresentintheoriginal

methodswaschangedtoString.

Nowallweneedisanextensionfunctionthatwillsimplifycallbackconfiguration.ItsnamecannotbethesameasanynameofTextView,butallweneedtodoisasmallmodification:

funTextView.addOnTextChangedListener(config:TextWatcherConfig.()->Unit){

valtextWatcher=TextWatcherConfig()

textWatcher.config()

addTextChangedListener(textWatcher)

}

Withsuchdefinitions,wecandefinecallbacksweneedthisway:

Page 390: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

searchView.addOnTextChangedListener{

onTextChanged{text,start,before,count->

presenter.onSearchChanged(text)

}

}

Weuseunderscoretohideunusedparameters,toimproveourimplementation:

searchView.addOnTextChangedListener{

onTextChanged{text,_,_,_->

presenter.onSearchChanged(text)

}

}

NowtwoothercallbacksbeforeTextChangedandafterTextChangedareignored,butwecanstilladdotherimplementations:

searchView.addOnTextChangedListener{

beforeTextChanged{_,_,_,_->

Log.i(TAG,"beforeTextChangedinvoked")

}

onTextChanged{text,_,_,_->

presenter.onSearchChanged(text)

}

afterTextChanged{

Log.i(TAG,"beforeTextChangedinvoked")

}

}

Alistenerdefinedthiswayhasthefollowingproperties:

ItisshorterthanobjectexpressionimplementationItincludesdefaultfunctionsimplementationsItallowsustohideunusedparameters

WhileinAndroidSDKtherearemultiplelistenerswithmorethanonehandler,DSLimplementationofoptionalcallbackclassesisreallypopularinAndroidprojects.Similarimplementationscanbealsofoundinlibraries,suchasthealready-mentionedAnko.

AnotherexampleisDSL,whichwillbeusedtodefinelayoutstructurewithoutusingXMLlayoutfiles.WewilldefineafunctiontoaddandconfigureLinearLayoutandTextViewanduseittodefineasimpleview:

funContext.linearLayout(init:LinearLayout.()->Unit):LinearLayout{

vallayout=LinearLayout(this)

layout.layoutParams=LayoutParams(WRAP_CONTENT,WRAP_CONTENT)

layout.init()

returnlayout

}

Page 391: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

funViewGroup.linearLayout(init:LinearLayout.()->Unit):LinearLayout{

vallayout=LinearLayout(context)

layout.layoutParams=LayoutParams(WRAP_CONTENT,WRAP_CONTENT)

layout.init()

addView(layout)

returnlayout

}

funViewGroup.textView(init:TextView.()->Unit):TextView{

vallayout=TextView(context)

layout.layoutParams=LayoutParams(WRAP_CONTENT,WRAP_CONTENT)

layout.init()

addView(layout)

returnlayout

}

//Usage

classMainActivity:AppCompatActivity(){

overridefunonCreate(savedInstanceState:Bundle?){

super.onCreate(savedInstanceState)

valview=linearLayout{

orientation=LinearLayout.VERTICAL

linearLayout{

orientation=LinearLayout.HORIZONTAL

textView{text="A"}

textView{text="B"}

}

linearLayout{

orientation=LinearLayout.HORIZONTAL

textView{text="C"}

textView{text="D"}

}

}

setContentView(view)

}

}

WecanalsodefineourcustomDSLfromscratch.Let'smakeasimpleDSLthatdefinesalistofarticles.Weknowthateacharticleshouldbedefinedinadifferentcategory,andthatarticlehasitsname,URL,andtags.Whatwewouldliketoachieveisthefollowingdefinition:

category("Kotlin"){

post{

name="Awesomedelegates"

url="SomeUrl.com"

}

post{

name="Awesomeextensions"

url="SomeUrl.com"

}

}

category("Android"){

post{

name="Awesomeapp"

url="SomeUrl.com"

tags=listOf("Kotlin","GoogleLogin")

}

Page 392: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

}

ThesimplestobjecthereisthePostclass.Itisholdingpostpropertiesandallowsthemtobechanged:

classPost{

varname:String=""

varurl:String=""

vartags:List<String>=listOf()

}

Next,weneedtodefineaclassthatwillholdthecategory.Itneedstostorealistofpostsanditalsoneedstocontainitsname.Theremustbealsoadefinedfunctionthatwillallowsimplepostaddition.ThisfunctionneedstocontainafunctionparameterinwhichPostisthereceivertype.Hereisthedefinition:

classPostCategory(valname:String){

varposts:List<Post>=listOf()

funpost(init:Post.()->Unit){

valpost=Post()

post.init()

posts+=post

}

}

Also,weneedaclassthatwillholdalistofcategoriesandallowsimplecategorydefinition:

classPostList{

varcategories:List<PostCategory>=listOf()

funcategory(name:String,init:PostCategory.()->Unit){

valcategory=PostCategory(name)

category.init()

categories+=category

}

}

AllweneednowisthedefinePostsfunction,whosedefinitionmightbethefollowing:

fundefinePosts(init:PostList.()->Unit):PostList{

valpostList=PostList()

postList.init()

returnpostList

}

Andthat'sallweneed.Nowwecandefineobjectstructurebyasimple,type-safebuilder:

Page 393: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

valpostList=definePosts{

category("Kotlin"){

post{

name="Awesomedelegates"

url="SomeUrl.com"

}

post{

name="Awesomeextensions"

url="SomeUrl.com"

}

}

category("Android"){

post{

name="Awesomeapp"

url="SomeUrl.com"

tags=listOf("Kotlin","GoogleLogin")

}

}

}

DSLisareallypowerfulconceptthatismoreandmoreusedaroundtheKotlincommunity.Itisalreadypossible,thankstolibraries,touseKotlinDSLtofullyreplacethefollowing:

Androidlayoutfiles(Anko)GradleconfigurationfilesHTMLfiles(kotlinx.html)JSONfiles(Kotson)

Andlotsofotherconfigurationfiles.Let'slookatsomeexamplelibrarythatisdefiningKotlinDSLtoprovidetype-safebuilders.

Page 394: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

AnkoAnkoisalibrarythatprovidesaDSLtodefineAndroidviewswithoutanyXMLlayouts.Thisisprettysimilartoexampleswe'vealreadyseen,butAnkomadeitpossibletofullyremoveXMLlayoutfilesfromaproject.HereisanexampleviewwritteninAnkoDSL:

verticalLayout{

valname=editText()

button("SayHello"){

onClick{toast("Hello,${name.text}!")}

}

}

Andhereistheresult:

Source:https://github.com/Kotlin/anko

ItisalsopossibletodefinemuchmorecomplexlayoutsusingAnkoDSL.TheseviewscanbeplacedeitheronacustomclassthatservesasavieworevendirectlyinsidetheonCreatemethod:

overridefunonCreate(savedInstanceState:Bundle?){

super.onCreate(savedInstanceState)

verticalLayout{

padding=dip(30)

editText{

hint="Name"

textSize=24f

}

editText{

hint="Password"

textSize=24f

}

button("Login"){

textSize=26f

}

}

}

Tolearnmoreaboutthisexample,youcanvisitAnkoWikiathttps://g

Page 395: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ithub.com/Kotlin/anko/wiki/Anko-Layouts.

ItisstilldebatableifDSLlayoutdefinitionaregoingtoreplaceXMLdefinitions.Atthetimeofwriting,itisnotsopopulartodefineviewsthisway,becauseofalackofsupportfromGoogle,butwhileGoogleannouncedthattheyaregoingtosupportKotlin,soitispossiblethatthisideawillbecomemorepopularandDSL-basedlayoutswillbemoresupportedandmaybeevenuniversalsomeday.

Page 396: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

SummaryInthischapter,wediscussedKotlinextensionfunctionsandproperties,bothdefinedtop-levelandastypemember.We'veseenhowKotlinstandardlibraryextensionfunctionscanbeusedtosimplifycollectionprocessingandperformvariousoperations.Wehavealsodescribedfunctiontypewithreceivertogetherwithfunctionliteralswithreceiver.Also,we'veseenafewimportantgenericfunctionsfromstandardlibrary,whichareusingextensions:let,apply,also,with,run,andto.Finally,we'veseenhowDSLcanbedefinedinKotlin,andwhereitisuseful.

Inthenextchapter,thereisgoingtobepresentedthenextfeaturethatwasnotpresentintheJavaworldwhileitisgivingreallybigpossibilitiesinKotlindevelopment--classandpropertydelegates.

Page 397: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

DelegatesKotlintakesdesignpatternsprettyseriously.Previously,we'veseenhowtheusageoftheSingletonpatternwassimplifiedbyobjectdeclarations,andhowusageoftheObservatorpatternwastrivializedthankstohigherorderfunctionsandfunctionaltype.Also,Kotlinsimplifiedtheusageofmostfunctionalpatterns,thankstolambdaexpressionsandfunctionaltypes.Inthischapter,wewillseehowusageofDelegationandDecoratorpatternsweresimplifiedthankstoclassdelegation.Wewillalsoseeafeature,whichisprettynewintheprogrammingworld--propertydelegation--andhowitisusedtomakeKotlinpropertiesmuchmorepowerful.

Inthischapter,wewillcoverthefollowingtopics:

DelegationpatternClassdelegationDecoratorpatternPropertydelegationPropertydelegatesfromthestandardlibraryCreationofacustompropertydelegate

Page 398: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ClassdelegationKotlinhasafeaturecalledclassdelegation.Itisareallyinconspicuousfeaturethathasmanypracticalapplications.Itisworthtonotice,thatitisstronglyconnectedwithtwodesignpatterns:DelegationPatternandDecoratorPattern.Wewilldiscussthosepatternsinmoredetailinupcomingsections.DelegationandDecoratorpatternareknownformanyyears,butinJavatheirimplementationrequiredalotofboilerplatecode.Kotlinisoneofthefirstlanguagesthatprovidednativesupportforthosepatternsreducingboilerplatecodetominimum.

Page 399: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

DelegationpatternInobject-orientedprogramming,Delegationpatternisadesignpattern,whichisanalternativetoinheritance.Delegationmeansthattheobjecthandlesarequestbydelegatingittoanotherobject(delegate),insteadofextendingtheclass.

TosupportthepolymorphicbehaviorknownfromJava,bothobjectsshouldimplementthesameinterfacethatholdsalldelegatedmethodsandproperties.Asimpleexampleofthedelegationpatternisthefollowing:

interfacePlayer{//1

funplayGame()

}

classRpgGamePlayer(valenemy:String):Player{

overridefunplayGame(){

println("Killing$enemy")

}

}

classWitcherPlayer(enemy:String):Player{

valplayer=RpgGamePlayer(enemy)//2

overridefunplayGame(){

player.playGame()//3

}

}

//Usage

RpgGamePlayer("monsters").playGame()//Prints:Killingmonsters

WitcherPlayer("monsters").playGame()//Prints:Killingmonsters

1. Whenwearetalkingaboutclassdelegation,thereneedstobeaninterfacethatdefineswhatmethodsaredelegated.

2. Objectthatwearedelegatingto(delegate).3. AllmethodsinsidetheWitcherPlayerclassshouldcallcorrespondingmethods

onthedelegateobject(player).

ThisiscalleddelegationbecausetheWitcherPlayerclassisdelegatingmethodsdefinedinthePlayerinterfacetoaninstanceoftypeRpgGamePlayer(player).Similarresultcouldbereachedbyusinginheritanceinsteadofdelegation.Itwouldlookasfollows:

classWitcherPlayer():RpgGamePlayer()

Page 400: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Atfirstglance,thesetwoapproachesmightlooksimilar,butdelegationandinheritancehavealotofdifferences.Ononehand,inheritanceismuchmorepopularandmorecommoninuse.ItisoftenusedinJava,andisconnectedtomultipleOOPpatterns.Ontheotherhand,therearesourcesthatstronglystandbehinddelegation.Forexample,theinfluentialbookDesignPatterns,bytheGangofFour,containstheprinciple:favorobjectcompositionoverclassinheritance.Also,thepopularbookEffectiveJava,includestherule:favorcompositionoverinheritance(Item6).Bothofthemstronglysupportthedelegationpattern.Herearesomebasicargumentsthatstandbehindtheusageofthedelegationpatterninsteadofinheritance:

Oftenclassesarenotdesignedforinheritance.Whenweoverridemethods,wearenotawareofunderlyingassumptionsaboutclassinternalbehavior(whenmethodsarecalled,howthosecallsaffecttheobjects,states,andsoon).Forexample,whenweoverridemethod,wemightnotbeawarethatitisusedbyothermethods,sooverriddenmethodsmaybecalledunexpectedlybyasuperclass.Evenifwecheckwhenthemethodiscalled,thisbehaviorcouldchangeinanewversionoftheclass(forexample,ifweextendclassfromanexternallibrary)andthusbreakoursubclass'behavior.Averysmallamountofclassesareproperlydesignedanddocumentedforinheritance,butnearlyallnonabstractclassesaredesignedforusage(thisincludesdelegation).InJava,itispossibletodelegateaclasstomultipleclasses,butinheritonlyfromone.Byinterface,wearespecifyingwhichmethodsandpropertieswewanttodelegate.Thisiscompatiblewiththeinterfacesegregationprinciple(fromSOLID)--weshouldn'texposeunnecessarymethodstotheclient.Someclassesarefinal,sowecanonlydelegatetothem.Infact,allclassesthatarenotdesignedforinheritanceshouldbefinal.Kotlindesignerswereawareofit,andtheymadeallclassesinKotlintobefinalbydefault.Makingaclassfinalandprovidingproperinterfaceisgoodpracticeforpubliclibraries.Wecanchangetheimplementationofaclasswithoutworryingthatitwillaffectlibraryusers(aslongasthebehaviorwillbethesamefromaninterfacepointofview).Itmakesthemimpossibletoinheritfrom,buttheyarestillgreatcandidatestodelegateto.

Moreinformationonhowclassesshouldbedesignedtosupport

Page 401: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

inheritanceandwhendelegationshouldbeusedcanbefoundinthebookEffectiveJava,inItem16:Favorcompositionoverinheritance.

Ofcourse,therearealsodisadvantagesofusingdelegationinsteadofinheritance.Herearethemainproblems:

WeneedtocreateinterfacesthatspecifywhichmethodsshouldbedelegatedWedon'thaveaccesstoprotectedmethodsandproperties

InJava,therewasonemorestrongargumentforusinginheritance:itwasmucheasiertoimplement.EvenwhilecomparingcodefromourWitcherPlayerexample,wecanseethatdelegationneededalotofextracode:

classWitcherPlayer(enemy:String):Player{

valplayer=RpgGamePlayer(enemy)

overridefunplayGame(){

player.playGame()

}

}

classWitcherPlayer():RpgGamePlayer()

Thisisespeciallyproblematicwhenwearedealingwithinterfaceswithmultiplemethods.Fortunately,modernlanguagesvaluetheusageoftheDelegationpattern,andmanyofthemhavenativeclassdelegationsupport.ThereisstrongsupportfortheDelegationpatterninSwiftandGroovy,andthereisalsosupportthroughothermechanismsinRuby,Python,JavaScript,andSmalltalk.Kotlinalsostronglysupportsclassdelegation,andmakesusageofthispatternreallysimple,andusingnearlyzero-boilerplatecode.TheWitcherPlayerclassfromtheexamplecouldbeimplementedinthiswayinKotlin:

classWitcherPlayer(enemy:String):PlayerbyRpgGamePlayer(enemy){}

Usingthebykeyword,weareinformingthecompilertodelegateallmethodsdefinedinthePlayerinterfacefromWitcherPlayertoRpgGamePlayer.AninstanceofRpgGamePlayeriscreatedduringtheWitcherPlayerconstruction.Insimplerwords:WitcherPlayerisdelegatingmethodsdefinedinthePlayerinterfacetoanewRpgGamePlayerobject.

Whatisreallyhappeninghereisthatduringcompilation,theKotlincompileris

Page 402: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

generatingnotimplementedmethodsfromPlayerinWitcherPlayerandfillingthemwithcallstoanRpgGamePlayerinstance(thesamewayasthatweimplementedtheminthefirstexample).Thebigimprovementisthatwedon'tneedtoimplementthosemethodsourselves.Alsonotethatifasignatureofadelegatedmethodchanges,thenwedon'tneedtochangeallobjectsthataredelegatedtoit,sotheclassiseasiertomaintain.

Thereisanotherwaytocreateandholdaninstanceofthedelegate.Itcanbeprovidedbyaconstructor,likeinthisexample:

classWitcherPlayer(player:Player):Playerbyplayer

Wecanalsodelegatetoapropertydefinedintheconstructor:

classWitcherPlayer(valplayer:Player):Playerbyplayer

Finally,wecandelegatetoanypropertyaccessibleduringclassdeclaration:

vald=RpgGamePlayer(10)

classWitcherPlayer(a:Player):Playerbyd

Inaddition,oneobjectcanhavemultipledifferentdelegates:

interfacePlayer{

funplayGame()

}

interfaceGameMaker{//1

fundevelopGame()

}

classWitcherPlayer(valenemy:String):Player{

overridefunplayGame(){

print("Killin$enemy!")

}

}

classWitcherCreator(valgameName:String):GameMaker{

overridefundevelopGame(){

println("Makin$gameName!")

}

}

classWitcherPassionate:

PlayerbyWitcherPlayer("monsters"),

GameMakerbyWitcherCreator("Witcher3"){

funfulfillYourDestiny(){

playGame()

developGame()

}

}

Page 403: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

//Usage

WitcherPassionate().fulfillYourDestiny()//Killinmonsters!MakinWitcher3!

1. TheWitcherPlayerclassisdelegatingthePlayerinterfacetoanewRpgGamePlayerobject,GameMakertoanewWitcherCreatorobject,andalsoincludesthefunctionfulfillYourDestinythatisusingfunctionsfrombothdelegates.NotethatbothWitcherPlayerandWitcherCreatorarenottaggedasopen,andwithoutthis,theycannotbeextended.Theycanbedelegated,though.

Withsuchlanguagesupport,theDelegationpatternismuchmoreattractivethaninheritance.Whilethispatternhasbothadvantagesanddisadvantages,itisgoodtoknowwhenitshouldbeused.Themaincaseswheredelegatesshouldbeusedareasfollows:

WhenyoursubclassviolatestheLiskovsubstitutionprinciple;forexample,whenwearedealingwithsituationswhereinheritancewasimplementedonlytoreusecodeofthesuperclass,butitisnotreallyactinglikeit.Whenthesubclassusesonlyaportionofthemethodsofthesuperclass.Inthiscase,itisonlyamatteroftimebeforesomeonecallsasuperclassmethodthattheywerenotsupposedtocall.Usingdelegation,wereuseonlymethodswechoose(definedintheinterface).Whenwecannotorweshouldnotinherit,because:

TheclassisfinalItisnotaccessibleandusedfrombehindinterfaceItisjustnotdesignedforinheritance

NotethatwhileclassesinKotlinarefinalbydefault,mostofthemwillbeleftfinal.Ifthoseclassesareplacedinalibrarythenmostlikelywewon'tbeabletochangeoropentheclass.Delegationwillbetheonlyoption,tomakeclasswithdifferentbehavior.

TheLiskovsubstitutionprincipleisaconceptinOOPstatingthatallsubclassesshouldactliketheirsuperclasses.Ineasierwords,Ifunittestsarepassingforsomeclass,theyshouldbepassingforitssubclassestoo.ThisprinciplehasbeenpopularizedbyRobertC.Martin,whoplaceditinhissetofmostimportantOOPrulesanddescribeditinthepopularbookCleanCode.

ThebookEffectiveJavastatesthat"inheritanceisappropriateonlyin

Page 404: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

circumstanceswhereasubclassreallyisasubtypeofthesuperclass."Inotherwords,classBshouldextendaclassonlyifanis-arelationshipexistsbetweenthetwoclasses.IfyouaretemptedtohaveclassBextendclassA,askyourselfIseveryBreallyanA?Inthenextpart,thebooksuggeststhatineveryothercasethereshouldbecompositionused(whichmostcommonimplementationisdelegation).

ItisalsoworthnotethatCocoa(theUIframeworkfromAppleforbuildingsoftwareprogramstorunoniOS)veryoftenusedelegatesinsteadofinheritance.Thispatternisbecomingmoreandmorepopular,andinKotlinitishighlysupported.

Page 405: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

DecoratorpatternAnothercommoncasewheretheKotlinclassdelegationisreallyusefuliswhenweareimplementingaDecoratorpattern.ADecoratorpattern(alsoknownasWrapperpattern)isadesignpatternthatmakesitpossibletoaddabehaviortoanexistingclasswithoutusinginheritance.Incontrasttoextensionswherewecanaddanewbehaviorwithoutmodifyinganobject,wearecreatingaconcreteobjectwithadifferentbehavior.ADecoratorpatternusesDelegation,butinaveryspecificway--delegateisprovidedfromoutsideoftheclass.TheclassicstructureispresentedinthefollowingUMLdiagram:

UMLdiagramofclassicimplementationoftheDecoratorpattern.Source:http://upload.wikimedia.org

TheDecoratorcontainstheobjectsthatitisdecoratingwhileitisimplementingthesameinterface.

ThemostpopularexampleofdecoratorusagefromtheJavaworldisInputStream.TherearedifferentkindsoftypesthatareextendingInputStream,andalotofdecoratorsthatcanbeusedtoaddfunctionalitiestothem.Thisdecoratorcanbe

Page 406: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

usedtoaddbuffering,getcontentofazippedfile,orconvertfilecontenttoaJavaobject.Let'slookattheexamplewheremultipledecoratorsareusedtoreadazippedJavaobject:

//Java

FileInputStreamfis=newFileInputStream("/someFile.gz");//1

BufferedInputStreambis=newBufferedInputStream(fis);//2

GzipInputStreamgis=newGzipInputStream(bis);//3

ObjectInputStreamois=newObjectInputStream(gis);//4

SomeObjectsomeObject=(SomeObject)ois.readObject();//5

1. Createasimplestreamforreadingafile.2. Makeanewstreamthatcontainsbuffering.3. Makeanewstreamthatcontainsfunctionalityofthereadingcompressed

dataintheGZIPfileformat.4. Makeanewstreamthataddsfunctionalitythatdeserializesprimitivedata

andobjectspreviouslywrittenusinganObjectOutputStream.5. StreamisusedinthereadObjectmethodofObjectInputStream,butallobjectsin

thisexampleareimplementingInputStream(whatmakesitpossibletopackitthisway)andcanbereadbythemethodsspecifiedbythisinterface.

Notethatthispatternisalsosimilartoinheritance,butwecandecidewhatdecoratorswewanttouseandinwhatorder.Thisismuchmoreflexibleandgivesmorepossibilitiesduringusage.SomepeoplearguethatInputStreamusagewouldbebetterifdesignerswouldmakeonebigclasswithalldesignedfunctionalities,andthenusemethodstoturnsomeofthemonoroff.Thisapproachwouldviolatethesingle-responsibilityprincipleandleadtomuchmorecomplicatedandmuchlessexpandablecode.

WhiletheDecoratorpatternisconsideredoneofthebestinpracticaluse,itisrarelyusedinJavaprojects.Thisisbecausetheimplementationisnotsimple.Interfacesoftencontainmultiplemethodsandcreatingadelegationtothemineachdecoratorgenerateslotsofboilerplatecode.ThereisadifferentsituationinKotlin--we'vealreadyseenthatinKotlinclassdelegationisactuallytrivial.Let'slookatsomeclassicexamplesofpracticalclassdelegationusageintheDecoratorpattern.Let'ssupposethatwewanttoaddthefirstpositionasthezeroelementtoseveraldifferentListAdapters.Thisextrapositionhassomespecialproperties.Wecouldn'timplementthisusinginheritancebecausetheseListAdaptersfordifferentlistsareofdifferenttypes(whichisthestandardsituation).Inthiscase,wecaneitherchangethebehaviorsofeachclass(DRY

Page 407: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

rule)orwecancreateadecorator.Hereistheshortcodeofthisdecorator:

classZeroElementListDecorator(valarrayAdapter:ListAdapter):

ListAdapterbyarrayAdapter{

overridefungetCount():Int=arrayAdapter.count+1

overridefungetItem(position:Int):Any?=when{

position==0->null

else->arrayAdapter.getItem(position-1)

}

overridefungetView(position:Int,convertView:View?,parent:

ViewGroup):View=when{

position==0->parent.context.inflator

.inflate(R.layout.null_element_layout,parent,false)

else->arrayAdapter.getView(position-1,convertView,parent)

}

}

overridefungetItemId(position:Int):Long=when{

position==0->0

else->arrayAdapter.getItemId(position-1)

}

WeusedaninflatorextensionpropertyofContexthere,whichisoftenincludedinKotlinAndroidprojectsandshouldbeknownfromChapter7,ExtensionFunctionsandProperties:

valContext.inflater:LayoutInflater

get()=LayoutInflater.from(this)

TheZeroElementListDecoratorclassdefinedthiswayalwaysaddsafirstelementwithstaticview.Herewecanseeasimpleexampleofitsuse:

valarrayList=findViewById(R.id.list)asListView

vallist=listOf("A","B","C")

valarrayAdapter=ArrayAdapter(this,

android.R.layout.simple_list_item_1,list)

arrayList.adapter=ZeroElementListDecorator(arrayAdapter)

InZeroElementListDecoratoritmightlookcomplicatedthatweneededtooverridefourmethods,butinfact,thereareeightmoreofthemandwedidn'thavetooverridethem,thankstoKotlin'sclassdelegation.WecanseethatKotlinclassdelegationismakingtheimplementationoftheDecoratorpatternmucheasier.

TheDecoratorpatternisreallysimpletoimplementandisprettyintuitive.Itcanbeusedinlotsofdifferentcasestoextendaclasswithextrafunctionality.Itisreallysafeandoftenreferredasagoodpractice.Theseexamplesarejustsomeofthepossibilitiesprovidedbyclassdelegation.Iamsurethatthereaderwillfindmoreusecaseswithpresentedpatternsanduseclassdelegationtomakethe

Page 408: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

projectmoreclean,safe,andconcise.

Page 409: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

PropertydelegationKotlinallowsnotonlyclassdelegation,butalsopropertydelegation.Inthissection,wearegoingtofindoutwhatdelegatedpropertiesare,reviewpropertydelegatesfromKotlinstandardlibrary,andlearnhowtocreateandusecustompropertydelegates.

Page 410: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Whataredelegatedproperties?Let'sstartwithexplanationofwhatpropertydelegatesare.Hereisanexampleoftheuseofpropertydelegation:

classUser(valname:String,valsurname:String)

varuser:UserbyUserDelegate()//1

println(user.name)

user=User("Marcin","Moskala")

1. WearedelegatinguserpropertytoaninstanceofUserDelegate(whichiscreatedbytheconstructor).

Propertydelegationissimilartoclassdelegation.Wedelegatetoanobjectusingthesamekeyword(by).Eachcalltoaproperty(set/get)willbedelegatedtoanotherobject(UserDelegate).Thiswaywecanreusethesamebehaviorformultipleproperties,forexample,settingapropertyvalueonlywhensomecriteriaaremet,oraddinglogentrywhenapropertyisaccessed/updated.

Weknowthatapropertydoesn'treallyneedabackingfield.Itmightbedefinedjustbygetter(read-only)oragetter/setter(read-write).Underthehood,propertydelegatesarejusttranslatedtocorrespondingmethodcalls(setValue/getValue).Theprecedingexamplewillbecompiledtosuchcode:

varp$delegate=UserDelegate()

varuser:User

get()=p$delegate.getValue(this,::user)

set(value){

p$delegate.setValue(this,::user,value)

}

Theexampleisshowingthatbyusingthebykeyword,wearedelegatingthesetterandgettercallstodelegate.ThatiswhyanyobjectthathasthegetValueandsetValuefunctionswithcorrectparameters(itwillbedescribedlater)canbeusedasadelegate(forread-onlypropertiesgetValueisenough,becauseonlythegetterisneeded).Itisimportantthatallthatclassneedstobeabletoserveasapropertydelegateistohavethesetwomethods.Nointerfaceisneeded.HereisanexampleimplementationofUserDelegate:

Page 411: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

classUserDelegate{

operatorfungetValue(thisRef:Any?,property:KProperty<*>):

User=readUserFromFile()

operatorfunsetValue(thisRef:Any?,property:KProperty<*>,

user:User){

saveUserToFile(user)

}

//...

}

ThesetValueandgetValuemethodsareusedtosetandgetvalueofaproperty(propertysettercallisdelegatedtoasetValuemethod,andpropertygetterisdelegatingvaluetothegetValuemethod).Bothfunctionsneedtobemarkedwiththeoperatorkeyword.Theyhavesomespecialsetofparametersthatdeterminewhereandtowhichpropertythedelegatecanserve.Ifapropertyisread-only,thenanobjectonlyneedstohaveagetValuemethodtobeabletoserveasitsdelegate:

classUserDelegate{

operatorfungetValue(thisRef:Any?,property:KProperty<*>):

User=readUserFromFile()

}

ThetypereturnedbythegetValuemethodandthetypeofpropertythattheuserdefinedinthesetValuemethoddeterminestypeofdelegatedproperty.

TypeofthefirstparameterofboththegetValueandsetValuefunctions(thisRef)iscontainsthereferencetocontextinwhichadelegateisused.Itcanbeusedtorestricttypesthatdelegatecanbeusedfor.Forexample,wecandefinedelegatethatmightbeusedonlyinsideanActivityclassinthefollowingway:

classUserDelegate{

operatorfungetValue(thisRef:Activity,property:KProperty<*>):

User=thisRef.intent

.getParcelableExtra("com.example.UserKey")

}

Aswe'veseen,therewillbeareferencetothisprovidedinallcontextswhereitisavailable.Onlyinsideextensionfunctionoronextensionpropertythereisnullplacedinstead.Referencetothisisusedtogetsomedatafromcontext.IfwewouldtypeittoActivity,thenwewouldbeabletousethisdelegateonlyinsideActivity(inanycontextwherethisisofthetypeActivity).

Also,ifwewanttoforcethedelegatetobeusedonlyonthetop-level,wecan

Page 412: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

thenspecifythefirstparameter(thisRef)typetoNothing?,becausetheonlypossiblevalueofthistypeisnull.

Anotherparameterinthesemethodsisproperty.Itcontainsareferencetoadelegatedproperty,whichcontainsitsmetadata(propertyname,type,andsoon).

Propertydelegationcanbeusedforpropertiesdefinedinanycontext(top-levelproperties,memberproperties,localvariables,andsoon):

varabySomeDelegate()//1

funsomeTopLevelFun(){

varbbySomeDelegate()//2

}

classSomeClass(){

varcbySomeDelegate()//3

funsomeMethod(){

valdbySomeDelegate()//4

}

}

1. Top-levelpropertywithdelegate2. Localvariable(insidetop-levelfunction)withdelegate3. Memberpropertywithdelegate4. Localvariable(insidemethod)withdelegate

Inthenextfewsections,wewilldescribedelegatesfromtheKotlinstandardlibrary.Theyareimportantnotonlybecausetheyareoftenuseful,butalsobecausetheyaregoodexamplesofhowpropertydelegationcanbeused.

Page 413: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

PredefineddelegatesTheKotlinstandardlibrarycontainssomepropertydelegatesthatareveryhandy.Let'sdiscusshowtheycanbeusedinreal-lifeprojects.

Page 414: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ThelazyfunctionSometimesweneedtoinitializeanobject,butwewanttomakesurethattheobjectwillbeinitializedonlyonce,whenitisusedforthefirsttime.InJava,wecouldsolvethisprobleminthefollowingway:

privatevar_someProperty:SomeType?=null

privatevalsomePropertyLock=Any()

valsomeProperty:SomeType

get(){

synchronized(somePropertyLock){

if(_someProperty==null){

_someProperty=SomeType()

}

return_someProperty!!

}

}

ThisconstructionisapopularpatterninJavadevelopment.Kotlinallowsustosolvethisprobleminamuchsimplerwaybyprovidingthelazydelegate.Itisthemostcommonlyuseddelegate.Itworksonlywithread-onlyproperties(val)anditsusageisasfollows:

valsomePropertybylazy{SomeType()}

Thelazyfunctioninthestandardlibraryfunctionthatisprovidingdelegate:

publicfun<T>lazy(initializer:()->T):

Lazy<T>=SynchronizedLazyImpl(initializer)

Formally,inthisexampleobjectofSynchronizedLazyImpl,itisusedasapropertydelegate.Although,mostoftenitiscalledlazydelegatefromitscorrespondingfunctionname.Thesamewayotherdelegatesarenamedfromthenamesofthefunctionsthatareprovidingthem.

Thelazydelegatealsohasathreadsafetymechanism.Bydefault,delegatesarefullythreadsafe,butwecanchangethisbehaviortomakethisfunctionmoreefficientinsituationswhereweknowthatthereneverwillbemorethanonethreadusingitatthesametime.Tofullyturnoffthread-safetymechanismsweneedtoplacetheenumtypevalueLazyThreadSafetyMode.NONEasafirstargumentofthelazyfunction:

Page 415: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

valsomePropertybylazy(LazyThreadSafetyMode.NONE){SomeType()}

Thankstothelazydelegate,theinitializationofthepropertyisdelayeduntilthevalueisneeded.Usageofthelazydelegateprovidesseveralbenefits:

Fasterclassinitializationleadingtofasterapplicationstartuptime,becausevalueinitializationisdelayeduntiltheyareusedforthefirsttimeSomevaluesmayneverbeusedforcertainflow,sotheywillneverbeinitialized--wearesavingresources(memory,processortime,battery)

Anotherbenefitisthatsomeobjectsneedtobecreatedlater,aftertheirclassinstanceiscreated.Forexample,inActivitywecannotaccesstheresourcesbeforelayoutissetusingthesetContentViewmethod,whichistypicallycalledinsidetheonCreatemethod.Iwillpresentitinthisexample.Let'slookattheJavaclasswithviewreferenceelementsfilledintheclassicJavaway:

//Java

publicclassMainActivityextendsActivity{

TextViewquestionLabelView

EditTextanswerLabelView

ButtonconfirmButtonView

@Override

publicvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

questionLabelView=findViewById<TextView>

(R.id.main_question_label);

answerLabelView=findViewById<EditText>

(R.id.main_answer_label);

confirmButtonView=findViewById<Button>

(R.id.main_button_confirm);

}

}

IfwetranslateitintoKotlin,one-to-one,itwilllookasfollows:

classMainActivity:Activity(){

varquestionLabelView:TextView?=null

varanswerLabelView:TextView?=null

varconfirmButtonView:Button?=null

overridefunonCreate(savedInstanceState:Bundle){

super.onCreate(savedInstanceState)

setContentView(R.layout.main_activity)

questionLabelView=findViewById<TextView>

(R.id.main_question_label)

Page 416: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

answerLabelView=findViewById<TextView>

(R.id.main_answer_label)

confirmButtonView=findViewById<Button>

(R.id.main_button_confirm)

}

}

Usingthelazydelegate,wecanimplementthisbehaviorinasimplerway:

classMainActivity:Activity(){

valquestionLabelView:TextViewbylazy

{findViewById(R.id.main_question_label)asTextView}

valanswerLabelView:TextViewbylazy

{findViewById(R.id.main_answer_label)asTextView}

valconfirmButtonView:Buttonbylazy

{findViewById(R.id.main_button_confirm)asButton}

overridefunonCreate(savedInstanceState:Bundle){

super.onCreate(savedInstanceState)

setContentView(R.layout.main_activity)

}

}

Thebenefitsofthisapproachareasfollows:

Thepropertyisdeclaredandinitializedinasingleplace,sothecodeismoreconcise.Thepropertiesarenon-nullableinsteadofnullable.Thispreventslotsofuselessnullabilitychecks.Thepropertiesarereadonlysothankstothatwehaveallbenefitslikethreadssynchronizationorsmartcasts.Thelambdapassedtothelazydelegate(containingfindViewById)willbeexecutedonlywhenthepropertyisaccessedforthefirsttime.Valueswillbetakenlaterthanduringclasscreation.Thiswillspeed-upthestartup.Ifwewon'tusesomeoftheseviews,theirvalueswon'tbetakenatall(findViewByIdisnotreallyanefficientoperationwhentheviewiscomplex).Notusedpropertywillbemarkedbythecompiler.InJavaimplementationitwon't,becausevaluesetwouldbenoticedbythecompilerasusage.

Wecanimprovetheprecedingimplementationbyextractingthecommonbehaviorandconvertingitintoanextensionfunction:

fun<T:View>Activity.bindView(viewId:Int)=lazy{findViewById(viewId)asT}

Then,wecandefinetheviewbindingsinsimplerandmoreconcisecode:

Page 417: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

classMainActivity:Activity(){

varquestionLabelView:TextViewbybindView(R.id.main_question_label)//1

varanswerLabelView:TextViewbybindView(R.id.main_answer_label)//1

varconfirmButtonView:ButtonbybindView(R.id.main_button_confirm)//1

overridefunonCreate(savedInstanceState:Bundle){

super.onCreate(savedInstanceState)

setContentView(R.layout.main_activity)

}

}

1. Wedon'tneedtosettypeprovidedtothebindViewfunctionbecauseitisinferredfrompropertytype.

NowwehaveasingledelegatethatcallsfindViewByIdunderthehood,whenweaccessaparticularviewforthefirsttime.Thisisaveryconcisesolution.

Thereisanotherwayofdealingwiththisproblem.ThecurrentpopularoneistheKotlinAndroidExtensionplugin,whichgeneratesauto-bindingtoviewsinActivitiesandFragments.WewilldiscussthepracticalapplicationsinChapter9,MakingyourMarvelGalleryApplication.

Evenwithsuchsupport,therearestillbenefitsfromstayingwithbindings.Oneisexplicitknowledgeofwhatelementsofviewweareusing,andanotheristheseparationbetweenthenameofelementIDandnameofavariableinwhichweholdthiselement.Alsocompilationtimeisfaster.

ThesamemechanismcanbeappliedtosolveotherAndroidrelatedproblems.Forexample,whenwepassanargumenttoActivity.ThestandardJavaimplementationlooksasfollows:

//Java

classSettingsActivityextendsActivity{

finalDoctorDOCTOR_KEY="doctorKey"

finalStringTITLE_KEY="titleKey"

Doctordoctor

Addressaddress

Stringtitle

publicstaticvoidstart(Contextcontext,Doctordoctor,

Stringtitle){

Intentintent=newIntent(context,SettingsActivity.class)

intent.putExtra(DOCTOR_KEY,doctor)

intent.putExtra(TITLE_KEY,title)

context.startActivity(intent)

}

Page 418: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

@Override

publicvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

doctor=getExtras().getParcelable(DOCTOR_KEY)

title=getExtras().getString(TITLE_KEY)

ToastHelper.toast(this,doctor.id)

ToastHelper.toast(this,title)

}

}

WecouldwritethesameimplementationinKotlin,butwecanalsoretrieveparametervalues(getString/getParcerable)togetherwiththevariabledeclaration.Todothis,weneedthefollowingextensionfunctions:

fun<T:Parcelable>Activity.extra(key:String)=lazy

{intent.extras.getParcelable<T>(key)}

funActivity.extraString(key:String)=lazy

{intent.extras.getString(key)}

ThenwecangetextraparametersbyusingextraandextraStringdelegates:

classSettingsActivity:Activity(){

privatevaldoctorbyextra<Doctor>(DOCTOR_KEY)//1

privatevaltitlebyextraString(TITLE_KEY)//1

overridefunonCreate(savedInstanceState:Bundle?){

super.onCreate(savedInstanceState)

setContentView(R.layout.settings_activity)

toast(doctor.id)//2

toast(title)//2

}

companionobject{//3

constvalDOCTOR_KEY="doctorKey"

constvalTITLE_KEY="titleKey"

funstart(context:Context,doctor:Doctor,title:String){//3

ontext.startActivity(getIntent<SettingsActivity>().apply{//4

putExtra(DOCTOR_KEY,doctor)//5

putExtra(TITLE_KEY,title)//5

})

}

}

}

1. WearedefiningpropertieswhichvaluesshouldberetrievedfromActivityargumentsusingcorrespondingkeys.

2. HereweaccesspropertiesfromargumentswithintheonCreatemethod.Whenweaskforproperty(usegetter),thelazydelegatewillgetitsvalue

Page 419: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

fromextrasandstoreitforlaterusage.

3. Tomakeastaticmethodtostartactivity,weneedtouseacompanionobject.

4. SettingsActivity::class.javaistheanalogueofJavaclassreferenceSettingsActivity.class.

5. WeareusingmethodsdefinedinChapter7,ExtensionFunctionsandProperties.

WecanalsomakefunctionstoretrieveothertypesthatcanbeheldbyBundle(forexample,Long,Serializable).ThisisaprettynicealternativetotheargumentinjectionlibrariessuchasActivityStarter,whenwewanttokeepareallyfastcompilationtime.Wecanusesimilarfunctionstobindstrings,colors,services,repositories,andotherpartsofmodelandlogic:

fun<T>Activity.bindString(@IdResid:Int):Lazy<T>=

lazy{getString(id)}

fun<T>Activity.bindColour(@IdResid:Int):Lazy<T>=

lazy{getColour(id)}

InActivity,everythingthatisheavyordependsonargumentsshouldbedeclaredusinglazydelegate(orprovidedasynchronously).Alsoweshoulddefineaslazyalltheelementsthatdependonelementsthatneedtobeinitializedlazily.Forexample,presenterdefinition,whichdependsonthedoctorproperty:

valpresenterbylazy{MainPresenter(this,doctor)}

Otherwise,theattempttoconstructaMainPresenterobjectwilltakeplaceduringclasscreationwhenwecannotyetreadvaluesfromtheintentanditwouldn'tbeabletofillthedoctorproperty,andtheapplicationwouldcrash.

IthinkthattheseexamplesareenoughtoconvinceusthatthelazydelegateisreallyusefulinAndroidprojects.Itisalsoagoodpropertydelegatetostartwith,asitissimpleandelegant.

Page 420: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ThenotNullfunctionThenotNulldelegateisthesimpleststandardlibrarydelegate,andthatiswhyitwillbepresentedfirst.Theusageisasfollows:

varsomeProperty:SomeTypebynotNull()

Functionsthatprovidemoststandardlibrarydelegates(includingthenotNullfunction)aredefinedinobjectdelegates.Tousethem,weneedtoeitherrefertothisobject(Delegates.notNull())orimportit(importkotlin.properties.Delegates.notNull).Wewillassumeinexamplesthatthisobjectisimportedsowewillomitreferencetoit.

ThenotNulldelegateallowsustodefineavariableasnon-nullable,thatisinitializedatalatertimeandnotduringtheobjectconstructiontime.Wecandefinevariabletobenon-nullablewithoutprovidingadefaultvalue.ThenotNullfunctionisanalternativetolateinit:

lateinitvarsomeProperty:SomeType

ThenotNulldelegateprovidesnearlythesameeffectaslateinit(onlytheerrormessageisdifferent).Inthecaseoftryingtousethispropertybeforesettingthevaluefirst,itwillthrowanIllegalStateExceptionanditwillterminateanAndroidapplication.Therefore,itshouldbeusedonlywhenweknowthatavaluewillbesetbeforethefirstattemptofusage.

ThedifferencebetweenlateinitandthenotNulldelegateisprettysimple.lateinitisfasterthannotNulldelegatesoitshouldbeusedinsteadofnotNulldelegateasoftenaspossible.Butithasrestrictions,lateinitcannotbeusedforprimitivesorfortop-levelproperties,sointhiscase,notNullisusedinstead.

Let'slookatthenotNulldelegateimplementation.HereisthenotNullfunctionimplementation:

publicfun<T:Any>notNull():ReadWriteProperty<Any?,T>=

NotNullVar()

Aswecansee,notNullisactuallyafunctionreturninganobjectthatisaninstance

Page 421: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ofouractualdelegatehiddenbehindaReadWritePropertyinterface.Let'slookatanactualdelegatedefinition:

privateclassNotNullVar<T:Any>():ReadWriteProperty<Any?,T>{//1

privatevarvalue:T?=null

publicoverridefungetValue(thisRef:Any?,

property:KProperty<*>):T{

returnvalue?:throwIllegalStateException("Property

${property.name}shouldbeinitializedbeforeget.")//2

}

publicoverridefunsetValue(thisRef:Any?,

property:KProperty<*>,value:T){

this.value=value

}

}

1. Classisprivate.ItispossiblebecauseitisprovidedbyfunctionnotNull,whichisreturningitasReadWriteProperty<Any?,T>,whichispublicinterface.

2. Hereweseehowareturnvalueisprovided.Ifitisnullduringusage,thenvaluewasnotsetandthemethodwillthrowanerror.Otherwise,itisreturningthevalue.

Thisdelegateshouldbeprettysimpletounderstand.ThesetValuefunctionsetsthevaluetoanullablefieldandgetValuereturnsthisfieldifitisnotnull,andthrowsanexceptionifitis.Hereisanexampleofthiserror:

varname:StringbyDelegates.notNull()

println(name)

//Error:Propertynameshouldbeinitializedbeforeget.

Thisisareallysimpleexampleofdelegatedpropertiesusage,butalsoagoodintroductiontohowpropertydelegatesworks.Delegatedpropertiesareverypowerfulconstructsthathavemultipleapplications.

Page 422: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

TheobservabledelegateAnobservableisthemostusefulstandardlibrarydelegateformutableproperties.Everytimeavalueisset(setValuemethodiscalled),thelambdafunctionfromthedeclarationisinvoked.Asimpleexampleofobservabledelegateisasfollows:

varname:StringbyDelegates.observable("Empty"){

property,oldValue,newValue->//1

println("$oldValue->$newValue")//2

}

//Usage

name="Martin"//3,

Prints:Empty->Martin

name="Igor"//3,

Prints:Martin->Igor

name="Igor"//3,4

Prints:Igor->Igor

1. Theargumentsoflambdafunctionareasfollows:property:Referencetothedelegatedproperty.Hereitisreferencetoname.ThisisthesameaspropertyfromsetValueandgetValue,whichwasdescribed.ItisoftheKPropertytype.Inthiscase(andinmostcases)wecanputtheunderscore("_"sign)insteadwhenitisnotused.oldValue:Thepreviousvalueoftheproperty(beforethechange).newValue:Thenewvalueoftheproperty(afterthechange).

2. Thelambdafunctionwillbeinvokedeachtimeanewvalueissettotheproperty.

3. Whenwesetthenewvalue,thenthevalueisupdated,butatthesametimelambdamethoddeclaredindelegateiscalled.

4. Notethatlambdaisinvokedeachtimesetterisusedanditdoesn'tmatterifanewvalueisequaltoprevious.

Itisparticularlyimportanttorememberthatlambdaiscalledeachtimeanewvalueisset,andnotwhenanobject'sinnerstateischanged.Forexample:

varlist:MutableList<Int>byobservable(mutableListOf())

{_,old,new->

println("Listchangedfrom$oldto$new")

}

//Usage

Page 423: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

list.add(1)//1

list=mutableListOf(2,3)

//2,prints:Listchangedfrom[1]to[2,3]

1. Doesnotprintanything,becausewedon'tchangetheproperty(thesetterisnotused).Weonlychangethepropertydefinedinsidethelist,butnottheobjectitself.

2. Herewechangethevalueoflist,sothelambdafunctionfromobservabledelegateiscalledandtextisprinted.

Observabledelegateisveryusefulforimmutabletypes,asopposedtomutableones.Fortunately,allbasictypesinKotlinareimmutablebydefault(List,Map,Set,Int,String).Let'slookatapracticalAndroidexample:

classSomeActivity:Activity(){

varlist:List<String>byDelegates.observable(emptyList()){

prop,old,new->if(old!=new)updateListView(new)

}

//...

}

Everytimewechangethelist,theviewisupdated.NotethatwhileListisimmutable,weneedtousesetterwhenwewanttoapplyanychanges,sowecanbesurethatafterthisoperationthelistwillbeupdated.ItismucheasierthanrememberingtocalltheupdateListViewmethodeverytimethelistchanges.Thispatterncanbeusedwidelyintheprojecttodeclarepropertiesthatareeditingviews.Itchangesthewaytheupdateviewmechanismcanwork.

AnotherproblemthatcanbesolvedusinganobservabledelegateisthatinListAdapterstherewasalwaysaproblemthatnotifyDataSetChangedhadtobecalledeachtimeelementsonthelistwerechanged.InJava,theclassicsolutionwastoencapsulatethislist,andcallnotifyDataSetChangedineachfunctionthatismodifyingit.InKotlin,wecansimplifythisusinganobservablepropertydelegate:

varlist:List<LocalDate>byobservable(list){_,old,new->//1

if(new!=old)notifyDataSetChanged()

}

1. Notethatherelistisimmutable,sothereisnowaytochangeitselementswithoutusingnotifyDataSetChanged.

Theobservabledelegateisusedtodefinebehaviorthatshouldhappenonthe

Page 424: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

propertyvaluechange.Itismostfrequentlyusedwhenwehaveoperationsthatshouldbedoneeverytimewechangeaproperty,orwhenwewanttobindapropertyvaluewithavieworsomeothervalues.Butinsidethefunctionwecannotdecideifanewvaluewillbesetornot.Forthis,thevetoabledelegateisusedinstead.

Page 425: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ThevetoabledelegateThevetoablefunctionisastandardlibrarypropertydelegatethatworkssimilarasanobservabledelegate,butwithtwomaindifferences:

ThelambdafromanargumentiscalledbeforeanewvalueissetItallowsthelambdafunctionfromadeclarationtodecideifanewvalueshouldbeacceptedorrejected

Forexample,ifwehaveanassumptionthatthelistmustalwayscontainlargernumberofitemsthantheoldone,thenwewilldefinethefollowingvetoabledelegate:

varlist:List<String>byDelegates.vetoable(emptyList())

{_,old,new->

new.size>old.size

}

Ifanewlistwillnotcontainalargernumberofitemsthantheoldone,thenthevaluewillnotchange.Sowecantreatvetoablelikeobservable,whichisalsodecidingifthevalueshouldbechangedornot.Let'ssupposethatwewanttohavealistboundedtoview,butitneedstohavethreeelementsatleast.Wedon'tallowanychangethatwillmakeitpossibletohavefewerelements.Theimplementationwouldlookasfollows:

varlist:List<String>byDelegates.vetoable(emptyList())

{prop,old,new->

if(new.size<3)return@vetoablefalse//1

updateListView(new)

true//2

}

1. Ifanewlistsizeissmallerthan3,thenwedonotacceptit,andreturnfalsefromlambda.Thisfalsevaluereturnedbyreturnstatementwithlabel(thatisusedtothereturnfromthelambdaexpression)istheinformationthatthenewvalueshouldn'tbeaccepted.

2. Thislambdafunctionneedstoreturnavalue.Thisvalueistakeneitherfromreturnwithalabelorbythelastlineofthelambdabody.Herevaluetrueinformsthatanewvalueshouldbeaccepted.

Page 426: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Hereisasimpleexampleofitsusage:

listVetoable=listOf("A","B","C")//UpdateA,B,C

println(listVetoable)//Prints:[A,B,C]

listVetoable=listOf("A")//Nothinghappens

println(listVetoable)//Prints:[A,B,C]

listVetoable=listOf("A","B","C","D","E")

//Prints:[A,B,C,D,E]

Wecouldalsomakeitunchangeablebecauseofsomeotherreasons,forexample,wemightstillbeloadingthedata.Also,thevetoablepropertydelegatecanbeusedinvalidators.Forexample:

varname:StringbyDelegates.vetoable("")

{prop,old,new->

if(isValid(new)){

showNewData(new)

true

}else{

showNameError()

false

}

ThispropertycanbechangedonlytoavaluethatiscorrectaccordingtothepredicateisValid(new).

Page 427: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

PropertydelegationtoMaptypeThestandardlibrarycontainsextensionsforMapandMutableMapwiththeStringkeytypethatprovidesthegetValueandsetValuefunctions.Thankstothem,mapcanalsobeusedasapropertydelegate:

classUser(map:Map<String,Any>){//1

valname:Stringbymap

valkotlinProgrammer:Booleanbymap

}

//Usage

valmap:Map<String,Any>=mapOf(//2

"name"to"Marcin",

"kotlinProgrammer"totrue

)

valuser=User(map)//3

println(user.name)//Prints:Marcin

println(user.kotlinProgrammer)//Prints:true

1. MapkeytypeneedstobeString,whilevaluetypeisnotrestricted.ItisoftenAnyorAny?

2. CreatingMapthatcontainsallthevalues3. Provideamaptoanobject.

ThiscanbeusefulwhenwearekeepingdatainMap,andalsoforfollowing:

WhenwewanttosimplifytheaccesstothesevaluesWhenwedefineastructurethatistellinguswhatkindofkeysweshouldexpectinthismapWhenweaskforapropertythatisdelegatedtoMap,itsvaluewillbetakenfromthismapvalueforakeyequaltothepropertyname

Howisitimplemented?Hereisthesimplifiedcodefromthestandardlibrary:

operatorfun<V,V1:V>Map<String,V>.getValue(//1

thisRef:Any?,//2

property:KProperty<*>):V1{//3

valkey=property.name//4

valvalue=get(key)

if(value==null&&!containsKey(key)){

throwNoSuchElementException("Key${property.name}

ismissinginthemap.")

}else{

returnvalueasV1//3

}

Page 428: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

}

1. Visatypeofvalueonthelist2. thisRefisoftypeAny?,soMapcanbeusedaspropertydelegateinanycontext3. V1isreturntype.Thisisofteninferredfromproperty,butitmustbesubtype

oftypeV4. Nameofthepropertyisusedaskeyonmap.

Keepinmindthatthisisjustanextensionfunction.AllthatanobjectneedstobeadelegateistocontainthegetValuemethod(andsetValue,forread-writeproperties).Wecanevencreateadelegatefromanobjectofananonymousclassusingtheobjectdeclaration:

valsomePropertybyobject{//1

operatorfungetValue(thisRef:Any?,

property:KProperty<*>)="Something"

}

println(someProperty)//prints:Something

1. Objectisnotimplementinganyinterface.ItjustcontainsthegetValuemethodwithpropersignature.Anditisenoughtomakeitworkasaread-onlypropertydelegate.

Notethatinmapthereneedstobeanentrywithsuchanamewhenweareaskingforvalueofproperty,otherwiseanerrorwillbethrown(makingthepropertynullabledoesnotchangeit).

Delegatingfieldstomapcanbeuseful,forexample,whenwehaveanobjectfromanAPIwithdynamicfields.Wewouldliketotreattheprovideddataasanobjecttohaveeasieraccesstoitsfields,butwealsoneedtokeepitasamaptobeabletolistallthefieldsgivenbyanAPI(evenonesthatwewerenotexpecting).

Inthepreviousexample,weusedMap,whichisimmutable;therefore,theobjectpropertieswereread-only(val).Ifwewanttomakeanobjectthatcanbechanged,thenweshoulduseMutableMap,andthenthepropertiescanbedefinedasmutable(var).Hereisanexample:

classUser(valmap:MutableMap<String,Any>){

varname:Stringbymap

varkotlinProgrammer:Booleanbymap

overridefuntoString():String="Name:$name,

Page 429: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Kotlinprogrammer:$kotlinProgrammer"

}

//Usage

valmap=mutableMapOf(//1

"name"to"Marcin",

"kotlinProgrammer"totrue

)

valuser=User(map)

println(user)//prints:Name:Marcin,Kotlinprogrammer:true

user.map.put("name","Igor")//1

println(user)//prints:Name:Igor,Kotlinprogrammer:true

user.name="Michal"//2

println(user)//prints:Name:Michal,Kotlinprogrammer:true

1. Propertyvaluecanbechangedjustbychangingthevalueofthemap2. Propertyvaluecanbealsochangedlikeinanyotherproperty.Whatis

reallyhappeningthereisthatvaluechangeisdelegatedtosetValue,whichischangingmap.

Whilepropertiesherearemutable,thesetValuefunctionmustalsobeprovided.ItisimplementedasanextensionfunctionforMutableMap.Hereisthesimplifiedcode:

operatorfun<V>MutableMap<String,V>.setValue(

thisRef:Any?,

property:KProperty<*>,

value:V

){

put(property.name,value)

}

Notehowevensosimplefunctionscanallowsuchinnovativewayofusingthecommonobjects.Thisshowswhatpossibilitiespropertydelegatesaregiving.

Kotlinallowsustodefinecustomdelegates.Rightnow,wecanfindmanylibrariesthatprovidesnewpropertydelegatesthatcanbeusedfordifferentpurposesinAndroid.TherearevariouswaysinwhichpropertydelegationcanbeusedinAndroid.Inthenextsection,wewillseesomeexamplesofcustompropertydelegates,andwewilltakealookatcaseswherethisfeaturecanbereallyhelpful.

Page 430: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

CustomdelegatesAllpreviousdelegatescamefromthestandardlibrary,butwecaneasilyimplementourownpropertydelegates.We'veseenthatinordertoallowaclasstobeadelegate,weneedtoprovidethegetValueandsetValuefunctions.Theymusthaveaconcretesignature,butthereisnoneedtoextendaclassorimplementtheinterface.Touseobjectasadelegate,wedon'tevenneedtochangeitsinternalimplementation,becausewecandefinegetValueandsetValueasextensionfunctions.However,whenwearecreatingcustomclassestobeadelegates,theninterfacemaybeuseful:

Itwoulddefinefunctionsstructure,sowecangeneratepropermethodsinAndroidStudio.Ifwearecreatinglibraries,thenwemightwanttomakedelegatesclassestobeprivateorinternaltopreventinappropriateusageofthem.We'veseenthissituationinthenotNullsection,wheretheclassNotNullVarwasprivateandservedasaReadWriteProperty<Any?,T>whichisaninterface.

InterfacesthatprovidefullfunctionalitytoallowsomeclasstobedelegateareReadOnlyProperty(forread-onlyproperties)andReadWriteProperty(forread-writeproperties).Theseinterfacesarereallyuseful,solet'slookattheirdefinitions:

publicinterfaceReadOnlyProperty<inR,outT>{

publicoperatorfungetValue(thisRef:R,

property:KProperty<*>):T

}

publicinterfaceReadWriteProperty<inR,T>{

publicoperatorfungetValue(thisRef:R,

property:KProperty<*>):T

publicoperatorfunsetValue(thisRef:R,

property:KProperty<*>,value:T)

}

Thevaluesofparameterswerealreadyexplained,butlet'slookatthemagain:

thisRef:Areferencetoanobjectwherethedelegateisused.Itstypedefinesthecontextinwhichthedelegatecanbeused.property:Areferencethatcontainsdataaboutadelegatedproperty.Itcontainsallinformationaboutthisproperty,suchasitsnameortype.

Page 431: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

value:Anewvaluetoset.

TheparametersthisRefandpropertyarenotusedinthefollowingdelegates:Lazy,ObservableandVetoable.Map,MutableMap,andnotNullusepropertytoobtainthenameofthepropertyforthekey.Buttheseparameterscanbeusedindifferentcases.

Let'slookatsomesmall,butuseful,examplesofcustompropertydelegates.We'veseenthelazypropertydelegateforread-onlyproperties;however,sometimesweneedalazypropertythatismutable.Ifitwouldbeaskedforthevaluebeforeinitialization,thenitshouldfillitsvaluefromtheinitializerandreturnit.Inothercasesitshouldactlikeanormalmutableproperty:

fun<T>mutableLazy(initializer:()->T):ReadWriteProperty<Any?,T>=MutableLazy<T>(initializer)

privateclassMutableLazy<T>(valinitializer:()->T):ReadWriteProperty<Any?,T>{

privatevarvalue:T?=null

privatevarinitialized=false

overridefungetValue(thisRef:Any?,property:KProperty<*>):T{

synchronized(this){

if(!initialized){

value=initializer()

}

returnvalueasT

}

}

overridefunsetValue(thisRef:Any?,

property:KProperty<*>,value:T){

synchronized(this){

this.value=value

initialized=true

}

}

}

1. Thedelegateishiddenbehindtheinterfaceandservedbyafunction,andassuchallowsustochangetheimplementationofMutableLazywithoutworryingifitwillaffectthecodethatisusingit.

2. WeareimplementingReadWriteProperty.Itisoptional,butreallyusefulbecauseitisimposingthecorrectstructureofaread-writeproperty.ItsfirsttypeisAny?meaningthatweareallowedtousethispropertydelegateinanycontext,includingtop-level.Itssecondtypeisgeneric.Notethatthereisnorestrictionsonthistype,soitmightbenullabletoo.

3. Thevalueofthepropertyisstoredinthevalueproperty,anditsexistenceis

Page 432: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

storedinaninitializedproperty.WeneedtodoitthiswaybecausewewanttoallowTtobeanullabletype.Thennullinthevaluecouldmeaneitherthatitwasnotyetinitializedorthatitisjustequaltonull.

4. Wedon'tneedtousetheoperatormodifier,becauseitisalreadyusedininterface.

5. IfgetValueiscalledbeforeanyvalueisset,thenthevalueisfilledusinginitializer.

6. WeneedtocastthevaluetoTbecauseitmightbenot-null,andweinitializedvalueasnullablewithnullasaninitialvalue.

Thispropertydelegatemightbeusefulindifferentuse-casesinAndroiddevelopment;forexample,whenadefaultvalueofapropertyisstoredinafile,andweneedtoreadit(whichisaheavyoperation):

vargameMode:GameModebyMutableLazy{

getDefaultGameMode()

}

varmapConfiguration:MapConfigurationbyMutableLazy{

getSavedMapConfiguration()

}

varscreenResolution:ScreenResolutionbyMutableLazy{

getOptimalScreenResolutionForDevice()

}

Thisway,ifausersetsacustomvalueofthispropertybeforeitsusage,wewon'thavetocalculateitourselves.Secondcustompropertydelegatewillallowustodefinepropertygetter:

vala:Intget()=1

valb:Stringget()="KOKO"

valc:Intget()=1+100

BeforeKotlin1.1definedit,wealwayshadtodefinethetypeofproperty.Toavoidit,wecandefinethefollowingextensionfunctiontofunctionaltype(therefore,alsothelambdaexpression):

inlineoperatorfun<R>(()->R).getValue(

thisRef:Any?,

property:KProperty<*>

):R=invoke()

Thenwecandefinethepropertieswithsimilarbehaviorthisway:

Page 433: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

valaby{1}

valbby{"KOKO"}

valcby{1+100}

Thiswayisnotpreferredbecauseofitsdecreasedefficiency,butitisaniceexampleofpossibilitiesthatdelegatedpropertiesprovidesus.Suchasmallextensionfunctionismakingfunctionaltypetobepropertydelegate.Thisis,thesimplifiedcodeinKotlinaftercompilation(notethattheextensionfunctionismarkedasinline,soitscallswerereplacedwithitsbody):

privateval`a$delegate`={1}

vala:Intget()=`a$delegate`()

privateval`b$delegate`={"KOKO"}

valb:Stringget()=`b$delegate`()

privateval`c$delegate`={1+100}

valc:Intget()=`c$delegate`()

Inthenextsection,wearegoingtoseesomecustomdelegatescreatedforrealprojects.Theywillbepresentedtogetherwiththeproblemsthattheysolve.

Page 434: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ViewbingingWhenweareusingModel-View-Presenter(MVP)intheproject,thenweneedtomakeallthechangesinViewbyPresenter.Thus,weareforcedtocreatemultiplefunctionsontheview,suchas:

overridefungetName():String{

returnnameView.text.toString()

}

overridefunsetName(name:String){

nameView.text=name

}

Wealsohavetodefinethefunctionsinthefollowinginterface:

interfaceMainView{

fungetName():String

funsetName(name:String)

}

Wemaysimplifytheprecedingcodeandreducetheneedforsetter/gettermethodsbyusingpropertybinding.Wecanbindthepropertytoviewelement.Thisistheresultwewouldliketoachieve:

overridevarname:StringbybindToTex(R.id.textView)

Andinterface:

interfaceMainView{

varname:String

}

Theprecedingexampleismoreconciseandeasiertomaintain.NotethatweprovideelementIDbyargument.Asimpleclassthatwillgiveustheexpectedresultsisasfollows:

funActivity.bindToText(

@IdResviewId:Int)=object:

ReadWriteProperty<Any?,String>{

valtextViewbylazy{findViewById<TextView>(viewId)}

overridefungetValue(thisRef:Any?,

property:KProperty<*>):String{

returntextView.text.toString()

}

Page 435: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

overridefunsetValue(thisRef:Any?,

property:KProperty<*>,value:String){

textView.text=value

}

}

Wecouldcreateasimilarbindingfordifferentviewpropertiesanddifferentcontexts(Fragment,Service).Anotherreallyusefultoolisbindingtovisibility,whichisbindingalogicalproperty(withthetypeBoolean)tothevisibilityofaviewelement:

funActivity.bindToVisibility(

@IdResviewId:Int)=object:

ReadWriteProperty<Any?,Boolean>{

valviewbylazy{findViewById(viewId)}

overridefungetValue(thisRef:Any?,

property:KProperty<*>):Boolean{

returnview.visibility==View.VISIBLE

}

overridefunsetValue(thisRef:Any?,

property:KProperty<*>,value:Boolean){

view.visibility=if(value)View.VISIBLEelseView.GONE

}

}

TheseimplementationsprovidepossibilitiesthatwouldbereallyhardtoachieveinJava.SimilarbindingsmightbecreatedforotherViewelementstomakeusingMVPshorterandsimpler.Thesnippetsthatwerejustpresentedareonlysimpleexamples,butbetterimplementationscanbefoundinthelibraryKotlinAndroidViewBindings(https://github.com/MarcinMoskala/KotlinAndroidViewBindings).

Page 436: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

PreferencebindingToshowmorecomplexexamples,wewillpresenttheattempttohelpwiththeSharedPreferencesusage.TherearebetterKotlinapproachesforthisproblem,butthisattemptisnicetoanalyze,anditisareasonableexampleofweusepropertydelegateonextensionproperty.Asaresult,wewanttobeabletotreatvaluessavedinSharedPreferencesasiftheywerepropertiesofaSharedPreferencesobject.Hereisexampleusage:

preferences.canEatPie=true

if(preferences.canEatPie){

//Code

}

Wecanachieveitifwemakethefollowingextensionpropertydefinitions:

varSharedPreferences.canEatPie:

BooleanbybindToPreferenceField(true)//1

varSharedPreferences.allPieInTheWorld:

LongbybindToPreferenceField(0,"AllPieKey")//2

1. ThepropertyoftypeBoolean.Whenapropertyisnon-nullable,thandefaultvalueshavetobeprovidedinthefirstargumentoffunction.

2. Thepropertycanhavecustomkeyprovided.Itisusefulinreal-lifeprojects,wherewemusthavecontroloverthiskey(forexample,tonotchangeitunintentionallyduringpropertyrename).

Let'sanalyzehowitworksbydeepinvestigationofthenot-nullproperty.First,let'slookattheproviderfunctions.NotethatthetypeofthepropertyisdeterminingthewaythevalueistakenfromSharedPreferences(becausetherearedifferentfunctions,suchasgetString,getInt,andsoon).Toobtainit,weneedthisclasstypetobeprovidedasthereifiedtypeoftheinlinefunction,orthroughtheparameter.Thisiswhatadelegateproviderfunctionlookslike:

inlinefun<reifiedT:Any>bindToPreferenceField(

default:T?,

key:String?=null

):ReadWriteProperty<SharedPreferences,T>//1

=bindToPreferenceField(T::class,default,key)

fun<T:Any>bindToPreferenceField(//2

Page 437: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

clazz:KClass<T>,

default:T?,

key:String?=null

):ReadWriteProperty<SharedPreferences,T>

=PreferenceFieldBinder(clazz,default,key)//1

1. BothfunctionsarereturningobjectbehindinterfaceReadWriteProperty<SharedPreferences,T>.NotethatcontexthereissettoSharedPreferences,soitcanbeusedonlythereorinSharedPreferencesextensions.Thisfunctionisdefinedbecausethetypeparametercannotberedefinedandweneedtoprovidetypeasanormalparameter.

2. NotethatthebindToPreferenceFieldfunctioncannotbeprivateorinternal,becauseinlinefunctionscanuseonlyfunctionswiththesameorlessrestrictedmodifiers.

Finally,let'sseethePreferenceFieldDelegateclass,whichisourdelegate:

internalopenclassPreferenceFieldDelegate<T:Any>(

privatevalclazz:KClass<T>,

privatevaldefault:T?,

privatevalkey:String?

):ReadWriteProperty<SharedPreferences,T>{

overrideoperatorfungetValue(thisRef:SharedPreferences,

property:KProperty<*>):T

=thisRef.getLong(getValue<T>(clazz,default,getKey(property))

overridefunsetValue(thisRef:SharedPreferences,

property:KProperty<*>,value:T){

thisRef.edit().apply

{putValue(clazz,value,getKey(property))}.apply()

}

privatefungetKey(property:KProperty<*>)=

key?:"${property.name}Key"

}

NowweknowhowthethisRefparameterisused.ItisofthetypeSharedPreferences,andwecanuseittogetandsetallthevalues.Herearedefinitionsofthefunctionsusedtogetandsavevaluesdependingonpropertytype:

internalfunSharedPreferences.Editor.putValue(clazz:KClass<*>,value:Any,key:String){

when(clazz.simpleName){

"Long"->putLong(key,valueasLong)

"Int"->putInt(key,valueasInt)

"String"->putString(key,valueasString?)

"Boolean"->putBoolean(key,valueasBoolean)

"Float"->putFloat(key,valueasFloat)

else->putString(key,value.toJson())

}

}

internalfun<T:Any>SharedPreferences.getValue(clazz:KClass<*>,default:T?,key:String):T=when(clazz.simpleName){

Page 438: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

"Long"->getLong(key,defaultasLong)

"Int"->getInt(key,defaultasInt)

"String"->getString(key,defaultas?String)

"Boolean"->getBoolean(key,defaultasBoolean)

"Float"->getFloat(key,defaultasFloat)

else->getString(key,default?.toJson()).fromJson(clazz)

}asT

WealsoneedtoJsonandfromJsondefined:

varpreferencesGson:Gson=GsonBuilder().create()

internalfunAny.toJson()=preferencesGson.toJson(this)!!

internalfun<T:Any>String.fromJson(clazz:KClass<T>)=preferencesGson.fromJson(this,clazz.java)

WithsuchdefinitionswecandefineadditionalextensionpropertiestoSharedPreferences:

varSharedPreferences.canEatPie:BooleanbybindToPreferenceField(true)

Aswe'vealreadyseeninChapter7,ExtensionFunctionsandProperties,thereisnosuchthinginJavaasafieldthatwemightaddtoaclass.Underthehood,theextensionpropertyiscompiledtogetterandsetterfunctions,andtheyaredelegatingcallstoacreateddelegate:

val'canEatPie$delegate'=bindToPreferenceField(Boolean::class,true)

funSharedPreferences.getCanEatPie():Boolean{

return'canEatPie$delegate'.getValue(this,

SharedPreferences::canEatPie)

}

funSharedPreferences.setCanEatPie(value:Boolean){

'canEatPie$delegate'.setValue(this,SharedPreferences::canEatPie,

value)

}

Alsorememberthatextensionfunctionsare,infact,juststaticfunctionswithanextensiononthefirstparameter:

val'canEatPie$delegate'=bindToPreferenceField(Boolean::class,true)

fungetCanEatPie(receiver:SharedPreferences):Boolean{

return'canEatPie$delegate'.getValue(receiver,

SharedPreferences::canEatPie)

}

funsetCanEatPie(receiver:SharedPreferences,value:Boolean){

'canEatPie$delegate'.setValue(receiver,

SharedPreferences::canEatPie,value)

}

Page 439: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Presentedexamplesshouldbeenoughtounderstandhowpropertydelegatesareworkingandhowtheycanbeused.PropertydelegatesareusedintensivelyinKotlinopensourcelibraries.TheyareusedtomakefastandsimpleDependencyInjection(forexample,Kodein,Injekt,TornadoFX),bindingtoviews,SharedPreferencesorotherelements(attemptsalreadyshownincludesPreferenceHolder,andKotlinAndroidViewBindings),todefinepropertykeysonconfigurationdefinition(forexample,Konfig),oreventodefineadatabasecolumnstructure(forexample,Kwery).Stillthereisabigfieldofusagesthatarewaitingtobediscovered.

Page 440: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ProvidingadelegateSinceKotlin1.1,thereisanoperator,provideDelegate,thatisusedtoprovidedelegateduringclassinitialization.ThemainmotivationbehindprovideDelegatewasthatitallowstoprovidecustomizeddelegatedependingontraitsofproperty(name,type,annotations,andsoon).

TheprovideDelegateoperatorreturnsdelegate,andalltypesthathavethisoperatordonotneedtobedelegatesthemselvesinordertobeusedasadelegate.Hereisanexample:

classA(vali:Int){

operatorfunprovideDelegate(

thisRef:Any?,

prop:KProperty<*>

)=object:ReadOnlyProperty<Any?,Int>{

overridefungetValue(

thisRef:Any?,

property:KProperty<*>

)=i

}

}

valabyA(1)

Inthisexample,Aisusedasadelegate,whileitimplementsneithergetvaluenorsetvaluefunction.Thisispossible,becauseitdefinesaprovideDelegateoperator,whichreturnsthedelegatethatwillbeusedinsteadofA.Propertydelegationiscompiledintothefollowingcode:

privatevala$delegate=A().provideDelegate(this,this::prop)

vala:Int

get()=a1$delegate.getValue(this,this::prop)

PracticalexamplecanbefoundinKotlinsupportingpartoflibraryActivityStarter(https://github.com/MarcinMoskala/ActivityStarter).Activityargumentsaredefinedusingannotations,butwecanusepropertydelegationtosimplifyusagefromKotlinandallowpropertiesdefinitionaspossiblyread-onlyandnotlateinit:

@get:Arg(optional=true)valname:StringbyargExtra(defaultName)

@get:Arg(optional=true)valid:IntbyargExtra(defaultId)

@get:Argvalgrade:CharbyargExtra()

@get:Argvalpassing:BooleanbyargExtra()

Page 441: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Buttherearesomerequirements:

WhenargExtraisused,propertygetterhavetobeannotatedWeneedtospecifydefaultvalueifargumentisoptionalandtypeisnotnullable.

Tocheckthisrequirements,weneedreferencetopropertytogetgetterannotation.WecannothavesuchreferenceintheargExtrafunction,butwecanimplementtheminsideprovideDevegate:

fun<T>Activity.argExtra(default:T?=null)=ArgValueDelegateProvider(default)

fun<T>Fragment.argExtra(default:T?=null)=ArgValueDelegateProvider(default)

fun<T>android.support.v4.app.Fragment.argExtra(default:T?=null)=

ValueDelegateProvider(default)

classArgValueDelegateProvider<T>(valdefault:T?=null){

operatorfunprovideDelegate(

thisRef:Any?,

prop:KProperty<*>

):ReadWriteProperty<Any,T>{

valannotation=prop.getter.findAnnotation<Arg>()

when{

annotation==null->

throwError(ErrorMessages.noAnnotation)

annotation.optional&&!prop.returnType.isMarkedNullable&&

default==null->

throwError(ErrorMessages.optionalValueNeeded)

}

returnArgValueDelegate(default)

}

}

internalobjectErrorMessages{

constvalnoAnnotation=

"ElementgettermustbeannotatedwithArg"

constvaloptionalValueNeeded=

"Argumentsthatareoptionalandhavenot-

nullabletypemusthavedefautvaluespecified"

}

Suchdelegateisthrowingappropriateerrorwhenconditionisnotfulfilled:

vala:A?byArgValueDelegateProvider()

//Throwserrorduringinitialization:ElementgettermustbeannotatedwithArg

@get:Arg(optional=true)vala:AbyArgValueDelegateProvider()throwserrorduringinitialization:Argumentsthatareoptionalandhavenot-nullabletypemusthavedefaultvaluespecified.

Thiswayunacceptableargumentdefinitionsarethrowingappropriateerrorsduringobjectinitializationinsteadofbreakingapplicationinunexpected

Page 442: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

situations.

Page 443: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

SummaryInthischapter,wedescribedclassdelegate,propertydelegates,andhowtheycanbeusedtoremoveredundancyincode.Wedefinedadelegateasanobjecttowhichcallsfromotherobjectorpropertyaredelegatedto.WelearneddesignpatternsthatclassdelegationisstronglyconnectedtoDelegatepatternandDecoratorpattern.

Delegationpatternismentionedasanalternativetoinheritance,andDecoratorpatternisawaytoaddfunctionalitytodifferentkindsofclassesthatareimplementingthesameinterface.We'veseenhowpropertydelegationworks,andKotlinstandardlibrarypropertydelegates:notNull,lazy,observable,vetoable,andtheusageofMapasadelegate.Welearnedhowtheyworkandwhentheyshouldbeused.We'vealsoseenhowtomakeacustompropertydelegate,togetherwithexamplesofreal-lifeusage.

Knowledgeaboutdifferentfeaturesandtheirusageisnotenough--thereisalsoaneedtounderstandhowtheycanbeusedtogethertobuildgreatapplications.Inthenextchapter,wewillwriteademoapplicationandexplainhowthevariousKotlinfeaturesdescribedthroughoutthisbookcanbecombinedtogether.

Page 444: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

MakingYourMarvelGalleryApplicationWe'vealreadyseenthemostimportantKotlinfeaturesthatallowustomakeAndroiddevelopmenteasierandmoreproductive,butitishardtounderstandthewholepicturejustbylookingatthepieces.Thisiswhy,inthischapter,wewillbuildawholeAndroidapplicationwritteninKotlin.

Itwasatoughdecisiontochoosewhatapplicationshouldbeimplementedinthischapter.Ithastobeshortandsimple,butatthesametimeitshouldutilizeasmanyKotlinfeaturesaspossible.Atthesametime,wewantedtominimizethenumberofusedlibraries,becauseitisabookaboutAndroiddevelopmentinKotlin,notaboutAndroidlibraries.Wewantedtomakeitlookasgoodaspossible,butwealsowantedtoavoidimplementationofcustomgraphicelements,becausetheyareusuallycomplexanddonotreallyprovidebenefitsfromaKotlinperspective.

WehavefinallydecidedtomakeaMarvelGalleryapplication--asmallappwhichwecanusetofindourfavoriteMarvelcharactersanddisplaytheirdetails.AlldataisprovidedfromtheMarvelwebsitebytheirAPI.

Page 445: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

MarvelGalleryLet'simplementourMarvelGalleryapplication.Thisapplicationshouldallowthefollowingusecases:

Afterstartingtheapplication,theusercanseeagalleryofcharacters.Afterstartingtheapplication,theusercansearchforacharacterbyitsname.Whentheuserclicksonacharacterpicture,thereisaprofiledisplayed.Thecharacterprofilecontainscharactername,photo,description,anditsoccurrences.

Thesearethreeuse-casesthatdescribethemainfunctionalitiesoftheapplication.Inthenextsections,wearegoingtoimplementthemoneafteranother.Ifyouarelostduringthischapter,rememberthatyoucanalwaystakealookatthecompleteapplicationonGitHub(https://github.com/MarcinMoskala/MarvelGallery).

Tounderstandbetterwhatwewanttobuild,let'slookatsomescreenshotsfromthefinalversionofourapplication:

Page 446: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava
Page 447: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

HowtousethischapterThischaptershowsallstepsandcodenecessarytobuildanapplication.Itspurposeistoshowthestep-by-stepprocessofthisapplicationdevelopment.Whenyouarereadingthischapter,concentrateonthedevelopmentprocessandtrytounderstandwhatthepurposeofpresentedcodeis.Youdon'tneedtofullyunderstandlayoutsandyoudon'thavetounderstandunittestdefinitionsaslongasyouunderstandwhattheyaredoing.ConcentrateonapplicationstructureandKotlinsolutionsthataremakingthefinalcodesimpler.Mostsolutionswerealreadydescribedinpreviouschapters,sotheyhaveonlyabriefdescription.Thevalueinthischapteristhattheirusageispresentedinthecontextofaconcreteapplication.

YoucandownloadtheapplicationcodefromGitHub(https://github.com/MarcinMoskala/MarvelGallery).

OnGitHub,youcanseethefinalcode,downloadit,oryoucancloneittoyourcomputerusingGit:

[email protected]:MarcinMoskala/MarvelGallery.git

TheapplicationalsoincludesUItestswritteninEspresso,buttheyarenotpresentedonthischaptertomakeitsimplerforreaderswhoarenotproficientinEspressousage.

EachsectionofthischapterhasacorrespondingGitbranchonthisproject,soifyouwanttoseehowthecodelooksattheendofthesectionthenyoucanjustswitchtothecorrespondingbranch:

Page 448: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Also,locally,afteryouclonetherepository,youcancheckoutthecorrespondingbranchusingthefollowingGitcommand:

gitcheckoutCharacter_search

Ifyouhaveanelectronicversionofthisbookandyouwanttomakethewholeapplicationbycopyandpastingpartsofthecodethenyoucandoit,butremembertoplacefilesinthefolderscorrespondingtothepackage.Thisway,youwillkeepacleanstructureintheproject.

Notethatifyouplacecodefromthebookinanotherfolder,therewillbeawarningdisplayed:

Youcanintentionallyplaceafileinanyfolder,becausethesecondfixpropositionistomovethefileintothepathcorrespondingtothedefinedpackage:

Page 449: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Youcanuseittomoveyourfileintothecorrectlocation.

Page 450: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

MakeanemptyprojectBeforewecanstartimplementingfunctionalities,weneedtocreateanemptyKotlinAndroidprojectwithasingleactivity,MainActivty.ThisprocesswasdescribedinChapter1,BeginningyourKotlinAdventure.Therefore,wedon'tneedtodescribeitdeeply,butwewillshowwhatthestepsareinAndroidStudio3.0:

1. Setname,package,andlocationforthenewproject.RemembertotickIncludeKotlinsupportoption:.

2. WecanchooseotherminimalAndroidversion,butinthisexample,wearegoingtosetAPI16:

Page 451: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

3. Chooseatemplate.Wedon'tneedanyofthesetemplatessoweshouldstartfromEmptyActivity:

Page 452: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

4. Namenewlycreatedactivity.WecankeepthefirstviewnamedMainActivity:

Page 453: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ForAndroidStudiopriorto3.x,weneedtofollowslightlydifferentsteps:

CreateaprojectfromtemplatewithemptyActivity.

1.ConfigureKotlinintheproject(forexample,Ctrl/Cmd+Shift+AandConfigureKotlininproject).2.ConvertallJavaclassestoKotlin(forexample,inMainActivityCtrl/Cmd+Shift+AandConvertJavafiletoKotlinfile).

Afterthesesteps,wewillhaveaKotlinAndroidapplicationwithanemptyActivitycreated:

Page 454: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava
Page 455: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

CharactergalleryInthissection,wewillimplementasingleusecase--afterstartingtheapplication,theusercanseeagalleryofcharacters.

Thisisaprettycomplexusecasebecauseitrequiresviewtobepresented,networkconnectionwithAPIandbusinessrulesimplementation.Therefore,wewillsplititintothefollowingtasks:

ViewimplementationCommunicationwithAPIBusinesslogicimplementationofcharacterdisplayPuttingitalltogether

Suchtasksaremucheasiertoimplement.Let'simplementthemoneafteranother.

Page 456: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ViewimplementationLet'sstartwithViewimplementation.Here,wearegoingtodefinewhatthelistofcharacterswilllooklike.Fortestingpurposes,wearealsogoingtodefineafewcharactersanddisplaythem.

Let'sstartwithMainActivitylayoutimplementation.WewilluseRecyclerViewtoshowalistofelements.TheRecyclerViewlayoutisdistributedinaseparatedependency,whichwehavetoaddtotheappmodulebuild.gradlefile:

implementation"com.android.support:recyclerview-v7:$android_support_version"

Theandroid_support_versioninstanceisavariablewhichisnotyetdefined.ThereasonbehinditisthattheversionshouldbethesameforallAndroidsupportlibrariesandwhenweextractthisversionnumberasaseparatorvariablethenitiseasiertomanage.ThisiswhyweshouldreplacethehardcodedversionforeachoftheAndroidsupportlibrarieswithareferencetoandroid_support_version:

implementation"com.android.support:appcompat-

v7:$android_support_version"

implementation"com.android.support:design:$android_support_version"

implementation"com.android.support:support-

v4:$android_support_version"

implementation"com.android.support:recyclerview-

v7:$android_support_version"

Andwehavetosetsupportlibraryversionvalue.Goodpracticeistodefineitintheprojectbuild.gradlefileinsidebuildscript,afterthekotlin_versiondefinition:

ext.kotlin_version='1.1.4-2'

ext.android_support_version="26.0.1"

NowwecanstartimplementationofMainActivitylayout.Thisistheeffectthatwewanttoachieve:

Page 457: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

WewillkeepcharacterelementsonRecyclerViewpackedintoSwipeRefreshLayouttoallowswipe-refresh.Also,tofulfillMarvelcopyright,thereneedstobeapresentedlabelthatisinformingthatdataisprovidedbyMarvel.Thelayoutactivity_main(res/layout/activity_main.xml)shouldbereplacedwithfollowingdefinition:

<?xmlversion="1.0"encoding="utf-8"?>

<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto"

xmlns:tools="http://schemas.android.com/tools"

android:id="@+id/charactersView"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:background="@android:color/white"

android:fitsSystemWindows="true">

<android.support.v4.widget.SwipeRefreshLayout

xmlns:android="http://schemas.android.com/apk/res/android"

android:id="@+id/swipeRefreshView"

android:layout_width="match_parent"

android:layout_height="match_parent">

<android.support.v7.widget.RecyclerView

android:id="@+id/recyclerView"

android:layout_width="match_parent"

Page 458: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

android:layout_height="match_parent"

android:scrollbars="vertical"/>

</android.support.v4.widget.SwipeRefreshLayout>

<TextView

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_alignParentBottom="true"

android:background="@android:color/white"

android:gravity="center"

android:text="@string/marvel_copyright_notice"/>

</RelativeLayout>

Weneedtoaddacopyrightnoticetostrings(res/values/strings.xml):

<stringname="marvel_copyright_notice">

DataprovidedbyMarvel.©2017MARVEL

</string>

Hereisapreview:

Thenextstepistodefinetheitemview.Wewouldlikeeachelementtobealwayssquare.Todothis,weneedtodefineaviewwhichwillpreservethesquareshape(placeitinview/views):

Page 459: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

packagecom.sample.marvelgallery.view.views

importandroid.util.AttributeSet

importandroid.widget.FrameLayout

importandroid.content.Context

classSquareFrameLayout@JvmOverloadsconstructor(//1

context:Context,

attrs:AttributeSet?=null,

defStyleAttr:Int=0

):FrameLayout(context,attrs,defStyleAttr){

overridefunonMeasure(widthMeasureSpec:Int,

heightMeasureSpec:Int){

super.onMeasure(widthMeasureSpec,widthMeasureSpec)//2

}

}

1. UsingJvmOverloadsannotation,we'veavoidedtelescopingconstructorsthatarenormallyusedtodefineacustomviewinAndroid.ThiswasdescribedinChapter4,ClassesandObjects.

2. Weareforcingtheelementtohavealwaysthesameheightaswidth.

WithSquareFrameLayout,wecandefinethelayoutofgalleryitems.Thisiswhatwewantittolooklike:

WeneedtodefineImageViewtodisplaythecharacterimage,andTextViewtodisplayitsname.WhileSquareFrameLayoutisactuallyFrameLayoutthathasfixedheight,itschildrenelements(imageandtext)arebydefaultplacedoneaboveanother.Let'saddlayoutintoitem_character.xmlfileinres/layout:

//./res/layout/item_character.xml

<com.sample.marvelgallery.view.views.SquareFrameLayoutxmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="wrap_content"

Page 460: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

android:gravity="center_horizontal"

android:orientation="horizontal"

android:padding="@dimen/element_padding">

<ImageView

android:id="@+id/imageView"

android:layout_width="match_parent"

android:layout_height="match_parent"/>

<TextView

android:id="@+id/textView"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:gravity="center"

android:paddingLeft="10dp"

android:paddingRight="10dp"

android:shadowColor="#111"

android:shadowDx="5"

android:shadowDy="5"

android:shadowRadius="0.01"

android:textColor="@android:color/white"

android:textSize="@dimen/standard_text_size"

tools:text="Somename"/>

</com.sample.marvelgallery.view.views.SquareFrameLayout>

Notethatwearealsousingvaluessuchaselement_paddingdefinedindimens.Let'saddthemtothedimen.xmlfileinres/values:

<?xmlversion="1.0"encoding="utf-8"?>

<resources>

<dimenname="character_header_height">240dp</dimen>

<dimenname="standard_text_size">20sp</dimen>

<dimenname="character_description_padding">10dp</dimen>

<dimenname="element_padding">10dp</dimen>

</resources>

Aswecansee,eachelementneedstodisplaythenameofthecharacteranditsimage.Therefore,themodelofacharacterneedstocontainthesetwoproperties.Let'sdefineasimplemodelforacharacter:

packagecom.sample.marvelgallery.model

dataclassMarvelCharacter(

valname:String,

valimageUrl:String

)

TodisplayalistofelementsusingRecyclerView,weneedtoimplementbothaRecyclerViewlistandanitemadapter.Alistadapterisusedtomanageallelementsinalist,whileanitemadapterisanadapterforasingleitemtype.Here,weneedonlyoneitemadapter,becausewedisplayasingletypeofitems.Itis,however,goodpracticetoassumethatinfuturetheremightbeotherkindofelementsonthislist,forexample,comicsorads.Thesamewiththelistadapter--weneed

Page 461: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

onlyoneinthisexample,butinmostprojectsthereismorethanasinglelistanditisbettertoextractcommonbehaviorintoasingleabstractclass.

WhilethisexampleisdesignedtopresenthowKotlincanbeusedinlargerprojects,wewilldefineanabstractlistadapter,whichwewillnameRecyclerListAdapter,andanabstractitemadapter,whichwewillnameItemAdapter.HereistheItemAdapterdefinition:

packagecom.sample.marvelgallery.view.common

importandroid.support.v7.widget.RecyclerView

importandroid.support.annotation.LayoutRes

importandroid.view.View

abstractclassItemAdapter<T:RecyclerView.ViewHolder>

(@LayoutResopenvallayoutId:Int){//1

abstractfunonCreateViewHolder(itemView:View):T//2

@Suppress("UNCHECKED_CAST")//1

funbindViewHolder(holder:RecyclerView.ViewHolder){

(holderasT).onBindViewHolder()//1

}

abstractfunT.onBindViewHolder()//1,3

}

1. Weneedtopassaholderasatypeparametertoallowdirectoperationsonitsfields.TheholderiscreatedinonCreateViewHoldersoweknowthatitstypewillbealwaystypeparameterT.Therefore,wecancasttheholdertoTonbindViewHolderanduseitasareceiverobjectforonBindViewHolder.Suppression@Suppress("UNCHECKED_CAST")isherejusttohidethewarningwhileweknowthatwecansecurelycastinthissituation.

2. Functionusedtocreateviewholder.Inmostcases,itwillbeasingleexpressionfunctionthatisjustcallingaconstructor.

3. IntheonBin+dViewHolderfunction,wewillsetallvaluesonitemview.

HereisthedefinitionofRecyclerListAdapter:

packagecom.sample.marvelgallery.view.common

importandroid.support.v7.widget.RecyclerView

importandroid.view.LayoutInflater

importandroid.view.ViewGroup

openclassRecyclerListAdapter(//1

varitemsList<AnyItemAdapter>=listOf()

):RecyclerView.Adapter<RecyclerView.ViewHolder>(){

overridefinalfungetItemCount()=items.size//4

Page 462: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

overridefinalfungetItemViewType(position:Int)=

items[position].layoutId//3,4

overridefinalfunonCreateViewHolder(parent:ViewGroup,

layoutId:Int):RecyclerView.ViewHolder{//4

valitemView=LayoutInflater.from(parent.context)

.inflate(layoutId,parent,false)

returnitems.first

{it.layoutId==layoutId}.onCreateViewHolder(itemView)//3

}

overridefinalfunonBindViewHolder

(holder:RecyclerView.ViewHolder,position:Int){//4

items[position].bindViewHolder(holder)

}

}

typealiasAnyItemAdapter=ItemAdapter

<outRecyclerView.ViewHolder>//5

1. Classisopeninsteadofabstractbecauseitcanbeinitializedandusedwithoutanychildren.Wedefinechildrentoallowustodefinecustommethodsfordifferentlists.

2. Wekeepitemsinlist.3. Wewilluselayouttodistinguishitemtype.Becauseofit,wecannotuse

twoitemadapterswiththesamelayoutonthesamelist,butthissolutionissimplifyingalot.

4. MethodsareoverridingmethodsofRecyclerView.Adapter,buttheyalsousefinalmodifiertorestricttheiroverrideinchildren.AlllistadaptersthatareextendingRecyclerListAdaptershouldoperateonitems.

5. WedefinetypealiastosimplifythedefinitionofanyItemAdapter.

Usingtheprecedingdefinitions,wecandefineMainListAdapter(adapterforcharacterlist)andCharacterItemAdapter(adapterforitemonlist).HereisthedefinitionofMainListAdapter:

packagecom.sample.marvelgallery.view.main

importcom.sample.marvelgallery.view.common.AnyItemAdapter

importcom.sample.marvelgallery.view.common.RecyclerListAdapter

classMainListAdapter(items:List<AnyItemAdapter>):RecyclerListAdapter(items)

Inthisproject,wedonotneedanyspecialmethodsdefinedinMainListAdapter,buttoshowhoweasyitistodefinethem,hereispresentedMainListAdapterwithadditionalmethodstoaddanddelete:

Page 463: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

classMainListAdapter(items:List<AnyItemAdapter>):RecyclerListAdapter(items){

funadd(itemAdapter:AnyItemAdapter){

items+=itemAdapter)

valindex=items.indexOf(itemAdapter)

if(index==-1)return

notifyItemInserted(index)

}

fundelete(itemAdapter:AnyItemAdapter){

valindex=items.indexOf(itemAdapter)

if(index==-1)return

items-=itemAdapter

notifyItemRemoved(index)

}

}

HereisthedefinitionofCharacterItemAdapter:

packagecom.sample.marvelgallery.view.main

importandroid.support.v7.widget.RecyclerView

importandroid.view.View

importandroid.widget.ImageView

importandroid.widget.TextView

importcom.sample.marvelgallery.R

importcom.sample.marvelgallery.model.MarvelCharacter

importcom.sample.marvelgallery.view.common.ItemAdapter

importcom.sample.marvelgallery.view.common.bindView

importcom.sample.marvelgallery.view.common.loadImage

classCharacterItemAdapter(

valcharacter:MarvelCharacter//1

):ItemAdapter<CharacterItemAdapter.ViewHolder>(R.layout.item_character){

overridefunonCreateViewHolder(itemView:View)=ViewHolder(itemView)

overridefunViewHolder.onBindViewHolder(){//2

textView.text=character.name

imageView.loadImage(character.imageUrl)//3

}

classViewHolder(itemView:View):RecyclerView.ViewHolder(itemView)

{

valtextViewbybindView<TextView>(R.id.textView)//4

valimageViewbybindView<ImageView>(R.id.imageView)//4

}

}

1. MarvelCharacterispassedbyconstructor.2. onBindViewHoldermethodisusedsetupviews.Itwasdefinedasanabstract

memberextensionfunctioninItemAdapterand,thankstothat,nowwecanusetextViewandimageViewexplicitlyinsideitsbody.

3. FunctionloadImageisnotdefinedyet.Wewilldefineitasanextensionfunctionabitlater.

4. Inviewholder,wearebindingpropertiestoviewelementsusingthe

Page 464: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

bindViewfunctionthatwillsoonbedefined.

Inside,weusethefunctionsloadImageandbindViewwhicharenotyetdefined.bindViewisatop-levelextensionfunctiontoRecyclerView.ViewHolder,whichisprovidingalazydelegatethatisprovidingaviewfoundbyitsID:

//ViewExt.kt

packagecom.sample.marvelgallery.view.common

importandroid.support.v7.widget.RecyclerView

importandroid.view.View

fun<T:View>RecyclerView.ViewHolder.bindView(viewId:Int)

=lazy{itemView.findViewById<T>(viewId)}

WealsoneedtodefinetheloadImageextensionfunctionthatwillhelpustodownloadanimagefromtheURLandplaceitintoImageView.TwotypicallibrariesusedtosuchpurposearePicassoandGlide.WewilluseGlide,andtodoit,weneedtoaddadependencyinbuild.gradle:

implementation"com.android.support:recyclerview-

v7:$android_support_version"

implementation"com.github.bumptech.glide:glide:$glide_version"

Specifytheversioninprojectbuild.gradle:

ext.android_support_version="26.0.0"

ext.glide_version="3.8.0"

AddpermissiontousetheinternetinAndroidManifest:

<manifestxmlns:android="http://schemas.android.com/apk/res/android"

package="com.sample.marvelgallery">

<uses-permissionandroid:name="android.permission.INTERNET"/>

<application

...

AndwecanfinallydefinetheloadImageextensionfunctionfortheImaveViewclass:

//ViewExt.kt

packagecom.sample.marvelgallery.view.common

importandroid.support.v7.widget.RecyclerView

importandroid.view.View

importandroid.widget.ImageView

importcom.bumptech.glide.Glide

fun<T:View>RecyclerView.ViewHolder.bindView(viewId:Int)

=lazy{itemView.findViewById<T>(viewId)}

funImageView.loadImage(photoUrl:String){

Glide.with(context)

Page 465: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

.load(photoUrl)

.into(this)

}

Itistimetodefinetheactivitythatwilldisplaythislist.Wewilluseonemoreelement,theKotlinAndroidextensionsplugin.Itisusedtosimplifyaccesstoviewelementsfromcode.Itsusageissimple--weaddthekotlin-android-extensionsplugininmodulebuild.gradle:

applyplugin:'com.android.application'

applyplugin:'kotlin-android'

applyplugin:'kotlin-android-extensions'

Andwehavesomeviewdefinedinlayout:

<TextView

android:id="@+id/nameView"

android:layout_width="wrap_content"

android:layout_height="wrap_content"/>

ThenwecanimportareferencetothisviewinsideActivity:

importkotlinx.android.synthetic.main.activity_main.*

AndwecanaccessViewelementsdirectlyusingitsnamewithoutusingthefindViewByIdmethodordefineannotations:

nameView.text="Somename"

WewilluseKotlinAndroidextensionsinallactivitiesintheproject.Nowlet'sdefineMainActivitytodisplayalistofcharacterswithimages:

packagecom.sample.marvelgallery.view.main

importandroid.os.Bundle

importandroid.support.v7.app.AppCompatActivity

importandroid.support.v7.widget.GridLayoutManager

importandroid.view.Window

importcom.sample.marvelgallery.R

importcom.sample.marvelgallery.model.MarvelCharacter

importkotlinx.android.synthetic.main.activity_main.*

classMainActivity:AppCompatActivity(){

privatevalcharacters=listOf(//1

MarvelCharacter(name="3-DMan",imageUrl="http://i.annihil.us/u/prod/marvel/i/mg/c/e0/535fecbbb9784.jpg"),

MarvelCharacter(name="Abomination(EmilBlonsky)",imageUrl="http://i.annihil.us/u/prod/marvel/i/mg/9/50/4ce18691cbf04.jpg")

)

overridefunonCreate(savedInstanceState:Bundle?){

super.onCreate(savedInstanceState)

requestWindowFeature(Window.FEATURE_NO_TITLE)//2

setContentView(R.layout.activity_main)

recyclerView.layoutManager=GridLayoutManager(this,2)//3

Page 466: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

valcategoryItemAdapters=characters

.map(::CharacterItemAdapter)//4

recyclerView.adapter=MainListAdapter(categoryItemAdapters)

}

}

1. Herewedefineatemporarylistofcharacterstodisplay.2. Weusethiswindowfeaturebecausewedon'twanttodisplayatitle.3. WeuseGridLayoutManagerasRecyclerViewlayoutmanagertoachieveagrid

effect.4. WearecreatingitemadaptersfromcharactersusingtheCharacterItemAdapter

constructorreference.

Nowwecancompiletheprojectandwewillseethefollowingscreen:

Page 467: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

NetworkdefinitionUntilnow,thepresenteddatawashardcodedinsidetheapplication,butwewanttousedatafromtheMarvelAPIinstead.Todoit,weneedtodefinesomenetworkmechanismsthatwillretrievethedatafromtheserver.WearegoingtouseRetrofit,apopularAndroidlibraryusedtosimplifynetworkoperations,togetherwithRxJava,apopularlibraryusedforreactiveprogramming.Forbothlibraries,wewilluseonlybasicfunctionalitiestomaketheirusageassimpleaspossible.Tousethem,weneedtoaddfollowingdependenciesinthemodulebuild.gradle:

dependencies{

implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:

$kotlin_version"

implementation"com.android.support:appcompat-v7:

$android_support_version"

implementation"com.android.support:recyclerview-v7:

$android_support_version"

implementation"com.github.bumptech.glide:glide:$glide_version"

//RxJava

implementation"io.reactivex.rxjava2:rxjava:$rxjava_version"

//RxAndroid

implementation"io.reactivex.rxjava2:rxandroid:$rxandroid_version"

//Retrofit

implementation(["com.squareup.retrofit2:retrofit:$retrofit_version",

"com.squareup.retrofit2:adapter-

rxjava2:$retrofit_version",

"com.squareup.retrofit2:converter-

gson:$retrofit_version",

"com.squareup.okhttp3:okhttp:$okhttp_version",

"com.squareup.okhttp3:logging-

interceptor:$okhttp_version"])

testImplementation'junit:junit:4.12'

androidTestImplementation

'com.android.support.test:runner:1.0.0'

androidTestImplementation

'com.android.support.test.espresso:espresso-core:3.0.0'

}

Andversiondefinitionsinprojectbuild.gradle:

ext.kotlin_version='1.1.3-2'

ext.android_support_version="26.0.0"

ext.glide_version="3.8.0"

ext.retrofit_version='2.2.0'

ext.okhttp_version='3.6.0'

ext.rxjava_version="2.1.2"

Page 468: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ext.rxandroid_version='2.0.1'

WealreadyhaveinternetpermissiondefinedonAndroidManifest,sowedon'tneedtoaddit.AsimpleRetrofitdefinitionmightlooklikethefollowing:

valretrofitbylazy{makeRetrofit()}//1

privatefunmakeRetrofit():Retrofit=Retrofit.Builder()

.baseUrl("http://gateway.marvel.com/v1/public/")//2

.build()

1. Wecankeepretrofitinstanceaslazytop-levelproperty.2. HerewedefinethebaseUrl

ButtherearesomeadditionalrequirementsonRetrofitthatneedtobematched.WeneedtoaddconverterstouseRetrofittogetherwithRxJava,andtosendobjectsserializedasJSON.WealsoneedinterceptorsthatwillbeusedtoprovideheadersandextraqueriesneededbyMarvelAPI.Thisisasmallapplication,sowecandefineallrequiredelementsastop-levelfunctions.ThefullRetrofitdefinitionwillbethefollowing:

//Retrofit.kt

packagecom.sample.marvelgallery.data.network.provider

importcom.google.gson.Gson

importokhttp3.OkHttpClient

importretrofit2.Retrofit

importretrofit2.adapter.rxjava2.RxJava2CallAdapterFactory

importretrofit2.converter.gson.GsonConverterFactory

importjava.util.concurrent.TimeUnit

valretrofitbylazy{makeRetrofit()}

privatefunmakeRetrofit():Retrofit=Retrofit.Builder()

.baseUrl("http://gateway.marvel.com/v1/public/")

.client(makeHttpClient())

.addConverterFactory(GsonConverterFactory.create(Gson()))//1

.addCallAdapterFactory(RxJava2CallAdapterFactory.create())//2

.build()

privatefunmakeHttpClient()=OkHttpClient.Builder()

.connectTimeout(60,TimeUnit.SECONDS)//3

.readTimeout(60,TimeUnit.SECONDS)//4

.addInterceptor(makeHeadersInterceptor())//5

.addInterceptor(makeAddSecurityQueryInterceptor())//6

.addInterceptor(makeLoggingInterceptor())//7

.build()

1. AddaconverterthatallowsobjectJSONserializationanddeserializationusingGSONlibrary.

2. AddaconverterthatwillallowRxJava2types(Observable,Single)as

Page 469: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

observablesforreturnedvaluesfromnetworkrequests.3. Weaddcustominterceptors.Weneedtodefineallofthem.

Let'sdefinetheneededinterceptors.makeHeadersInterceptorisusedtoaddstandardheadersforeachrequest:

//HeadersInterceptor.kt

packagecom.sample.marvelgallery.data.network.provider

importokhttp3.Interceptor

funmakeHeadersInterceptor()=Interceptor{chain->//1

chain.proceed(chain.request().newBuilder()

.addHeader("Accept","application/json")

.addHeader("Accept-Language","en")

.addHeader("Content-Type","application/json")

.build())

}

1. InterceptorisSAM,sowecandefineitusingaSAMconstructor.

ThemakeLoggingInterceptorfunctionisusedtodisplaylogsonconsolewhenwearerunningtheapplicationindebugmode:

//LoggingInterceptor.kt

packagecom.sample.marvelgallery.data.network.provider

importcom.sample.marvelgallery.BuildConfig

importokhttp3.logging.HttpLoggingInterceptor

funmakeLoggingInterceptor()=HttpLoggingInterceptor().apply{

level=if(BuildConfig.DEBUG)HttpLoggingInterceptor.Level.BODY

elseHttpLoggingInterceptor.Level.NONE

}

ThemakeAddRequiredQueryInterceptorfunctionismorecomplex,becauseitisusedtoprovidequeryparametersusedbyMarvelAPItoverifytheuser.TheseparametersneedahashcalculatedusingtheMD5algorithm.ItalsoneedsapublicandprivatekeyfromtheMarvelAPI.Everyonecangeneratetheirownkeysathttps://developer.marvel.com/.Onceyouhavegeneratedkeys,weneedtoplacetheminthegradle.propertiesfile:

org.gradle.jvmargs=-Xmx1536m

marvelPublicKey=REPLEACE_WITH_YOUR_PUBLIC_MARVEL_KEY

marvelPrivateKey=REPLEACE_WITH_YOUR_PRIVATE_MARVEL_KEY

Alsoaddthefollowingdefinitionsinthemodulebuild.gradleinAndroidinthedefaultConfigsection:

Page 470: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

defaultConfig{

applicationId"com.sample.marvelgallery"

minSdkVersion16

targetSdkVersion26

versionCode1

versionName"1.0"

testInstrumentationRunner

"android.support.test.runner.AndroidJUnitRunner"

buildConfigField("String","PUBLIC_KEY","\"${marvelPublicKey}\"")

buildConfigField("String","PRIVATE_KEY","\"${marvelPrivateKey}\"")

}

Afterprojectrebuild,youwillbeabletoaccessthesevaluesbyBuildConfig.PUBLIC_KEYandBuildConfig.PRIVATE_KEY.Usingthesekeys,wecangeneratequeryparametersthatarerequiredbyMarvelAPI:

//QueryInterceptor.kt

packagecom.sample.marvelgallery.data.network.provider

importcom.sample.marvelgallery.BuildConfig

importokhttp3.Interceptor

funmakeAddSecurityQueryInterceptor()=Interceptor{chain->

valoriginalRequest=chain.request()

valtimeStamp=System.currentTimeMillis()

//Urlcustomization:addqueryparameters

valurl=originalRequest.url().newBuilder()

.addQueryParameter("apikey",BuildConfig.PUBLIC_KEY)//1

.addQueryParameter("ts","$timeStamp")//1

.addQueryParameter("hash",calculatedMd5(timeStamp.toString()+BuildConfig.PRIVATE_KEY+BuildConfig.PUBLIC_KEY))//1

.build()

//Requestcustomization:setcustomurl

valrequest=originalRequest

.newBuilder()

.url(url)

.build()

chain.proceed(request)

}

1. Weneedtoprovidethreeadditionalqueries:apikey:Whichisjustincludingourpublickey.ts:Whichisjustcontainingdevicetimeinmilliseconds.Itisusedtoimprovethesecurityofthehashprovidedinthenextquery.hash:WhichiscalculatedasMD5hashfromtimestamp,private,andpublickey,oneafteranotherinasingleString.

HereisthedefinitionofthefunctionusedtocalculatetheMD5hash:

//MD5.kt

packagecom.sample.marvelgallery.data.network.provider

importjava.math.BigInteger

Page 471: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

importjava.security.MessageDigest

/**

*CalculateMD5hashfortext

*@paramtimeStampCurrenttimeStamp

*@returnMD5hashstring

*/

funcalculatedMd5(text:String):String{

valmessageDigest=getMd5Digest(text)

valmd5=BigInteger(1,messageDigest).toString(16)

return"0"*(32-md5.length)+md5//1

}

privatefungetMd5Digest(str:String):ByteArray=MessageDigest.getInstance("MD5").digest(str.toByteArray())

privateoperatorfunString.times(i:Int)=(1..i).fold(""){acc,_->acc+this}

1. Weareusingthetimesextensionoperatortofillthehashwithzerosifitisshorterthan32.

Wehaveinterceptorsdefined,sowecandefineactualAPImethods.TheMarvelAPIcontainsalotofdatamodelsthatarerepresentingcharacters,lists,andsoon.Weneedtodefinethemasseparateclasses.Suchclassesarecalleddatatransferobjects(DTOs).Wewilldefineobjectswewillneed:

packagecom.sample.marvelgallery.data.network.dto

classDataContainer<T>{

varresults:T?=null

}

packagecom.sample.marvelgallery.data.network.dto

classDataWrapper<T>{

vardata:DataContainer<T>?=null

}

packagecom.sample.marvelgallery.data.network.dto

classImageDto{

lateinitvarpath:String//1

lateinitvarextension:String//1

valcompleteImagePath:String

get()="$path.$extension"

}

packagecom.sample.marvelgallery.data.network.dto

classCharacterMarvelDto{

lateinitvarname:String//1

lateinitvarthumbnail:ImageDto//1

valimageUrl:String

get()=thumbnail.completeImagePath

}

Page 472: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

1. Forvaluesthatmightnotbeprovided,weshouldsetadefaultvalue.Valuesthataremandatorymightbeprefixedwithlateinitinstead.

RetrofitisusingreflectiontocreateanHTTPrequestbasingofinterfacedefinition.ThisishowwecanimplementaninterfacethatisdefininganHTTPrequest:

packagecom.sample.marvelgallery.data.network

importcom.sample.marvelgallery.data.network.dto.CharacterMarvelDto

importcom.sample.marvelgallery.data.network.dto.DataWrapper

importio.reactivex.Single

importretrofit2.http.GET

importretrofit2.http.Query

interfaceMarvelApi{

@GET("characters")

fungetCharacters(

@Query("offset")offset:Int?,

@Query("limit")limit:Int?

):Single<DataWrapper<List<CharacterMarvelDto>>>

}

Withsuchdefinitions,wecanfinallygetalistofcharacters:

retrofit.create(MarvelApi::class.java)//1

.getCharacters(0,100)//2

.subscribe({/*code*/})//3

1. WeusearetrofitinstancetocreateanobjectthatwillmakeHTTPrequestsaccordingtotheMarvelApiinterfacedefinition.

2. WecreateobservablereadytosendcalltoAPI.3. Bysubscribe,wesendanHTTPrequestandwestartlisteningforaresponse.

Thefirstargumentisthecallbackthatisinvokedwhenwesuccessfullyreceivearesponse.

Suchanetworkdefinitioncouldbesufficient,butwemightimplementitbetter.ThebiggestproblemisthatwenowneedtooperateonDTOobjectsinsteadofonourowndatamodelobjects.Formapping,weshoulddefineanadditionallayer.Therepositorypatternisusedforthispurpose.Thispatternisalsoreallyhelpfulwhenweareimplementingunittests,becausewecanmocktherepositoryinsteadofthewholeAPIdefinition.Thisisthedefinitionofrepositorythatwewouldliketohave:

packagecom.sample.marvelgallery.data

Page 473: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

importcom.sample.marvelgallery.model.MarvelCharacter

importio.reactivex.Single

interfaceMarvelRepository{

fungetAllCharacters():Single<List<MarvelCharacter>>

}

AndhereistheimplementationofMarvelRepository:

packagecom.sample.marvelgallery.data

importcom.sample.marvelgallery.data.network.MarvelApi

importcom.sample.marvelgallery.data.network.provider.retrofit

importcom.sample.marvelgallery.model.MarvelCharacter

importio.reactivex.Single

classMarvelRepositoryImpl:MarvelRepository{

valapi=retrofit.create(MarvelApi::class.java)

overridefungetAllCharacters():Single<List<MarvelCharacter>>=api.getCharacters(

offset=0,

limit=elementsOnListLimit

).map{

it.data?.results.orEmpty().map(::MarvelCharacter)//1

}

companionobject{

constvalelementsOnListLimit=50

}

}

1. WearegettingalistofDTOelementsandmappingitintoMarvelCharacterusingaconstructorreference.

Tomakeitwork,weneedtodefineanadditionalconstructorinMarvelCharacter,thattakesCharacterMarvelDtoasanargument:

packagecom.sample.marvelgallery.model

importcom.sample.marvelgallery.data.network.dto.CharacterMarvelDto

classMarvelCharacter(

valname:String,

valimageUrl:String

){

constructor(dto:CharacterMarvelDto):this(

name=dto.name,

imageUrl=dto.imageUrl

)

}

TherearedifferentwaystoprovideaninstanceofMarvelRepository.Inmostcommonimplementation,aconcreteinstanceofMarvelRepositoryispassedtoPresenterasconstructorargument.ButwhataboutUItesting(suchasEspresso

Page 474: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

tests)?Wedon'twanttotesttheMarvelAPIandwedon'twanttomakeaUItestdependingonit.Thesolutionistomakeamechanismthatwillgeneratestandardimplementationduringnormalruntime,butitwillalsoallowustosetadifferentimplementationfortestingpurposes.Wewillmakethefollowinggenericimplementationofsuchmechanism(placeitindata):

packagecom.sample.marvelgallery.data

abstractclassProvider<T>{

abstractfuncreator():T

privatevalinstance:Tbylazy{creator()}

vartestingInstance:T?=null

funget():T=testingInstance?:instance

}

InsteadofdefiningourownProvider,wemightusesomeofDependencyInjectionlibraries,suchasDaggerorKodein.DaggerusageforsuchpurposesisreallycommoninAndroiddevelopment,butwe'vedecidedthatwewon'tincludeitinthisexampletoavoidadditionalcomplexityfordeveloperswhoarenotexperiencedwiththislibrary.

WecanmaketheMarvelRepositorycompanionobjectproviderextendaboveclass:

packagecom.sample.marvelgallery.data

importcom.sample.marvelgallery.model.MarvelCharacter

importio.reactivex.Single

interfaceMarvelRepository{

fungetAllCharacters():Single<List<MarvelCharacter>>

companionobject:Provider<MarvelRepository>(){

overridefuncreator()=MarvelRepositoryImpl()

}

}

Thankstotheprecedingdefinition,wecanusetheMarvelRepositorycompanionobjecttogetaninstanceofMarvelRepository:

valmarvelRepository=MarvelRepository.get()

ItwillbealazyinstanceofMarvelRepositoryImpl,untilsomebodysetssomenot-nullvalueofthetestingInstanceproperty:

MarvelRepository.get()//ReturnsinstanceofMarvelRepositoryImpl

Page 475: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

MarvelRepository.testingInstance=object:MarvelRepository{

overridefungetAllCharacters():Single<List<MarvelCharacter>>

=Single.just(emptyList())

}

MarvelRepository.get()//returnsaninstanceofananonymousclassinwhichthereturnedlistisalwaysempty.

SuchaconstructionisusefultoallowUItestsusingespresso.ItsusageforelementoverrideispresentintheprojectandcanbefoundinGitHub.Itisnotpresentedinthissectiontokeepitsimplertounderstandfordeveloperswhoarenotproficientintesting.Ifyouarewillingtoseeit,thenyoucanfinditathttps://github.com/MarcinMoskala/MarvelGallery/blob/master/app/src/androidTest/java/com/sample/marvelgallery/MainActivityTest.kt.

Let'sfinallyconnectthisrepositorywithviewbyimplementationofthebusinesslogicofthecharactergallerydisplay.

Page 476: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

BusinesslogicimplementationWehavebothviewandrepositorypartsimplementedanditistimetofinallyimplementthebusinesslogic.Onthispoint,weneedonlytogetthecharacterlistanddisplayitonthelistwhentheuserentersthescreenorwhentheyrefreshit.WewillextractthesebusinesslogicrulesfromviewimplementationbyusinganarchitecturalpatternknownasModel-View-Presenter(MVP).Herearethesimplifiedrules:

Model:Thisisthelayerresponsibleformanagingdata.Model'sresponsibilitiesincludeusingAPIs,cachingdata,managingdatabases,andsoon.Presenter:Thepresenteristhemiddle-manbetweenModelandView,anditshouldincludeallyourpresentationlogic.Thepresenterisresponsibleforreactingtouserinteractions,usingandupdatingtheModelandtheView.View:ThisisresponsibleforpresentingdataandforwardinguserinteractioneventstothePresenter.

Inourimplementationofthispattern,wewilltreatActivityasaView,andforeachviewweneedtocreateapresenter.Itisgoodpracticetowriteunitteststocheckwhetherbusinesslogicrulesareimplementedcorrectly.Tomakeitsimple,weneedtohideActivitybehindaneasy-to-mockinterfacethatisrepresentingallpossiblePresenterinteractionwithview(Activity).Also,wearegoingtocreateallthedependencies(suchasMarvelRepository)inActivityanddeliverthemtothePresenterviatheconstructorasobjectshiddenbehindinterfaces(forexample,passMarvelRepositoryImplasMarvelRepository).

InPresenter,weneedtoimplementthefollowingbehaviors:

WhenthePresenteriswaitingforaresponse,loadinganimationisdisplayedAftertheViewhasbeencreated,alistofcharactersisloadedanddisplayedAftertherefreshmethodiscalled,alistofcharactersisloadedWhentheAPIreturnsalistofcharacters,itisdisplayedontheviewWhentheAPIreturnsanerror,itisdisplayedontheview

Page 477: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Aswecansee,thePresenterneedstogetbyconstructorbothViewandMarvelRepository,anditshouldspecifythemethodsthatwillbecalledwhentheviewiscreatedortheuserrequestlistisrefreshed:

packagecom.sample.marvelgallery.presenter

importcom.sample.marvelgallery.data.MarvelRepository

importcom.sample.marvelgallery.view.main.MainView

classMainPresenter(valview:MainView,valrepository:MarvelRepository){

funonViewCreated(){

}

funonRefresh(){

}

}

TheViewneedstospecifythemethodsusedtoshowthelistofcharacters,showerrorandshowprogressbarwhenViewisrefreshing(defineitinview/mainandmoveMainActivitytoview/main):

packagecom.sample.marvelgallery.view.main.main

importcom.sample.marvelgallery.model.MarvelCharacter

interfaceMainView{

varrefresh:Boolean

funshow(items:List<MarvelCharacter>)

funshowError(error:Throwable)

}

Beforeaddinglogictoapresenter,let'sdefinefirsttwounittests:

//testsourceset

packagecom.sample.marvelgallery

importcom.sample.marvelgallery.data.MarvelRepository

importcom.sample.marvelgallery.model.MarvelCharacter

importcom.sample.marvelgallery.presenter.MainPresenter

importcom.sample.marvelgallery.view.main.MainView

importio.reactivex.Single

importorg.junit.Assert.assertEquals

importorg.junit.Assert.fail

importorg.junit.Test

@Suppress("IllegalIdentifier")//1

classMainPresenterTest{

@Test

fun`Afterviewwascreated,listofcharactersisloadedanddisplayed`(){

assertOnAction{onViewCreated()}.thereIsSameListDisplayed()

}

@Test

fun`Newlistisshownafterviewwasrefreshed`(){

assertOnAction{onRefresh()}.thereIsSameListDisplayed()

Page 478: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

}

privatefunassertOnAction(action:MainPresenter.()->Unit)

=PresenterActionAssertion(action)

privateclassPresenterActionAssertion

(valactionOnPresenter:MainPresenter.()->Unit){

funthereIsSameListDisplayed(){

//Given

valexampleCharacterList=listOf(//2

MarvelCharacter("ExampleName","ExampleImageUrl"),

MarvelCharacter("Name1","ImageUrl1"),

MarvelCharacter("Name2","ImageUrl2")

)

vardisplayedList:List<MarvelCharacter>?=null

valview=object:MainView{//3

overridevarrefresh:Boolean=false

overridefunshow(items:List<MarvelCharacter>){

displayedList=items//4

}

overridefunshowError(error:Throwable){

fail()//5

}

}

valmarvelRepository=object:MarvelRepository{//3

overridefungetAllCharacters():

Single<List<MarvelCharacter>>

=Single.just(exampleCharacterList)//6

}

valmainPresenter=MainPresenter(view,marvelRepository)

//3

//When

mainPresenter.actionOnPresenter()//7

//Then

assertEquals(exampleCharacterList,displayedList)//8

}

}

}

1. DescriptivenamesareallowedinKotlinunittests,buttherewillbeawarningdisplayed.Thissuppressionisneededtohidethiswarning.

2. Definealistofexamplecharacterstodisplay.3. Defineaviewandrepositoryandcreateapresenterusingthem.4. Whenalistofelementsisshown,thenweshouldsetitasadisplayedlist.5. ThetestisfailingwhenshowErroriscalled.6. ThegetAllCharactersmethodisjustreturninganexamplelist.7. Wecalladefinedactiononthepresenter.8. Wecheckwhetherthelistreturnedbytherepositoryisthesameasthe

Page 479: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

displayedlist.

Tosimplifytheprecedingdefinitions,wecouldextractBaseMarvelRepositoryandBaseMainView,andkeepexampledatainaseparateclass:

//testsourceset

packagecom.sample.marvelgallery.helpers

importcom.sample.marvelgallery.data.MarvelRepository

importcom.sample.marvelgallery.model.MarvelCharacter

importio.reactivex.Single

classBaseMarvelRepository(

valonGetCharacters:()->Single<List<MarvelCharacter>>

):MarvelRepository{

overridefungetAllCharacters()=onGetCharacters()

}

//testsourceset

packagecom.sample.marvelgallery.helpers

importcom.sample.marvelgallery.model.MarvelCharacter

importcom.sample.marvelgallery.view.main.MainView

classBaseMainView(

varonShow:(items:List<MarvelCharacter>)->Unit={},

valonShowError:(error:Throwable)->Unit={},

overridevarrefresh:Boolean=false

):MainView{

overridefunshow(items:List<MarvelCharacter>){

onShow(items)

}

overridefunshowError(error:Throwable){

onShowError(error)

}

}

//testsourceset

packagecom.sample.marvelgallery.helpers

importcom.sample.marvelgallery.model.MarvelCharacter

objectExample{

valexampleCharacter=MarvelCharacter

("ExampleName","ExampleImageUrl")

valexampleCharacterList=listOf(

exampleCharacter,

MarvelCharacter("Name1","ImageUrl1"),

MarvelCharacter("Name2","ImageUrl2")

)

}

NowwecansimplifythedefinitionofPresenterActionAssertion:

packagecom.sample.marvelgallery

Page 480: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

importcom.sample.marvelgallery.helpers.BaseMainView

importcom.sample.marvelgallery.helpers.BaseMarvelRepository

importcom.sample.marvelgallery.helpers.Example

importcom.sample.marvelgallery.model.MarvelCharacter

importcom.sample.marvelgallery.presenter.MainPresenter

importio.reactivex.Single

importorg.junit.Assert.assertEquals

importorg.junit.Assert.fail

importorg.junit.Test

@Suppress("IllegalIdentifier")

classMainPresenterTest{

@Test

fun`Afterviewwascreated,listofcharactersisloadedanddisplayed`(){

assertOnAction{onViewCreated()}.thereIsSameListDisplayed()

}

@Test

fun`Newlistisshownafterviewwasrefreshed`(){

assertOnAction{onRefresh()}.thereIsSameListDisplayed()

}

privatefunassertOnAction(action:MainPresenter.()->Unit)

=PresenterActionAssertion(action)

privateclassPresenterActionAssertion

(valactionOnPresenter:MainPresenter.()->Unit){

funthereIsSameListDisplayed(){

//Given

vardisplayedList:List<MarvelCharacter>?=null

valview=BaseMainView(

onShow={items->displayedList=items},

onShowError={fail()}

)

valmarvelRepository=BaseMarvelRepository(

onGetCharacters=

{Single.just(Example.exampleCharacterList)}

)

valmainPresenter=MainPresenter(view,marvelRepository)

//When

mainPresenter.actionOnPresenter()

//Then

assertEquals(Example.exampleCharacterList,displayedList)

}

}

}

Westartthetests:

Page 481: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Wewillseethattheyarenotpassing:

ThereasonisthatfunctionalitiesarenotimplementedyetinMainPresenter.Thesimplestcodethatissatisfactorytopassthisunittestisthefollowing:

packagecom.sample.marvelgallery.presenter

importcom.sample.marvelgallery.data.MarvelRepository

importcom.sample.marvelgallery.view.main.MainView

classMainPresenter(valview:MainView,valrepository:MarvelRepository){

funonViewCreated(){

loadCharacters()

}

funonRefresh(){

loadCharacters()

}

privatefunloadCharacters(){

repository.getAllCharacters()

.subscribe({items->

view.show(items)

})

}

}

Nowourtestsarepassing:

Buttherearetwoissueswithfollowingimplementation:

Page 482: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Itwon'tworkinAndroid,becausegetAllCharactersisusinganetworkoperationanditcannotrunonthemainthreadasinthisexampleWewillhaveamemoryleakiftheuserlefttheapplicationbeforeloadinghadbeenfinished

Toresolvethefirstissue,weneedtospecifyonwhichthreadswhatoperationsshouldrun.ThenetworkrequestshouldberunningontheI/Othread,andweshouldobserveontheAndroidmainthread(becausewearechangingtheviewincallback):

repository.getAllCharacters()

.subscribeOn(Schedulers.io())//1

.observeOn(AndroidSchedulers.mainThread())//2

.subscribe({items->view.show(items)})

1. WespecifythatthenetworkrequestshouldberunninginIOthread.2. Wespecifythatcallbacksshouldbestartedonthemainthread.

Whilethesearecommonschedulerstoshow,wecanextracttheminatop-levelextensionfunction:

//RxExt.kt

packagecom.sample.marvelgallery.data

importio.reactivex.Single

importio.reactivex.android.schedulers.AndroidSchedulers

importio.reactivex.schedulers.Schedulers

fun<T>Single<T>.applySchedulers():Single<T>=this

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

AnduseitinMainPresenter:

repository.getAllCharacters()

.applySchedulers()

.subscribe({items->view.show(items)})

TestsarenotallowedtoaccesstheAndroidmainthread.Therefore,ourtestswillnotpass.Also,operationsrunningonanewthreadarenotwhatwewantinunittests,becausewewouldhaveproblemassertionssynchronization.Toresolvetheseproblems,weneedoverrideschedulersbeforeunitteststomakeeverythingrunonthesamethread(additinMainPresenterTestclass):

packagecom.sample.marvelgallery

importcom.sample.marvelgallery.helpers.BaseMainView

importcom.sample.marvelgallery.helpers.BaseMarvelRepository

importcom.sample.marvelgallery.helpers.Example

Page 483: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

importcom.sample.marvelgallery.model.MarvelCharacter

importcom.sample.marvelgallery.presenter.MainPresenter

importio.reactivex.Single

importio.reactivex.android.plugins.RxAndroidPlugins

importio.reactivex.plugins.RxJavaPlugins

importio.reactivex.schedulers.Schedulers

importorg.junit.Assert.assertEquals

importorg.junit.Assert.fail

importorg.junit.Before

importorg.junit.Test

@Suppress("IllegalIdentifier")

classMainPresenterTest{

@Before

funsetUp(){

RxAndroidPlugins.setInitMainThreadSchedulerHandler{

Schedulers.trampoline()}

RxJavaPlugins.setIoSchedulerHandler{Schedulers.trampoline()}

RxJavaPlugins.setComputationSchedulerHandler{

Schedulers.trampoline()}

RxJavaPlugins.setNewThreadSchedulerHandler{

Schedulers.trampoline()}

}

@Test

fun`Afterviewwascreated,listofcharactersisloadedand

displayed`(){

assertOnAction{onViewCreated()}.thereIsSameListDisplayed()

}

@Test

fun`Newlistisshownafterviewwasrefreshed`(){

assertOnAction{onRefresh()}.thereIsSameListDisplayed()

}

Nowunittestsarepassingagain:

Anotherproblemismemoryleakiftheuserleavestheapplicationbeforewegetaserverresponse.Acommonsolutionistokeepallsubscriptionsincomposite,anddisposethemallwhentheuserisleavingtheapplication:

privatevarsubscriptions=CompositeDisposable()

funonViewDestroyed(){

subscriptions.dispose()

}

Inbiggerapplications,mostpresentershavesomesubscriptions.Sothe

Page 484: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

functionalityofcollectingsubscriptionsanddisposingthemwhentheuserdestroystheviewcanbetreatedascommonbehaviorandextractedinBasePresenter.Also,tosimplifytheprocess,wecanmakeaBaseActivityWithPresenterclassthatwillholdthepresenterbehindthePresenterinterfaceandcalltheonViewDestroyedmethodwhentheviewisdestroyed.Let'sdefinethismechanisminourapplication.HereisthedefinitionofPresenter:

packagecom.sample.marvelgallery.presenter

interfacePresenter{

funonViewDestroyed()

}

HereisthedefinitionofBasePresenter:

packagecom.sample.marvelgallery.presenter

importio.reactivex.disposables.CompositeDisposable

abstractclassBasePresenter:Presenter{

protectedvarsubscriptions=CompositeDisposable()

overridefunonViewDestroyed(){

subscriptions.dispose()

}

}

HereisthedefinitionofBaseActivityWithPresenter:

packagecom.sample.marvelgallery.view.common

importandroid.support.v7.app.AppCompatActivity

importcom.sample.marvelgallery.presenter.Presenter

abstractclassBaseActivityWithPresenter:AppCompatActivity(){

abstractvalpresenter:Presenter

overridefunonDestroy(){

super.onDestroy()

presenter.onViewDestroyed()

}

}

Tosimplifyhowanewsubscriptionisaddedtosubscriptions,wecandefineaplusassignoperator:

//RxExt.ext

packagecom.sample.marvelgallery.data

importio.reactivex.Single

importio.reactivex.android.schedulers.AndroidSchedulers

importio.reactivex.disposables.CompositeDisposable

Page 485: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

importio.reactivex.disposables.Disposable

importio.reactivex.schedulers.Schedulers

fun<T>Single<T>.applySchedulers():Single<T>=this

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

operatorfunCompositeDisposable.plusAssign(disposable:Disposable){

add(disposable)

}

AndwecanusebothsolutionstomakeMainPresentersecure:

packagecom.sample.marvelgallery.presenter

importcom.sample.marvelgallery.data.MarvelRepository

importcom.sample.marvelgallery.data.applySchedulers

importcom.sample.marvelgallery.data.plusAssign

importcom.sample.marvelgallery.view.main.MainView

classMainPresenter(

valview:MainView,

valrepository:MarvelRepository

):BasePresenter(){

funonViewCreated(){

loadCharacters()

}

funonRefresh(){

loadCharacters()

}

privatefunloadCharacters(){

subscriptions+=repository.getAllCharacters()

.applySchedulers()

.subscribe({items->

view.show(items)

})

}

}

ThefirsttwoMainPresenterbehaviorshavebeenimplemented.Itistimetomoveontothenextone--whentheAPIreturnsanerror,itisdisplayedontheview.WecanaddthisrequirementasatestinMainPresenterTest:

@Test

fun`Newlistisshownafterviewwasrefreshed`(){

assertOnAction{onRefresh()}.thereIsSameListDisplayed()

}

@Test

fun`WhenAPIreturnserror,itisdisplayedonview`(){

//Given

valsomeError=Error()

varerrorDisplayed:Throwable?=null

valview=BaseMainView(

onShow={_->fail()},

onShowError={errorDisplayed=it}

Page 486: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

)

valmarvelRepository=BaseMarvelRepository

{Single.error(someError)}

valmainPresenter=MainPresenter(view,marvelRepository)

//When

mainPresenter.onViewCreated()

//Then

assertEquals(someError,errorDisplayed)

}

privatefunassertOnAction(action:MainPresenter.()->Unit)

=PresenterActionAssertion(action)

AsimplechangethatwillmakethistestpassiserrorhandlerspecificationinthesubscribemethodinMainPresenter:

subscriptions+=repository.getAllCharacters()

.applySchedulers()

.subscribe({items->//onNext

view.show(items)

},{//onError

view.showError(it)

})

WhilesubscribeisJavamethod,wecannotusenamedargumentconvention.Suchinvocationisnotreallydescriptive.ThisiswhywearegoingtodefineintheRxExt.ktcustomsubscribemethodnamedsubscribeBy:

//Ext.kt

fun<T>Single<T>.applySchedulers():Single<T>=this

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

fun<T>Single<T>.subscribeBy(

onError:((Throwable)->Unit)?=null,

onSuccess:(T)->Unit

):Disposable=subscribe(onSuccess,{onError?.invoke(it)})

Andwewilluseitinsteadofsubscribe:

subscriptions+=repository.getAllCharacters()

.applySchedulers()

.subscribeBy(

onSuccess=view::show,

onError=view::showError

)

subscribeByinfullversiondefinedfordifferentRxJavatyped(suchasObservable,Flowable,andsoon)togetherwithlotsofotherusefulKotlinextensionstoRxJavacanbefoundinRxKotlinlibrary(https://github.com/ReactiveX/RxKotlin).

Toshowandhidelistloading,wewilldefineadditionallistenerstoeventsthat

Page 487: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

arealwaysoccurringbeforeandafterprocessing:

subscriptions+=repository.getAllCharacters()

.applySchedulers()

.doOnSubscribe{view.refresh=true},}

onSuccess=view::show,

.doFinally{view.refresh=false}

.subscribeBy(

onSuccess=view::show,

onError=view::showError,

onFinish={view.refresh=false}

)

Andtestsarepassingagain:

Thesubscribemethodisbecominglessandlessreadable,butwewillresolvethisproblemtogetherwithanotherbusinessrule,whosedefinitionisthefollowing--whenthepresenteriswaitingforaresponse,refreshisdisplayed.DefineitsunittestinMainPresenterTest:

packagecom.sample.marvelgallery

importcom.sample.marvelgallery.helpers.BaseMainView

importcom.sample.marvelgallery.helpers.BaseMarvelRepository

importcom.sample.marvelgallery.helpers.Example

importcom.sample.marvelgallery.model.MarvelCharacter

importcom.sample.marvelgallery.presenter.MainPresenter

importio.reactivex.Single

importio.reactivex.android.plugins.RxAndroidPlugins

importio.reactivex.plugins.RxJavaPlugins

importio.reactivex.schedulers.Schedulers

importorg.junit.Assert.*

importorg.junit.Before

importorg.junit.Test

@Suppress("IllegalIdentifier")

classMainPresenterTest{

@Test

fun`Whenpresenteriswaitingforresponse,refreshisdisplayed`()

{

//Given

valview=BaseMainView(refresh=false)

valmarvelRepository=BaseMarvelRepository(

onGetCharacters={

Single.fromCallable{

//Then

assertTrue(view.refresh)//1

Example.exampleCharacterList

Page 488: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

}

}

)

valmainPresenter=MainPresenter(view,marvelRepository)

view.onShow={_->

//Then

assertTrue(view.refresh)//1

}

//When

mainPresenter.onViewCreated()

//Then

assertFalse(view.refresh)//1

}

}

1. Weexpectrefreshdisplayedduringnetworkrequestandwhenelementsareshown,butnotafterprocessingfinishes.

Weexpectrefreshtobedisplayedduringanetworkrequestandwhenelementsareshown,butnotafterprocessingfinishes.

InthepresentedversiononRxJava2,assertionsinsidecallbacksarenotbreakingthetestbutdisplayinganerrorontheexecutionreportinstead:

Probably,infutureversions,itwillbepossibletoaddahandlerthatisallowingtofailatestfrominsideacallback.

Toshowandhidelistloading,wewilldefineadditionallistenerstoeventsthatarealwaysoccurringbeforeandafterprocessing:

subscriptions+=repository.getAllCharacters()

.applySchedulers()

.doOnSubscribe{view.refresh=true}

.doFinally{view.refresh=false}

Page 489: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

.subscribeBy(

onSuccess=view::show,

onError=view::showError

)

Afterthesechanges,alltestsarepassingagain:

Nowwehaveafullyfunctionalpresenter,network,andview.Timetoconnectitallandfinishimplementationofthefirstusecase.

Page 490: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

PuttingitalltogetherWehaveMainPresenterreadytobeusedintheproject.NowweneedtouseitinMainActivity:

packagecom.sample.marvelgallery.view.main

importandroid.os.Bundle

importandroid.support.v7.widget.GridLayoutManager

importandroid.view.Window

importcom.sample.marvelgallery.R

importcom.sample.marvelgallery.data.MarvelRepository

importcom.sample.marvelgallery.model.MarvelCharacter

importcom.sample.marvelgallery.presenter.MainPresenter

importcom.sample.marvelgallery.view.common.BaseActivityWithPresenter

importcom.sample.marvelgallery.view.common.bindToSwipeRefresh

importcom.sample.marvelgallery.view.common.toast

importkotlinx.android.synthetic.main.activity_main.*

classMainActivity:BaseActivityWithPresenter(),MainView{//1

overridevarrefreshbybindToSwipeRefresh(R.id.swipeRefreshView)

//2

overridevalpresenterbylazy

{MainPresenter(this,MarvelRepository.get())}//3

overridefunonCreate(savedInstanceState:Bundle?){

super.onCreate(savedInstanceState)

requestWindowFeature(Window.FEATURE_NO_TITLE)

setContentView(R.layout.activity_main)

recyclerView.layoutManager=GridLayoutManager(this,2)

swipeRefreshView.setOnRefreshListener

{presenter.onRefresh()}//4

presenter.onViewCreated()//4

}

overridefunshow(items:List<MarvelCharacter>){

valcategoryItemAdapters=items.map(::CharacterItemAdapter)

recyclerView.adapter=MainListAdapter(categoryItemAdapters)

}

overridefunshowError(error:Throwable){

toast("Error:${error.message}")//2

error.printStackTrace()

}

}

1. ActivityshouldextendBaseActivityWithPresenterandimplementMainView.2. bindToSwipeRefreshandtoastarenotyetimplemented.3. Wemakepresenterlazily.Thefirstargumentisareferencetoactivity

behindtheMainViewinterface.4. Weneedtopasseventstothepresenterusingitsmethods.

Page 491: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Intheprecedingcode,weusedtwofunctionsthatwerealreadydescribedinthebook,toast,usedtodisplaytoastonthescreen,andbindToSwipeRefresh,usedtobindpropertywithvisibilityofswiperefresh:

//ViewExt.kt

packagecom.sample.marvelgallery.view.common

importandroid.app.Activity

importandroid.content.Context

importandroid.support.annotation.IdRes

importandroid.support.v4.widget.SwipeRefreshLayout

importandroid.support.v7.widget.RecyclerView

importandroid.view.View

importandroid.widget.ImageView

importandroid.widget.Toast

importcom.bumptech.glide.Glide

importkotlin.properties.ReadWriteProperty

importkotlin.reflect.KProperty

fun<T:View>RecyclerView.ViewHolder.bindView(viewId:Int)

=lazy{itemView.findViewById<T>(viewId)}

funImageView.loadImage(photoUrl:String){

Glide.with(context)

.load(photoUrl)

.into(this)

}

funContext.toast(text:String,length:Int=Toast.LENGTH_LONG){

Toast.makeText(this,text,length).show()

}

funActivity.bindToSwipeRefresh(@IdResswipeRefreshLayoutId:Int):ReadWriteProperty<Any?,Boolean>

=SwipeRefreshBinding(lazy{findViewById<SwipeRefreshLayout>(swipeRefreshLayoutId)})

privateclassSwipeRefreshBinding(lazyViewProvider:Lazy<SwipeRefreshLayout>):ReadWriteProperty<Any?,Boolean>{

valviewbylazyViewProvider

overridefungetValue(thisRef:Any?,

property:KProperty<*>):Boolean{

returnview.isRefreshing

}

overridefunsetValue(thisRef:Any?,

property:KProperty<*>,value:Boolean){

view.isRefreshing=value

}

}

Nowourapplicationshouldcorrectlyshowalistofcharacters:

Page 492: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Ourfirstusecasehasbeenimplemented.Wecanmoveontothenextone.

Page 493: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

CharactersearchAnotherbehaviorweneedtoimplementischaractersearch.Hereistheusecasedefinition,afterstartingtheapplication,theusercansearchforacharacterbyitsname.

Toaddit,wearegoingtoaddEditTexttotheactivity_mainlayout:

<?xmlversion="1.0"encoding="utf-8"?>

<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto"

xmlns:tools="http://schemas.android.com/tools"

android:id="@+id/charactersView"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:background="@android:color/white"

android:fitsSystemWindows="true">

<!--DummyitemtopreventEditTextfromreceiving

focusoninitialload-->

<LinearLayout

android:layout_width="0px"

android:layout_height="0px"

android:focusable="true"

android:focusableInTouchMode="true"

tools:ignore="UselessLeaf"/>

<android.support.design.widget.TextInputLayout

android:id="@+id/searchViewLayout"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_margin="@dimen/element_padding">

<EditText

android:id="@+id/searchView"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_centerHorizontal="true"

android:hint="@string/search_hint"/>

</android.support.design.widget.TextInputLayout>

<android.support.v4.widget.SwipeRefreshLayoutxmlns:android="http://schemas.android.com/apk/res/android"

android:id="@+id/swipeRefreshView"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:layout_below="@+id/searchViewLayout"

app:layout_behavior="@string/appbar_scrolling_view_behavior">

<android.support.v7.widget.RecyclerView

android:id="@+id/recyclerView"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:scrollbars="vertical"/>

Page 494: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

</android.support.v4.widget.SwipeRefreshLayout>

<TextView

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_alignParentBottom="true"

android:background="@android:color/white"

android:gravity="center"

android:text="@string/marvel_copyright_notice"/>

</RelativeLayout>

WeneedtoaddAndroidSupportDesignlibrarydependencytoallowTextInputLayoutusage:

implementation"com.android.support:appcompat-v7:$android_support_version"

implementation"com.android.support:design:$android_support_version"

implementation"com.android.support:recyclerview-v7:$android_support_version"

Andstringsearch_hintdefinitioninstrings.xml:

<resources>

<stringname="app_name">MarvelGallery</string>

<stringname="search_hint">Searchforcharacter</string>

<stringname="marvel_copyright_notice">

DataprovidedbyMarvel.©2017MARVEL

</string>

</resources>

Also,tokeepthelabelthatisinformingaboutMarvelcopyrightwhenthekeyboardisopened,wealsoneedtoadjustResizetowindowSoftInputModeinactivitydefinitioninAndroidManifest:

<activity

android:name="com.sample.marvelgallery.view.main.MainActivity"

android:windowSoftInputMode="adjustResize">

<intent-filter>

<actionandroid:name="android.intent.action.MAIN"/>

<categoryandroid:name="android.intent.category.LAUNCHER"/>

</intent-filter>

</activity>

Weshouldseethefollowingpreview:

Page 495: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

NowwehaveasearchfieldaddedinMainActivity:

Thebehaviorweareexpectingisthatwhenevertheuserchangesthetextinthe

Page 496: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

searchfield,anewlistwillbeloaded.WeneedanewmethodinMainPresenter,thatwillbeusedtoinformthepresenterthatthetextwaschanged.WewillcallitonSearchChanged:

funonRefresh(){

loadCharacters()

}

funonSearchChanged(text:String){

//TODO

}

privatefunloadCharacters(){

subscriptions+=repository.getAllCharacters()

.applySchedulers()

.doOnSubscribe{view.refresh=true}

.doFinally{view.refresh=false}

.subscribeBy(

onSuccess=view::show,

onError=view::showError

)

}

}

WeneedtochangetheMarvelRepositorydefinitiontoacceptasearchqueryasgetAllCharactersparameter(remembertoupdatealsoBaseMarvelRepository):

interfaceMarvelRepository{

fungetAllCharacters(searchQuery:String?):

Single<List<MarvelCharacter>>

companionobject:Provider<MarvelRepository>(){

overridefuncreator()=MarvelRepositoryImpl()

}

}

Asaresult,wehavetoupdatetheimplementation:

classMarvelRepositoryImpl:MarvelRepository{

valapi=retrofit.create(MarvelApi::class.java)

overridefungetAllCharacters(searchQuery:String?):

Single<List<MarvelCharacter>>=api.getCharacters(

offset=0,

searchQuery=searchQuery,

limit=elementsOnListLimit

).map{it.data?.results.orEmpty().map(::MarvelCharacter)?:

emptyList()}

companionobject{

constvalelementsOnListLimit=50

}

}

Page 497: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Wealsoupdatethenetworkrequestdefinition:

interfaceMarvelApi{

@GET("characters")

fungetCharacters(

@Query("offset")offset:Int?,

@Query("nameStartsWith")searchQuery:String?,

@Query("limit")limit:Int?

):Single<DataWrapper<List<CharacterMarvelDto>>>

}

Andtoallowcodecompilation,weneedtoprovidenullasagetAllCharactersargumentinMainPresenter:

privatefunloadCharacters(){

subscriptions+=repository.getAllCharacters(null)

.applySchedulers()

.doOnSubscribe{view.refresh=true}

.doFinally{view.refresh=false}

.subscribeBy(

onSuccess=view::show,

onError=view::showError

)

}

}

AndweneedtoupdateBaseMarvelRepository:

packagecom.sample.marvelgallery.helpers

importcom.sample.marvelgallery.data.MarvelRepository

importcom.sample.marvelgallery.model.MarvelCharacter

importio.reactivex.Single

classBaseMarvelRepository(

valonGetCharacters:(String?)->Single<List<MarvelCharacter>>

):MarvelRepository{

overridefungetAllCharacters(searchQuery:String?)

=onGetCharacters(searchQuery)

}

Nowournetworkimplementationisreturningalistofcharactersthatstartsfromaquery,orafilllistifwedon'tspecifyanyquery.Timetoimplementthepresenter.Let'sdefinethefollowingtests:

@file:Suppress("IllegalIdentifier")

packagecom.sample.marvelgallery

importcom.sample.marvelgallery.helpers.BaseMainView

importcom.sample.marvelgallery.helpers.BaseMarvelRepository

importcom.sample.marvelgallery.presenter.MainPresenter

importio.reactivex.Single

importorg.junit.Assert.*

Page 498: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

importorg.junit.Test

classMainPresenterSearchTest{

@Test

fun`Whenviewiscreated,thensearchqueryisnull`(){

assertOnAction{onViewCreated()}searchQueryIsEqualTonull

}

@Test

fun`Whentextischanged,thenwearesearchingfornewquery`(){

for(textinlistOf("KKO","HJHJ","Andsowhat?"))

assertOnAction{onSearchChanged(text)}

searchQueryIsEqualTotext

}

privatefunassertOnAction(action:MainPresenter.()->Unit)

=PresenterActionAssertion(action)

privateclassPresenterActionAssertion(valactionOnPresenter:

MainPresenter.()->Unit){

infixfunsearchQueryIsEqualTo(expectedQuery:String?){

varcheckApplied=false

valview=BaseMainView(onShowError={fail()})

valmarvelRepository=BaseMarvelRepository{searchQuery->

assertEquals(expectedQuery,searchQuery)

checkApplied=true

Single.never()

}

valmainPresenter=MainPresenter(view,marvelRepository)

mainPresenter.actionOnPresenter()

assertTrue(checkApplied)

}

}

}

Tomakefollowingtestpass,weneedtoaddsearchqueryasaparameterwithdefaultargumenttotheloadCharactersmethodofMainPresenter:

funonSearchChanged(text:String){

loadCharacters(text)

}

privatefunloadCharacters(searchQuery:String?=null){

subscriptions+=repository.getAllCharacters(searchQuery)

.applySchedulers()

.doOnSubscribe{view.refresh=true}

.doFinally{view.refresh=false}

.subscribeBy(

onSuccess=view::show,

onError=view::showError

)

}

}

ButthetrickypartisthattheMarvelAPIdoesnotallowonlywhitespacesasansearchquery.Thereshouldbeanullsendinstead.Therefore,iftheuserdeletesthelastcharacteroriftheytrytosearchplaceonlyspaceinsearchfield,thenthe

Page 499: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

applicationwouldcrash.Weshouldpreventsuchsituations.Hereisatestthatischeckingwhetherthepresenterischangingaquerywithonlywhitespacesintonull:

@Test

fun`Whentextischanged,thenwearesearchingfornewquery`(){

for(textinlistOf("KKO","HJHJ","Andsowhat?"))

assertOnAction{onSearchChanged(text)}

searchQueryIsEqualTotext

}

@Test

fun`Forblanktext,thereisrequestwithnullquery`(){

for(emptyTextinlistOf("","",""))

assertOnAction{onSearchChanged(emptyText)}

searchQueryIsEqualTonull

}

privatefunassertOnAction(action:MainPresenter.()->Unit)

=PresenterActionAssertion(action)

WecanimplementasecuritymechanismintheloadCharactersmethod:

privatefunloadCharacters(searchQuery:String?=null){

valqualifiedSearchQuery=if(searchQuery.isNullOrBlank())null

elsesearchQuery

subscriptions+=repository

.getAllCharacters(qualifiedSearchQuery)

.applySchedulers()

.smartSubscribe(

onStart={view.refresh=true},

onSuccess=view::show,

onError=view::showError,

onFinish={view.refresh=false}

)

}

Nowalltestsarepassingagain:

Page 500: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

WestillneedtoimplementanActivityfunctionalitythatwillcallthepresenterwhentexthaschanged.WewilldoitusingtheoptionalcallbackclassdefinedinChapter7,ExtensionFunctionsandProperties:

//TextChangedListener.kt

packagecom.sample.marvelgallery.view.common

importandroid.text.Editable

importandroid.text.TextWatcher

importandroid.widget.TextView

funTextView.addOnTextChangedListener(config:TextWatcherConfiguration.()->Unit){

addTextChangedListener(TextWatcherConfiguration().apply{config()}

addTextChangedListener(textWatcher)

}

classTextWatcherConfiguration:TextWatcher{

privatevarbeforeTextChangedCallback:

(BeforeTextChangedFunction)?=null

privatevaronTextChangedCallback:

(OnTextChangedFunction)?=null

privatevarafterTextChangedCallback:

(AfterTextChangedFunction)?=null

funbeforeTextChanged(callback:BeforeTextChangedFunction){

beforeTextChangedCallback=callback

}

funonTextChanged(callback:OnTextChangedFunction){

onTextChangedCallback=callback

}

funafterTextChanged(callback:AfterTextChangedFunction){

afterTextChangedCallback=callback

}

overridefunbeforeTextChanged(s:CharSequence,

start:Int,count:Int,after:Int){

beforeTextChangedCallback?.invoke(s.toString(),

start,count,after)

}

overridefunonTextChanged(s:CharSequence,start:Int,

before:Int,count:Int){

onTextChangedCallback?.invoke(s.toString(),

start,before,count)

}

overridefunafterTextChanged(s:Editable){

afterTextChangedCallback?.invoke(s)

}

}

privatetypealiasBeforeTextChangedFunction=

(text:String,start:Int,count:Int,after:Int)->Unit

privatetypealiasOnTextChangedFunction=

(text:String,start:Int,before:Int,count:Int)->Unit

privatetypealiasAfterTextChangedFunction=

(s:Editable)->Unit

Page 501: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

AnduseitintheonCreatemethodofMainActivity:

packagecom.sample.marvelgallery.view.main

importandroid.os.Bundle

importandroid.support.v7.widget.GridLayoutManager

importandroid.view.Window

importcom.sample.marvelgallery.R

importcom.sample.marvelgallery.data.MarvelRepository

importcom.sample.marvelgallery.model.MarvelCharacter

importcom.sample.marvelgallery.presenter.MainPresenter

importcom.sample.marvelgallery.view.common.BaseActivityWithPresenter

importcom.sample.marvelgallery.view.common.addOnTextChangedListener

importcom.sample.marvelgallery.view.common.bindToSwipeRefresh

importcom.sample.marvelgallery.view.common.toast

importkotlinx.android.synthetic.main.activity_main.*

classMainActivity:BaseActivityWithPresenter(),MainView{

overridevarrefreshbybindToSwipeRefresh(R.id.swipeRefreshView)

overridevalpresenterbylazy

{MainPresenter(this,MarvelRepository.get())}

overridefunonCreate(savedInstanceState:Bundle?){

super.onCreate(savedInstanceState)

requestWindowFeature(Window.FEATURE_NO_TITLE)

setContentView(R.layout.activity_main)

recyclerView.layoutManager=GridLayoutManager(this,2)

swipeRefreshView.setOnRefreshListener{presenter.onRefresh()}

searchView.addOnTextChangedListener{

onTextChanged{text,_,_,_->

presenter.onSearchChanged(text)

}

}

presenter.onViewCreated()

}

overridefunshow(items:List<MarvelCharacter>){

valcategoryItemAdapters=items.map(::CharacterItemAdapter)

recyclerView.adapter=MainListAdapter(categoryItemAdapters)

}

overridefunshowError(error:Throwable){

toast("Error:${error.message}")

error.printStackTrace()

}

}

Thatisallweneedtodefinethefunctionalityofthecharactersearch.Nowwecanbuildtheapplicationanduseittofindourfavoritecharacter:

Page 502: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Withacorrectlyworkingapplication,wecanmoveontothenextusecase.

Page 503: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

CharacterprofiledisplaySearchingthroughcharactersisnotenough.Tomaketheappfunctional,weshouldaddacharacterdescriptiondisplay.Hereistheusecasewe'vedefined--whentheuserclicksonsomecharacterpicture,thereisaprofiledisplayed.Thecharacterprofilecontainscharactername,photo,description,anditsoccurrences.

Toimplementthisusecase,weneedtocreateanewactivityandlayoutthatwilldefinewhatthisActivitylookslike.Todoit,createanewActivitycalledCharacterProfileActivityinthepackagecom.sample.marvelgallery.view.character:

Wewillstartitsimplementationfromchangesinlayout(inactivity_character_profile.xml).Hereisthefinalresultwewouldliketoachieve:

Page 504: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

ThebaseelementisCoordinatorLayoutwithAppBarandCollapsingToolbarLayoutbothusedtoachieveacollapsingeffectknownfrommaterialdesign:

Collapsingeffectstepbystep.

WealsoneedTextViewfordescriptionandoccurrencesthatwillbefilledwithdatainthenextusecase.Hereisthefullactivity_character_profilelayoutdefinition:

<?xmlversion="1.0"encoding="utf-8"?>

<android.support.design.widget.CoordinatorLayoutxmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto"

xmlns:tools="http://schemas.android.com/tools"

android:id="@+id/character_detail_layout"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:background="@android:color/white">

<android.support.design.widget.AppBarLayout

android:id="@+id/appBarLayout"

Page 505: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:theme="@style/ThemeOverlay.AppCompat.ActionBar">

<android.support.design.widget.CollapsingToolbarLayout

android:id="@+id/toolbarLayout"

android:layout_width="match_parent"

android:layout_height="match_parent"

app:contentScrim="?attr/colorPrimary"

app:expandedTitleTextAppearance="@style/ItemTitleName"

app:layout_scrollFlags="scroll|exitUntilCollapsed">

<android.support.v7.widget.AppCompatImageView

android:id="@+id/headerView"

android:layout_width="match_parent"

android:layout_height="@dimen/character_header_height"

android:background="@color/colorPrimaryDark"

app:layout_collapseMode="parallax"/>

<android.support.v7.widget.Toolbar

android:id="@+id/toolbar"

android:layout_width="match_parent"

android:layout_height="?attr/actionBarSize"

android:background="@android:color/transparent"

app:layout_collapseMode="pin"

app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>

</android.support.design.widget.CollapsingToolbarLayout>

</android.support.design.widget.AppBarLayout>

<android.support.v4.widget.NestedScrollView

android:layout_width="match_parent"

android:layout_height="match_parent"

android:overScrollMode="never"

app:layout_behavior="@string/appbar_scrolling_view_behavior">

<LinearLayout

android:id="@+id/details_content_frame"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:focusableInTouchMode="true"

android:orientation="vertical">

<TextView

android:id="@+id/descriptionView"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:gravity="center"

android:padding="@dimen/character_description_padding"

android:textSize="@dimen/standard_text_size"

tools:text="Thisissomelongtextthatwillbevisibleasancharacterdescription."/>

<TextView

android:id="@+id/occurrencesView"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:padding="@dimen/character_description_padding"

android:textSize="@dimen/standard_text_size"

tools:text="Hewasinfollowingcomics:\n*KOKOKO\n*KOKOKO\n*KOKOKO\n*KOKOKO\n*KOKOKO\n*KOKOKO\n*KOKOKO\n*KOKOKO\n*KOKOKO\n*KOKOKO\n*KOKOKO"/>

</LinearLayout>

</android.support.v4.widget.NestedScrollView>

Page 506: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

<TextView

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_gravity="bottom"

android:background="@android:color/white"

android:gravity="bottom|center"

android:text="@string/marvel_copyright_notice"/>

<ProgressBar

android:id="@+id/progressView"

style="?android:attr/progressBarStyleLarge"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_gravity="center"

android:visibility="gone"/>

</android.support.design.widget.CoordinatorLayout>

Wealsoneedtoaddfollowingstylesinstyles.xml:

<resources>

<!--Baseapplicationtheme.-->

<stylename="AppTheme"

parent="Theme.AppCompat.Light.DarkActionBar">

<!--Customizeyourthemehere.-->

<itemname="colorPrimary">@color/colorPrimary</item>

<itemname="colorPrimaryDark">@color/colorPrimaryDark</item>

<itemname="colorAccent">@color/colorAccent</item>

</style>

<stylename="AppFullScreenTheme"

parent="Theme.AppCompat.Light.NoActionBar">

<itemname="android:windowNoTitle">true</item>

<itemname="android:windowActionBar">false</item>

<itemname="android:windowFullscreen">true</item>

<itemname="android:windowContentOverlay">@null</item>

</style>

<stylename="ItemTitleName"

parent="TextAppearance.AppCompat.Headline">

<itemname="android:textColor">@android:color/white</item>

<itemname="android:shadowColor">@color/colorPrimaryDark</item>

<itemname="android:shadowRadius">3.0</item>

</style>

<stylename="ItemDetailTitle"

parent="@style/TextAppearance.AppCompat.Small">

<itemname="android:textColor">@color/colorAccent</item>

</style>

</resources>

AndweneedtodefineAppFullScreenThemeasthethemeforCharacterProfileActivityinAndroidManifest:

<activityandroid:name=".view.CharacterProfileActivity"

android:theme="@style/AppFullScreenTheme"/>

Hereisapreviewofthedefinedlayout:

Page 507: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

Thisviewwillbeusedtodisplaydataaboutthecharacter,butfirstweneedtoopenitfromMainActivity.WeneedtosetonClickListenerinCharacterItemAdapter,thatiscallingclickedcallbackprovidedbyconstructor:

packagecom.sample.marvelgallery.view.main

importandroid.support.v7.widget.RecyclerView

importandroid.view.View

importandroid.widget.ImageView

importandroid.widget.TextView

importcom.sample.marvelgallery.R

importcom.sample.marvelgallery.model.MarvelCharacter

importcom.sample.marvelgallery.view.common.ItemAdapter

importcom.sample.marvelgallery.view.common.bindView

importcom.sample.marvelgallery.view.common.loadImage

classCharacterItemAdapter(

valcharacter:MarvelCharacter,

valclicked:(MarvelCharacter)->Unit

):ItemAdapter<CharacterItemAdapter.ViewHolder>(R.layout.item_character){

overridefunonCreateViewHolder(itemView:View)=

ViewHolder(itemView)

overridefunViewHolder.onBindViewHolder(){

textView.text=character.name

imageView.loadImage(character.imageUrl)

itemView.setOnClickListener{clicked(character)}

}

classViewHolder(itemView:View):

Page 508: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

RecyclerView.ViewHolder(itemView){

valtextViewbybindView<TextView>(R.id.textView)

valimageViewbybindView<ImageView>(R.id.imageView)

}

}

AndweneedtoupdateMainActivity:

packagecom.sample.marvelgallery.view.main

importandroid.os.Bundle

importandroid.support.v7.widget.GridLayoutManager

importandroid.view.Window

importcom.sample.marvelgallery.R

importcom.sample.marvelgallery.data.MarvelRepository

importcom.sample.marvelgallery.model.MarvelCharacter

importcom.sample.marvelgallery.presenter.MainPresenter

importcom.sample.marvelgallery.view.character.CharacterProfileActivity

importcom.sample.marvelgallery.view.common.BaseActivityWithPresenter

importcom.sample.marvelgallery.view.common.addOnTextChangedListener

importcom.sample.marvelgallery.view.common.bindToSwipeRefresh

importcom.sample.marvelgallery.view.common.toast

importkotlinx.android.synthetic.main.activity_main.*

classMainActivity:BaseActivityWithPresenter(),MainView{

overridevarrefreshbybindToSwipeRefresh(R.id.swipeRefreshView)

overridevalpresenterbylazy

{MainPresenter(this,MarvelRepository.get())}

overridefunonCreate(savedInstanceState:Bundle?){

super.onCreate(savedInstanceState)

requestWindowFeature(Window.FEATURE_NO_TITLE)

setContentView(R.layout.activity_main)

recyclerView.layoutManager=GridLayoutManager(this,2)

swipeRefreshView.setOnRefreshListener{presenter.onRefresh()}

searchView.addOnTextChangedListener{

onTextChanged{text,_,_,_->

presenter.onSearchChanged(text)

}

}

presenter.onViewCreated()

}

overridefunshow(items:List<MarvelCharacter>){

valcategoryItemAdapters=

items.map(this::createCategoryItemAdapter)

recyclerView.adapter=MainListAdapter(categoryItemAdapters)

}

overridefunshowError(error:Throwable){

toast("Error:${error.message}")

error.printStackTrace()

}

privatefuncreateCategoryItemAdapter(character:MarvelCharacter)

=CharacterItemAdapter(character,

{showHeroProfile(character)})

privatefunshowHeroProfile(character:MarvelCharacter){

CharacterProfileActivity.start(this,character)

}

Page 509: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

}

Intheprecedingimplementation,weareusingamethodfromtheCharacterProfileActivitycompanionobjecttostartCharacterProfileActivity.WeneedtopasstheMarvelCharacterobjecttothismethod.ThemostefficientwaytopassaMarvelCharacterobjectispassitasparcelable.Toallowit,MarvelCharactermustimplementtheParcelableinterface.ThisiswhyausefulsolutionistousesomeannotationprocessinglibrarysuchasParceler,PaperParcel,orSmuggler,thatgeneratesthenecessaryelements.WewillusesolutionfromKotlinAndroidextensionswealreadyhaveintheproject.Duringbookpublication,itwasstillexperimental,sothereneedstobeaddedfollowingdefinitioninthebuild.gradlemodule:

androidExtensions{

experimental=true

}

AllweneedtodoittoaddParcelizeannotationbeforeclass,andweneedtomakethisclassimplementParcelable.WewillalsoadderrorsuppressionbecausetohidedefaultAndroidwarning:

packagecom.sample.marvelgallery.model

importandroid.annotation.SuppressLint

importandroid.os.Parcelable

importcom.sample.marvelgallery.data.network.dto.CharacterMarvelDto

importkotlinx.android.parcel.Parcelize

@SuppressLint("ParcelCreator")

@Parcelize

constructor(dto:CharacterMarvelDto):this(

name=dto.name,

imageUrl=dto.imageUrl

)

}

Nowwecanimplementthestartfunctionandfieldcharacter,thatwillgettheargumentvaluefromIntentusingthepropertydelegate:

packagecom.sample.marvelgallery.view.character

importandroid.content.Context

importandroid.support.v7.app.AppCompatActivity

importandroid.os.Bundle

importandroid.view.MenuItem

importcom.sample.marvelgallery.R

importcom.sample.marvelgallery.model.MarvelCharacter

importcom.sample.marvelgallery.view.common.extra

importcom.sample.marvelgallery.view.common.getIntent

importcom.sample.marvelgallery.view.common.loadImage

Page 510: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

importkotlinx.android.synthetic.main.activity_character_profile.*

classCharacterProfileActivity:AppCompatActivity(){

valcharacter:MarvelCharacterbyextra(CHARACTER_ARG)//1

overridefunonCreate(savedInstanceState:Bundle?){

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_character_profile)

setUpToolbar()

supportActionBar?.title=character.name

headerView.loadImage(character.imageUrl,centerCropped=true)//1

}

overridefunonOptionsItemSelected(item:MenuItem):Boolean=when{

item.itemId==android.R.id.home->onBackPressed().let{true}

else->super.onOptionsItemSelected(item)

}

privatefunsetUpToolbar(){

setSupportActionBar(toolbar)

supportActionBar?.setDisplayHomeAsUpEnabled(true)

}

companionobject{

privateconstvalCHARACTER_ARG="com.sample.marvelgallery.view.character.CharacterProfileActivity.CharacterArgKey"

funstart(context:Context,character:MarvelCharacter){

valintent=context

.getIntent<CharacterProfileActivity>()//1

.apply{putExtra(CHARACTER_ARG,character)}

context.startActivity(intent)

}

}

}

1. TheextraandgetIntentextensionfunctionswerealreadypresentedinthebook,buttheyarenotimplementedyetintheproject.Also,loadImagewilldisplayanerrorbecauseitneedstobechanged.

WeneedtoupdateloadImage,anddefineextraandgetIntentastop-levelfunctions:

//ViewExt.kt

packagecom.sample.marvelgallery.view.common

importandroid.app.Activity

importandroid.content.Context

importandroid.content.Intent

importandroid.os.Parcelable

importandroid.support.annotation.IdRes

importandroid.support.v4.widget.SwipeRefreshLayout

importandroid.widget.ImageView

importandroid.widget.Toast

importcom.bumptech.glide.Glide

importkotlin.properties.ReadWriteProperty

importkotlin.reflect.KProperty

importandroid.support.v7.widget.RecyclerView

importandroid.view.View

Page 511: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

fun<T:View>RecyclerView.ViewHolder.bindView(viewId:Int)

=lazy{itemView.findViewById<T>(viewId)}

funImageView.loadImage(photoUrl:String,centerCropped:Boolean=false){

Glide.with(context)

.load(photoUrl)

.apply{if(centerCropped)centerCrop()}

.into(this)

}

fun<T:Parcelable>Activity.extra(key:String,default:T?=null):Lazy<T>

=lazy{intent?.extras?.getParcelable<T>(key)?:default?:throwError("Novalue$keyinextras")}

inlinefun<reifiedT:Activity>Context.getIntent()=Intent(this,T::class.java)

//...

InsteadofdefiningfunctionstostarttheActivity,wemightusesomelibrarythatisgeneratingthesemethods.Forexample,wemightusetheActivityStarterlibrary.ThisiswhatCharacterProfileActivitywouldlooklike:

classCharacterProfileActivity:AppCompatActivity(){

@get:Argvalcharacter:MarvelCharacterbyargExtra()

overridefunonCreate(savedInstanceState:Bundle?){

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_character_profile)

setUpToolbar()

supportActionBar?.title=character.name

headerView.loadImage(character.imageUrl,centerCropped=true)//1

}

overridefunonOptionsItemSelected(item:MenuItem):Boolean=when{

item.itemId==android.R.id.home->onBackPressed().let{true}

else->super.onOptionsItemSelected(item)

}

privatefunsetUpToolbar(){

setSupportActionBar(toolbar)

supportActionBar?.setDisplayHomeAsUpEnabled(true)

}

}

WeshouldstartitofgetitsintentusingstaticmethodsofthegeneratedclassCharacterProfileActivityStarter:

CharacterProfileActivityStarter.start(context,character)

valintent=CharacterProfileActivityStarter.getIntent(context,character)

Toallowit,weneedthekaptplugininthemodulebuild.gradle(usedtosupportannotationprocessinginKotlin):

applyplugin:'kotlin-kapt'

Page 512: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

AndActivityStarterdependenciesinmodulebuild.gradle:

implementation'com.github.marcinmoskala.activitystarter:activitystarter:1.00'

implementation'com.github.marcinmoskala.activitystarter:activitystarter-kotlin:1.00'

kapt'com.github.marcinmoskala.activitystarter:activitystarter-compiler:1.00'

Afterthesechanges,whenweclickintocharacterinMainActivity,thenCharacterProfileActivitywillbestarted:

Wearedisplayingthenameandshowingthecharacterphoto.Thenextstepistodisplaythedescriptionandlistofoccurrences.ThenecessarydatacanbefoundintheMarvelAPIandweonlyneedtoextendDTOmodelstogetthem.WeneedtoaddListWrapperthatisusedtoholdalist:

packagecom.sample.marvelgallery.data.network.dto

classListWrapper<T>{

varitems:List<T>=listOf()

}

WeneedtodefineComicDto,whichholdsthedataweneedaboutoccurrence:

packagecom.sample.marvelgallery.data.network.dto

classComicDto{

lateinitvarname:String

}

AndweneedtoupdateCharacterMarvelDto:

packagecom.sample.marvelgallery.data.network.dto

Page 513: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

classCharacterMarvelDto{

lateinitvarname:String

lateinitvardescription:String

lateinitvarthumbnail:ImageDto

varcomics:ListWrapper<ComicDto>=ListWrapper()

varseries:ListWrapper<ComicDto>=ListWrapper()

varstories:ListWrapper<ComicDto>=ListWrapper()

varevents:ListWrapper<ComicDto>=ListWrapper()

valimageUrl:String

get()=thumbnail.completeImagePath

}

DataisnowreadfromtheAPIandkeptinDTOobjects,buttousethemintheproject,wealsoneedtochangetheMarvelCharacterclassdefinition,andaddanewconstructor:

@SuppressLint("ParcelCreator")

@Parcelize

classMarvelCharacter(

valname:String,

valimageUrl:String,

valdescription:String,

valcomics:List<String>,

valseries:List<String>,

valstories:List<String>,

valevents:List<String>

):Parcelable{

constructor(dto:CharacterMarvelDto):this(

name=dto.name,

imageUrl=dto.imageUrl,

description=dto.description,

comics=dto.comics.items.map{it.name},

series=dto.series.items.map{it.name},

stories=dto.stories.items.map{it.name},

events=dto.events.items.map{it.name}

)

}

NowwecanupdateCharacterProfileActivitytodisplaythedescriptionandlistofoccurrences:

classCharacterProfileActivity:AppCompatActivity(){

valcharacter:MarvelCharacterbyextra(CHARACTER_ARG)

overridefunonCreate(savedInstanceState:Bundle?){

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_character_profile)

setUpToolbar()

supportActionBar?.title=character.name

descriptionView.text=character.description

occurrencesView.text=makeOccurrencesText()//1

headerView.loadImage(character.imageUrl,centerCropped=true)

}

Page 514: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

overridefunonOptionsItemSelected(item:MenuItem):Boolean=when{

item.itemId==android.R.id.home->onBackPressed().let{true}

else->super.onOptionsItemSelected(item)

}

privatefunsetUpToolbar(){

setSupportActionBar(toolbar)

supportActionBar?.setDisplayHomeAsUpEnabled(true)

}

privatefunmakeOccurrencesText():String=""//1,2

.addList(R.string.occurrences_comics_list_introduction,character.comics)

.addList(R.string.occurrences_series_list_introduction,character.series)

.addList(R.string.occurrences_stories_list_introduction,character.stories)

.addList(R.string.occurrences_events_list_introduction,character.events)

privatefunString.addList(introductionTextId:Int,list:List<String>):String{//3

if(list.isEmpty())returnthis

valintroductionText=getString(introductionTextId)

vallistText=list.joinToString(transform=

{"$bullet$it"},separator="\n")

returnthis+"$introductionText\n$listText\n\n"

}

companionobject{

privateconstvalbullet='\u2022'//4

privateconstvalCHARACTER_ARG="com.naxtlevelofandroiddevelopment.marvelgallery.presentation.heroprofile.CharacterArgKey"

funstart(context:Context,character:MarvelCharacter){

valintent=context

.getIntent<CharacterProfileActivity>()

.apply{putExtra(CHARACTER_ARG,character)}

context.startActivity(intent)

}

}

}

1. Thecompositionofthelistofoccurrencesisquiteacomplextask,soweextractittothefunctionmakeOccurrencesText.There,foreachoccurrencetype(comic,series,andsoon),wewanttoshowintroductiontextandlistonlyiftherearesomeoccurrencesofthistype.Wealsowanttoprefixeachitemwithabullet.

2. makeOccurrencesTextisasingleexpressionfunctionthatisusingaddListtoappendaninitiallyemptystringwiththenextliststhatwewanttodisplay.

3. addListisamemberextensionfunction.Itisreturningastringunchangediftheprovidedlistisempty,oritisreturningastringappendedwithintroductiontextandlistofelementswithbullets.

4. Thisisthecharacterthatisusedasalistbullet.

Wealsoneedtodefinestringsinstrings.xml:

<resources>

<stringname="app_name">MarvelGallery</string>

Page 515: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

<stringname="marvel_copyright_notice">

DataprovidedbyMarvel.©2017MARVEL</string>

<stringname="search_hint">Searchforcharacter</string>

<stringname="occurrences_comics_list_introduction">Comics:</string>

<stringname="occurrences_series_list_introduction">Series:</string>

<stringname="occurrences_stories_list_introduction">Stories:</string>

<stringname="occurrences_events_list_introduction">Events:</string>

</resources>

Nowwecanseethewholecharacterprofile--charactername,image,description,andlistsofitsoccurrencesincomics,series,events,andstories:

Page 516: Android Development with Kotlinenglishonlineclub.com/pdf/Android Development with Kotlin... · 2019-09-21 · developed apps and games for Android since 2008. He is a Kotlin and RxJava

SummaryTheapplicationiscomplete,buttherearestilllotsoffunctionalitiesthatcanbeadded.Inthisapplication,we'veseensomeexamplesofhowKotlincanbeusedtosimplifyAndroiddevelopment.Buttherearestillalotofsolutionstodiscover.KotlinsimplifiesAndroiddevelopmentatanylevel--fromcommonoperationssuchaslistenersetorviewelementreference,tohigh-levelfunctionalitiessuchasfunctionalprogrammingorcollectionprocessing.

ThisbookcannotsayeverythingaboutAndroiddevelopmentwithKotlin.Itwasdesignedtoshowenoughtoalloweveryonetostarttheirownadventurewithbaggagefullofideasandfeatureunderstanding.ThenextstepistoopenAndroidStudio,createyourownproject,andstarthavingfunwithKotlin.Thebigadventureisinfrontofyou.