Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Kotlin
Code

Kotlin Mula sa Scratch: Advanced Functions

by
Difficulty:IntermediateLength:LongLanguages:
This post is part of a series called Kotlin From Scratch.
Kotlin From Scratch: More Fun With Functions
Kotlin From Scratch: Classes and Objects

Tagalog (Wikang Tagalog) translation by Anna Nelson (you can also view the original English article)

Ang Kotlin ay isang functional language at ibig sabihin nito ang functions ay unahan at gitna. Ang language na ito ay puno ng features para gawing mabilis at makahulugan ang coding functions. Sa post na ito, matututunan mo ang tungkol sa extension functions, higher-order functions, closures, at inline functions sa Kotlin.

Sa nakaraang artikulo, iyong natutunan ang tungkol sa top-level functions, lambda expressions, anonymous functions, local functions, infix functions, at ang panghuli ang member functions sa Kotlin, Sa pagtuturong ito, ipagpapatuloy natin na mas alamin ang tungkol sa functions sa Kotlin sa pamamagitan ng pagtuklas pa dito:

  • extension functions
  • higher-order functions
  • mga closure
  • inline functions

1. Extension Functions

Hindi ba maganda kung ang String type sa Java ay mayroon method para gawing capital ang unang letra sa String-katulad ng ucfirst() in PHP? Maaari nating tawagin ang method na ito na upperCaseFirstLetter().

Para magawa ito, maaari kang gumawa ng subclass ng String na nagi-extend ng String type sa Java. Subalit tandaan na ang String class sa Java ay hindi nababago - ibig sabihin hindi mo ito maaaring i-extend. Ang posibleng solusyon ay gumawa ng helper functions o top-level functions, subalit maaaring hindi ito naaayon dahil hindi natin magagamit ang IDE auto-complete feature para makita ang listahan ng methods na mayroon para sa String type. Ang maganda talaga ay kung kahit papaano ay maglagay ng function sa class ng hindi kailangang i-inherit mula sa class na iyon.

Sa totoo lang, sa Kotlin ay binigyan tayo ng isa pang kahanga-hangang feauture: extension functions. Nagbibigay ito sa atin ng kakayahan na ma-extend ang class na may bagong functionality na hindi kailangang i-inherit mula sa class na iyon.Sa madaling salita, hindi natin kailangang gumawa ng bagong subtype o palitan ang orihinal na type.

