Kotlin faldgruber, og hvordan man undgår dem

Kotlin er alt det vrede sidst. Og selvom jeg er enig i, at sproget er gennemtænkt, har det - som med alt andet - dets mangler.

I denne artikel forklarer jeg nogle af de faldgruber, jeg stød på, og prøver at hjælpe dig med at undgå dem.

Mysteriet null

I Kotlin kan du skrive din kode, som om null aldrig eksisterede, og dette kan få dig til at glemme, at null er allestedsnærværende, men det skjuler sig. Lad os se på denne enkle og tilsyneladende uskyldige klasse:

Hvis du prøver at øjeblikkeligt dette, får du en NullPointerException, fordi linjen forsøgte at få adgang til længden på c, før den blev initialiseret.

Naturligvis var applikationslogikken mangelfuld her, men du har stadig en undtagelse. Den værste del af dette er, at din IDE ikke klager over dette.

Takeaway her er, at Kotlin vil hjælpe dig i mange tilfælde (næsten alle) med at undgå null, men du kan ikke glemme det, og fra tid til anden støder du på ting som dette.

Håndtering af nul fra JDK

Kotlins standardbibliotek håndterer nuller fint. Men hvis du bruger klasser fra JDK, bliver du nødt til at håndtere mulige nullpunkter fra biblioteksfunktioner manuelt.

Det meste af tiden er Kotlin-klasserne nok, men nogle gange skal du bruge noget som ConcurrentHashMap:

I dette tilfælde skal du bruge !! operatør. Men i andre tilfælde kan nullsikkerhedsoperatøren (?) Også fungere. Ikke desto mindre, hvis du bruger Java-biblioteker i vid udstrækning, skal du kaste din kode med !! s og? S eller skrive adaptere til Java-klasser. Dette er noget, du ikke rigtig kan undgå.

Der er et andet mere afskyeligt problem, du muligvis støder på. Når du bruger metoder på JDK-klasser, kan de returnere nul og har ikke syntaktisk sukker som kortadgang ovenfor.

Overvej følgende eksempel:

I dette tilfælde bruger du kig, som faktisk kan returnere nul. Men Kotlin-kompilatoren klager ikke, så du kan få en NullPointerException, hvis din kø var tom.

Problemet her er, at vi brugte kø, som er en JDK-grænseflade, og hvis du ser på implementeringen af ​​kig:

Det siger, at kig vil returnere E, hvilket vil føre til, at Kotlin mener at E ikke er nullable. Dette kan muligvis bearbejdes i en fremtidig version af Kotlin, men lige nu er det vigtigt at huske dette i dine projekter og bruge grænseflader som dette:

Det indre

Når en lambda har en enkelt parameter, kan du udelade den fra din kode og kan bruge den i stedet:

"Det: implicit navn på en enkelt parameter En anden nyttig konvention er, at hvis en funktion bogstavelig har kun en parameter, kan dens erklæring udelades (sammen med ->), og dens navn vil være den." - Kotlin docs

Problemet med dette er, når du har indlejrede funktioner som i dette eksempel:

Det tager kun et par få at miste sporet, hvilket er hvilket. Løsningen på dette problem er at navngive parametrene eksplicit:

Meget bedre!

Den lumske kopi

Se på denne dataklasse:

Dataklasser giver dig en masse funktioner, og du kan også lave en kopi af dem. Gæt hvad dette vil udskrive:

Dette udskriver foobar, wombar, ups. Problemet er, at selvom navnet indikerer, at kopi faktisk laver en faktisk kopi, faktisk kun kopierer referencerne i dit objekt. Dette kan være snigende, hvis du glemmer at skrive en enhedstest og du videregiver dine dataklasser som om de var uforanderlige værdiobjekter.

Løsningen på dette problem er at være opmærksom på dine dataklasser, og hvis de skulle være værdiobjekter, gør dem til en:

Der er et andet problem med dataklasser: Du kan ikke fortælle Kotlin, hvilke felter du vil lægge i din lig / hashCode, du kan kun tilsidesætte begge og skrive dem for hånd. Husk dette.

Intern lækage

Se på dette eksempel:

Hvis du bruger klasser som denne fra andre Kotlin-projekter, respekteres det interne nøgleord. Hvis du ser på dette fra et Java-projekt, vil HiddenOperation dog være offentlig! For at undgå dette foreslår jeg, at du bruger grænseflader til at skjule implementeringsdetaljer:

Ikke-generiske globale udvidelser

Nyttigheden af ​​udvidelsesfunktioner er uden tvivl høj, men med stor kraft kommer stort ansvar. Du kan for eksempel skrive udvidelsesfunktioner til JDK-klasser, som vil være synlige for hele projektet. Dette kan være problematisk, når de er ikke-generiske og repræsenterer operationer, der kun giver mening i en lokal sammenhæng:

Nu vil alle på dit projekt ridse på hovedet, når de støder på dette. Så jeg synes, det er godt, hvis du tænker to gange, før du skriver udvidelsesfunktioner, men de kan være virkelig magtfulde. her er nogle eksempler, der kan være nyttige:

Enhed, der returnerer lambdas vs Java SAM-konvertering

Når du har funktioner, der accepterer lambdas, kan du udelade returneringssøgeord, hvis lambdas returtype er enhed:

Dette er fint, men hvis du kalder dette fra Java, står du overfor det akavede problem med at skulle returnere enhed:

Dette er meget klodset fra Java-siden. Hvis du prøver at gøre dette arbejde fra Java, kan du definere en grænseflade:

så kan du bruge Java's SAM-konvertering til at gøre dette meget enkelt:

men så fra Kotlinsiden bliver det et rod:

Problemet er, at Kotlin ikke understøtter SAM-konvertering til Kotlin-klasser, og det fungerer kun med Java-klasser. Mit forslag er, at du i enkle tilfælde bare skal bruge Java's indbyggede SAM-grænseflader som forbruger:

Java interop med umodificerbare samlinger

Kotlin giver dig uforanderlige varianter af JDKs samlingsklasser:

Dette er en meget flot tilføjelse, men hvis du ser på dette fra Java-siden, vil du se JDK's Set api:

Hvis du prøver at ændre dette sæt, vil det samme ske, som hvis du har anvendt Java's metode Collection.unmodifiableSet (). Jeg ved ikke, om dette kan bearbejdes (eller bør), men dette er noget, du kan huske på, når du arbejder med Kotlins uforanderlige versioner af Java-samlinger.

Ingen overbelastning i grænseflader

Dette er kun et problem fra et interop-perspektiv, men Kotlin understøtter ikke @JvmOverloads-kommentaren i en grænseflade, og du kan heller ikke bruge den til tilsidesættelser:

I øjeblikket er det eneste, du kan gøre for at have overbelastning, at definere dem for hånd:

Husk dog, at du kan foreslå forbedringer til Kotlin selv ved hjælp af KEEP (Kotlin Evolution and Enhancement Process). KEEP er noget som JEP i Java, men selvfølgelig har KEEP langt mindre bureaukrati sammenlignet med JEP.

Konklusion

Kotlin er et meget populært sprog lige nu, og jeg er enig i, at det er en Turbo Java, men du skal tage enhver hype med et saltkorn. Som vi har set ovenfor, har Kotlin sine egne faldgruber, som du skal være opmærksom på, hvis du planlægger at bruge det.

Alt i alt tror jeg, at de ovennævnte problemer enten kan løses let eller ikke kritiske, og at de ikke begrænser anvendeligheden af ​​selve sproget.

Tak for at have læst! Du kan læse flere af mine artikler på min blog.