Ang extension function ay dini-declare sa labas ng class na nais nitong i-extend. Sa madaling salita, ito ay top-level function din (kung gusto mo ng refresher sa top-level functions sa Kotlin, pumunta sa More Fun With Functions na pagtuturo sa seryeng ito.

Kasama ng extension functions, sinu-support din ng Kotlin ang extension properties. Sa post na ito, tatalakayin natin ang extension functions, at maghihintay tayo ng susunod na post para talakayin ang extension properties kasama ang classes sa Kotlin.

Paggawa ng Extension Function

Katulad ng makikia mo sa code sa ibaba, tinukoy natin ang top-level na function bilang karaniwan sa atin na mag-deklara ng extension function. Ang extension function na ito ay nasa loob ng package na tinatawag na calledcom.chike.kotlin.strings.

Para makagawa ng extension function, kailangan mong lagyan ng prefix ang name ng class na iyong inextend bago ang function name. Ang class name o ang type kung saan ang extension ay tinukoy ay tinatawag na receiver type, at ang receiver object ay ang class instance o value kung saan ang extension function ay tinawag.

Tandaan na ang keyword na ito sa loob ng function body ay nire-reference ang receiver object o instance.

Ang Pagtawag sa Extension Function

Pagkatapos gumawa ng extension function, una kailangan mong i-import ang extension function sa iba pang packages o files na gagamitin sa file na iyon o package.Pagkatapos, ang pagtawag sa function ay pareho ng pagtawag sa kahit na anong ibang method ng receiver type class.

Sa halimbawa sa itaas, ang receiver type ay class String, at ang receiver object ay “chike”. Kung gumagamit ka ng IDE katulad ng IntelliJ IDEA na may IntelliJ IDEA feature, iyong makikita ang bagong extension function na iminumungkahi sa lahat ng listahan ng iba pang functions sa String type.

IntelliJ IDEA intellisense feature

Jave Interoperability

Tandaan na sa likod ng mga ito o hindi hayagan, ang Kotlin ay gagawa ng static method. Ang unang argument ng static method na ito ay ang receiver object. Kung kaya madali para sa Java callers na tawagin ang static method na ito at ipasa ang receiver object bilang argument.

Halimbawa, kung ang extension function ay dineklara sa StringUtils.kt file, ang Kotlin compiler ay gagawa ng Java class StringUtilsKt na may static method upperCaseFirstLetter().

Ibig sabihin nito ay ang Java callers ay maaaring tawagin ang method sa pamamagitan ng pag-reference ng generated class nito, katulad ng para sa kahit na anong iba pang static method.

Tandaan na ang Java interop mechanism na ito ay hawig sa kung paano ang top-level functions ay gumagana sa Kotlin, katulad ng tinalakay natin sa More Fun With Functions na post!

Extension Function laban sa Member Functions

Tandaan na ang extension functions ay hindi maaaring i-override ang functions na nadeklara na sa class o interface- na tinatawag na member functions (kung gusto mo ng refresher sa member functions sa Kotlin, tingnan ang dating pagtuturo sa seryeng ito). Kung kaya kung natukoy mo na ang extension function na may eksaktong parehong function signature-parehong function name at parehong numero- types at pagkakasunud-sunod ng arguments, anumang return type ito- hindi ito ii-invoke ng Kotlin compiler. Sa proseso ng compilation, kapag ang function ay naka-invoke, ang Kotlin compiler ay titingin muna ng katugma sa member function na tinukoy sa instance type o sa superclasses nito. Kapag may katugma, ang member function na iyon ay ang na-invoke o bound. Kapag walang katugma, ang compiler ay magi-invoke ng kahit na anong extension function ng parehong type.

Kung kaya ang buod nito: ang member functions ay laging nananalo.

Tingnan natin ang isang praktikal na halimbawa.

Sa code sa itaas, natukoy natin ang type na tinatawag na Student na may dalawang member functions: printResult() at expel().Kaya tinukoy natin ang dalawang extension functions na may parehong pangalan ng member functions.

Tawagin natin ang printResult() function at tingnan natin ang resulta.

Katulad ng iyong nakikita, ang function na na-invoke o bound ay ang member function at hindi ang extension function na may parehong function signature (bagaman ang IntelliJ IDEA ay magbibigay pa din sa iyo ng pahiwatig tungkol dito).

Gayunpaman, ang pagtawag ng member function expel() at ng extension function expel(reason: String) ay magbibigay ng iba’t ibang resulta dahil ang function signatures ay iba.

Member Extension Functions

Ikaw ay magdi-deklara ng extension function bilang top-level function ng madalas, subalit tandaan na maaari mo ring ideklara ang mga iyon bilang member functions.

Sa itaas na code, nag-declare tayo ng extension function exFunction() ng ClassB type sa loob ng iba pang class ClassA. Ang dispatch receiver ay ang instance ng class kung saan ang extension ay naka-deklara, at at ang instance ng receiver type ng extension method ay tinatawag na extension receiver. Kapag mayroong problema sa pangalan o shadowing sa pagitan ng dispatch receiver at extension receiver, tandaan na pinipili ng compiler ang extension receiver.

Kung kaya sa halimbawang code sa itaas, ang extension receiver ay ang instance ng ClassB — ibig sabihin ang toString() method ng type ClassB kapag tinawag sa loob ng extension function exFunction().Para sa halip ma-invoke natin ang toString() method ng dispatch receiver ClassA , kailangan nating gumamit ng qualified katulad nito:

2. Higher-Order Functions

Ang higher-order function ay isa lang function na nagiging ibang function (o lambda expression) bilang parameter, nagbabalik ng function, o ginagawa pareho. Ang huling() collection function ay isang halimbawa ng higher-order function mula sa standard library.

Dito ay pinasa natin ang lambda sa huling function para magsilbing predicate para hanapin sa loob ng subset ng elements. Pupunta na tayo sa paggawa ng ating sariling higher-order functions sa Kotlin.

Paggawa ng Higher-Order Function

Kung titingnan natin ang function circleOperation() sa ibaba, mayroon itong dalawang parameters. Ang una, radius, tumatanggap ng double, at ang pangalawa, op, ay isang function na tumatanggap ng double bilang input at gayundin ay bumabalik ng double bilang output-masasabi natin sa madaling salita na ang pangalawang parameter ay “isang function mula sa double papunta sa double”.

Pagmasdan na ang op function parameter types para sa function ay nakapaloob sa parentheses (), at ang output type ay hinihiwalay ng arrow. Ang function circleOperation() ay tipikal na halimbawa ng higher-order function na tinatanggap ang function bilang parameter.

Pag-Invoke ng Higher-Order Function

Sa invocation ng circleOperation() function na ito, ipinasa natin ang isa pang function dito,ang  calArea(). (Tandaan na kapag ang method signature ng napasang function ay hindi nagtugma sa dineklara ng higher-order function, ang function call ay hindi magko-compile.)

Para ipasa ang calArea() function bilang parameter sa circleOperatin(), kailangang lagyan natin ito ng prefix na :: at tanggalin ang () brackets.

Sa paggamit ang higher-order functions ng tama ay magagawa nating madaling basahin at mas madaling maintindihan ang code.

Lambdas at Higher-Order Functions

Maaari din nating ipasa ang lambda (o function literal) sa higher-order function ng direkta kapag nagi-invoke ng function.

Tandaan, para maiwasan natin ang hayagang pagpangalan ng argument , maaari nating gamitin ang it argument name auto-generated para sa atin kung ang lambda ay may isa lang na argument. (Kung gusto mo ng refresher sa lambda sa Kotlin, tingnan ang More Fun With Functions na pagtuturo sa seryeng ito).

Pagbabalik ng Function

Tandaan na bilang karagdagan sa pagtanggap ng function bilang parameter, ang higher-order functions ay maaari ding magbalik ng functions sa callers.

Dito ang multiplier() function ay magbabalik ng function na nilalagay ang nariyan ng factor sa kahit na anong numero na ipinasa dito. Ang ibinalik na function na ito ay lambda (o function literal) mula sa double hanggang double (ibig sabihin ang input param ng binalik na function ay double type, at ang resulta ng output ay double type).

Para masubukan ito, nagpasa tayo ng factor ng dalawa at nagtalaga ng returned function sa variable doubler. Maaari natin itong i-invoke katulad ng normal function,at kung ano man ang value na ipinasa natin dito ay madodoble.

3. Closures

Ang closure ay isang function na may access sa variables at parameters na tinukoy sa labas ng saklaw nito.

Sa code sa itaas, ang lambda na ipinasa sa filter() collection function ay gumagamit ng parameter length ng outer function printFilteredNamesByLength(). Tandaan na ang parameter na ito ay tinukoy sa labas ng saklaw ng lambda, subalit ang lambda ay naa-access pa din ang length. Ang mekanismo na ito ay halimbawa ng closure sa functional programming.

4. Inline Functions

Sa More Fun With Functions, nabanggit ko na ang Kotlin compiler ay gumagawa ng anonymous class sa nakaraang bersyon ng Java ng hindi hayagan kapag gumagawa ng lambda expressions.

Sa kasamaang-palad, ang mekanismong ito ay ipinakikilala ang overhead dahil ang anonymous class ay ginawa sa ilalim ng hood sa bawat oras na gumagawa tayo ng lambda. At, ang lambda na gumagamit ng outer function parameter o local variable na may closure ay nagdadagdag ng sarili nitong allocation overhead dahil ang bagong object ay para sa heap sa bawat invocation.

Pagkukumpara ng Inline Functions Sa Normal Functions

Para maiwasan itong overheads, ang koponan ng Kotlin ay nagbigay sa atin ng inline modifier para sa functions. Ang higher-order function kasama ang kanilang inline modifier ay mai-inline sa oras ng code compilation. Sa madaling salita, kokopyahin ng compiler ang lambda (o function literal) at gayon din ang higher-order function body at ilalagay ang  mga ito sa call site.

Tingnan natin ang praktikal na halimbawa.

Sa code sa itaas, mayroon tayong higher-order function circleOperation() na walang inline modifier. Ngayon tingnan natin ang Kotlin bytecode na nabubuo kapag nag compile at decompile tayo ng code, at ikinumpara natin sa may inline modifier.

Sa nagawang Java bytecode sa itaas, iyong makikita na tinawag ng compiler ang function circleOperation() sa loob ng main() method.

Ngayon sa halip ay tukuyin natin ang higher-order function bilang inline, gayon din ay tingnan ang nagawang bytecode.

Para makagawa ng higher-order function inline,kailagan nating ilagay ang inline modifier bago ang fun keyword, katulad ng ginawa natin sa code sa itaas. Tingnan din natin ang bytecode na nagawa para sa inline function na ito.

Kung titingnan natin ang nagawang bytecode para sa inline function sa loob ng main() function, mapapansin mo na sa halip na tawagin ang circleOperation() function, nakopya na nito ang circleOperation() function body kasama ang lambda body at nilagay ito sa call-site nito.

Sa mekanismong ito,ang ating code ay nagamit ng makabuluhan — hindi na kailangang gumawa ng anonymous classes o karagdagang memory allocations. Subalit sadyang alamin na magkakaroon tayo ng malaking bytecode ng hindi hayagan kaysa sa dati. Dahil dito, lubos itong iminumungkahi sa inline smaller higher-order functions lang na tumatanggap ng lambda bilang parameters.

Marami sa standard higher-order functions sa Kotlin ay mayroong inline modifier. Halimbawa, kung iyong titingnan ang koleksyon ng operation functions filter() at unang(), iyong makikita na mayroon silang inline modifier at maliit din ang sukat.

Tandaan na wag gawing inline ang normal functons na hindi tumatanggap ng lambda bilang parameter! Iko-compile nila, subalit wala itong makabuluhang pagbabago sa performance (ang IntelliJ IDEA ay magbibigay ng ideya tungkol dito).

Ang noinline Modifier

Kung mayroon kang higit sa dalawang lambda parameters sa function, mayroon kang pagpipilian na magpasya kung aling lambda ang hindi dapat i-inline gamit ang noinline modiier sa parameter. Ang functionality na ito ay mahalaga lalo na sa lambda parameter na kakailanganin ng maraming code. Sa madaling salita, hindi ika-copy at paste ng Kotlin compiler ang lambda na iyon kung saan ito tinawag sa halip ay gagawa ito ng anonymous class ng hindi hayagan.

Dito, nilagay natin ang noinline modifier sa pangalawang lambda parameter. Tandaan na ang modifier na ito ay may bisa lang kapag ang function ay may inline modifier.                                                                                        

Stack Trace sa Inline Functions

Tandaan na kapag ang exception ay ibinato sa loob ng inline function, ang method call stack sa  stack trace ay iba sa normal function ng walang inline modifier. Ito ay dahil sa copy and paste na mekanismo na ginamit ng compiler para sa inline functions. Ang nakakatuwang bagay ay ang IntelliJ IDEA ay nakakatulong sa atin para mas mabilis na ma-navigate ang method-call stack sa stack trace para sa inline function. Tumingin tayo ng halimbawa.

Sa code sa itaas, ang exception ay ibinato ng sadya sa loob ng inline function myFunc(). Ngayon tingnan natin ang stack trace sa loob ng IntelliJ IDEA kapag pinagana ang code. Kung titingnan natin ang screenshot sa ibaba, iyong makikita na binigyan tayo ng dalawang navigation options na pagpipilian: ang Inline function body o ang inline function call site. Kapag pinili ang nauna dadalhin tayo nito sa punto kung saan ang exception ay ibinato sa function body, habang ang huli ay dadalhin tayo sa punto kung saan ang method ay tinawag.

IntelliJ IDEA stack trace for inline function

Kapag ang function ay hindi inline, ang ating stack trace ay katulad ng dati mo ng nakikita:

IntelliJ IDEA stack trace for normal function

Konklusyon

Sa pagtuturong ito, iyong natutunan ang mas marami pang bagay na iyong magagawa sa functions sa Kotlin. Nasama natin ang:

  • extension functions
  • higher-order functions
  • mga closure
  • inline functions

Sa susunod na pagtuturo sa serye ng Kotlin Mula Sa Scratch, mas tutuklasin natin ang object-oriented programming at sisimulan nating pag-aralan paano gumagana ang classes work sa Kotlin. Sana makita ko kayo uli sa lalong madaling panahon!

Para mas matutunan pa ang tungkol sa Kotlin language, nirerekumenda kong puntahan ang documentation ng Kotlin. O tignan ang iba pa naming posts tungkol sa pagdevelop ng Android app dito sa Envato Tuts+!

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.