Dziesięć funkcji Kotlina przyspieszających rozwój Androida

Opublikowany: 2022-03-11

Wstęp

Jakiś czas temu Tomasz przedstawił programowanie Kotlina na Androida. Przypomnijmy: Kotlin to nowy język programowania opracowany przez Jetbrains, firmę stojącą za jednym z najpopularniejszych środowisk Java IDE, IntelliJ IDEA. Podobnie jak Java, Kotlin jest językiem ogólnego przeznaczenia. Ponieważ jest zgodny z kodem bajtowym Java Virtual Machine (JVM), może być używany równolegle z Javą i nie wiąże się z obciążeniem wydajnością.

W tym artykule omówię 10 najważniejszych przydatnych funkcji, które usprawnią rozwój Androida.

Uwaga : w momencie pisania tego artykułu rzeczywiste wersje to Android Studio 2.1.1. oraz Kotlin 1.0.2.

Kotlin

Masz dość niekończącego się kodu Java? Wypróbuj Kotlina i oszczędź swój czas i zdrowie psychiczne.
Ćwierkać

Konfiguracja Kotlina

Ponieważ Kotlin jest rozwijany przez JetBrains, jest dobrze obsługiwany zarówno w Android Studio, jak i IntelliJ.

Pierwszym krokiem jest zainstalowanie wtyczki Kotlin. Po pomyślnym wykonaniu dostępne będą nowe akcje konwersji Javy na Kotlin. Dwie nowe opcje to:

  1. Utwórz nowy projekt na Androida i skonfiguruj Kotlina w projekcie.
  2. Dodaj obsługę Kotlin do istniejącego projektu Androida.

Aby dowiedzieć się, jak stworzyć nowy projekt na Androida, zapoznaj się z oficjalnym przewodnikiem krok po kroku. Aby dodać obsługę Kotlina do nowo utworzonego lub istniejącego projektu, otwórz okno dialogowe Znajdź działanie za pomocą Command + Shift + A na Macu lub Ctrl + Shift + A na Windows/Linux i wywołaj akcję Configure Kotlin in Project .

Aby stworzyć nową klasę Kotlin, wybierz:

  • File > New > Kotlin file/class , lub
  • File > New > Kotlin activity

Alternatywnie możesz utworzyć klasę Java i przekonwertować ją na Kotlin za pomocą akcji wspomnianej powyżej. Pamiętaj, że możesz go użyć do konwersji dowolnej klasy, interfejsu, wyliczenia lub adnotacji, a to może być użyte do łatwego porównania Javy z kodem Kotlin.

Kolejnym przydatnym elementem, który pozwala zaoszczędzić sporo czasu na pisanie, są rozszerzenia Kotlin. Aby z nich skorzystać, musisz zastosować inną wtyczkę w pliku build.gradle modułu:

 apply plugin: 'kotlin-android-extensions'

Zastrzeżenie : jeśli używasz akcji wtyczki Kotlin do skonfigurowania projektu, umieści ona następujący kod w twoim pliku build.gradle najwyższego poziomu:

 buildscript { ext.kotlin_version = '1.0.2' repositories { jcenter() } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } }

Spowoduje to, że rozszerzenie nie będzie działać. Aby to naprawić, po prostu skopiuj ten kod do każdego modułu projektu, w którym chcesz używać Kotlina.

Jeśli wszystko skonfigurujesz poprawnie, powinieneś być w stanie uruchomić i przetestować swoją aplikację w taki sam sposób, jak w standardowym projekcie Androida, ale teraz przy użyciu Kotlina.

Oszczędność czasu z Kotlin

Zacznijmy więc od opisania niektórych kluczowych aspektów języka Kotlin i podpowiedzmy, jak można zaoszczędzić czas, używając go zamiast Javy.

Funkcja nr 1: Import układu statycznego

Jednym z najczęstszych kodów szablonowych w systemie Android jest użycie funkcji findViewById() w celu uzyskania odwołań do widoków w działaniach lub fragmentach.

Istnieją rozwiązania, takie jak biblioteka Butterknife, które oszczędzają trochę pisania, ale Kotlin robi kolejny krok, pozwalając zaimportować wszystkie odniesienia do widoków z układu za pomocą jednego importu.

Rozważmy na przykład następujący układ XML działania:

 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:andro xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="co.ikust.kotlintest.MainActivity"> <TextView android: android:layout_width="wrap_content" android:layout_height="wrap_content"/> </RelativeLayout>

Oraz towarzyszący kod czynności:

 package co.ikust.kotlintest import android.support.v7.app.AppCompatActivity import android.os.Bundle import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) helloWorldTextView.text = "Hello World!" } }

Aby uzyskać referencje dla wszystkich widoków w układzie ze zdefiniowanym identyfikatorem, użyj rozszerzenia Android Kotlin Anko. Pamiętaj, aby wpisać tę instrukcję importu:

 import kotlinx.android.synthetic.main.activity_main.*

Zauważ, że nie musisz pisać średnikami na końcu linii w Kotlinie, ponieważ są one opcjonalne.

TextView z układu jest importowany jako instancja TextView o nazwie równej identyfikatorowi widoku. Nie dajcie się zmylić składnią, której używa się do ustawienia etykiety:

 helloWorldTextView.text = "Hello World!"

Omówimy to wkrótce.

Zastrzeżenia :

  • Upewnij się, że importujesz poprawny układ, w przeciwnym razie zaimportowane odniesienia View będą miały wartość null .
  • Używając fragmentów, upewnij się, że zaimportowane referencje View są używane po wywołaniu funkcji onCreateView() . Zaimportuj układ w funkcji onCreateView() i użyj odwołań do widoku, aby skonfigurować interfejs użytkownika w onViewCreated() . Referencje nie zostaną przypisane przed zakończeniem działania metody onCreateView() .

Cecha #2: Pisanie zajęć POJO z Kotlin

Coś, co zaoszczędzi najwięcej czasu z Kotlinem, to napisanie klas POJO (Plain Old Java Object) używanych do przechowywania danych. Na przykład w treści żądania i odpowiedzi interfejsu API RESTful. W aplikacjach, które opierają się na RESTful API, takich klas będzie wiele.

W Kotlinie wiele się dla ciebie robi, a składnia jest zwięzła. Rozważmy na przykład następującą klasę w Javie:

 public class User { private String firstName; private String lastName; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } }

Pracując z Kotlinem, nie musisz ponownie wpisywać słowa kluczowego publicznego. Domyślnie wszystko ma zakres publiczny. Na przykład, jeśli chcesz zadeklarować klasę, po prostu napisz:

 class MyClass { }

Odpowiednik powyższego kodu Java w Kotlinie:

 class User { var firstName: String? = null var lastName: String? = null }

Cóż, to oszczędza dużo pisania, prawda? Przejdźmy przez kod Kotlin.

Kotlin oszczędza dużo pisania

Przy definiowaniu zmiennych w Kotlinie mamy dwie możliwości:

  • Zmienne mutowalne, zdefiniowane przez słowo kluczowe var .
  • Zmienne niezmienne, zdefiniowane przez słowo kluczowe val .

Następną rzeczą, na którą należy zwrócić uwagę, jest to, że składnia różni się nieco od Javy; najpierw deklarujesz nazwę zmiennej, a następnie typ. Ponadto domyślnie właściwości są typami innymi niż null, co oznacza, że ​​nie mogą akceptować wartości null . Aby zdefiniować zmienną akceptującą wartość null , po typie należy dodać znak zapytania. O tym i zerowym bezpieczeństwie porozmawiamy później w Kotlinie.

Inną ważną rzeczą do odnotowania jest to, że Kotlin nie ma możliwości deklarowania pól dla klasy; można zdefiniować tylko właściwości. Tak więc w tym przypadku firstName i lastName są właściwościami, którym przypisano domyślne metody pobierające/ustawiające. Jak wspomniano, w Kotlinie oba są domyślnie publiczne.

Akcesory niestandardowe można napisać, na przykład:

 class User { var firstName: String? = null var lastName: String? = null val fullName: String? get() firstName + " " + lastName }

Z zewnątrz, jeśli chodzi o składnię, właściwości zachowują się jak pola publiczne w Javie:

 val userName = user.firstName user.firstName = "John"

Zauważ, że nowa właściwość fullName jest tylko do odczytu (zdefiniowana przez słowo kluczowe val ) i ma niestandardowy pobieracz; po prostu dołącza imię i nazwisko.

Wszystkie właściwości w Kotlinie muszą być przypisane, gdy są zadeklarowane lub znajdują się w konstruktorze. W niektórych przypadkach nie jest to wygodne; na przykład dla właściwości, które zostaną zainicjowane przez iniekcję zależności. W takim przypadku można użyć modyfikatora lateinit . Oto przykład:

 class MyClass { lateinit var firstName : String; fun inject() { firstName = "John"; } }

Więcej szczegółów na temat nieruchomości można znaleźć w oficjalnej dokumentacji.

Cecha #3: Dziedziczenie klas i konstruktorzy

Kotlin ma też bardziej zwięzłą składnię, jeśli chodzi o konstruktory.

Konstruktorzy

Klasy Kotlin mają konstruktor główny i jeden lub więcej konstruktorów pomocniczych. Przykład zdefiniowania głównego konstruktora:

 class User constructor(firstName: String, lastName: String) { }

Konstruktor podstawowy występuje po nazwie klasy w definicji klasy. Jeśli główny konstruktor nie ma żadnych adnotacji ani modyfikatorów widoczności, słowo kluczowe konstruktor można pominąć:

 class Person(firstName: String) { }

Zauważ, że główny konstruktor nie może mieć żadnego kodu; każda inicjalizacja musi być wykonana w bloku kodu init :

 class Person(firstName: String) { init { //perform primary constructor initialization here } }

Ponadto podstawowy konstruktor może służyć do definiowania i inicjowania właściwości:

 class User(var firstName: String, var lastName: String) { // ... }

Podobnie jak zwykłe, właściwości zdefiniowane z konstruktora podstawowego mogą być niezmienne ( val ) lub mutowalne ( var ).

Klasy mogą mieć również konstruktory drugorzędne; składnia do zdefiniowania jest następująca:

 class User(var firstName: String, var lastName) { constructor(name: String, parent: Person) : this(name) { parent.children.add(this) } }

Należy zauważyć, że każdy konstruktor pomocniczy musi delegować do konstruktora podstawowego. Jest to podobne do Javy, która używa this słowa kluczowego:

 class User(val firstName: String, val lastName: String) { constructor(firstName: String) : this(firstName, "") { //... } }

Podczas tworzenia instancji zwróć uwagę, że Kotlin nie ma new słów kluczowych, podobnie jak Java. Aby utworzyć instancję wyżej wymienionej klasy User , użyj:

 val user = User("John", "Doe)

Przedstawiamy dziedziczenie

W Kotlinie wszystkie klasy wywodzą się z Any , co jest podobne do Object w Javie. Domyślnie klasy są zamknięte, podobnie jak klasy końcowe w Javie. Tak więc, aby rozszerzyć klasę, musi być ona zadeklarowana jako open lub abstract :

 open class User(val firstName, val lastName) class Administrator(val firstName, val lastName) : User(firstName, lastName)

Zauważ, że musisz wykonać delegację do domyślnego konstruktora klasy rozszerzonej, co jest podobne do wywołania metody super() w konstruktorze nowej klasy w Javie.

Więcej informacji o zajęciach znajdziesz w oficjalnej dokumentacji.

Funkcja nr 4: Wyrażenia lambda

Wyrażenia lambda, wprowadzone w Javie 8, to jedna z jego ulubionych funkcji. Jednak sytuacja nie jest tak jasna na Androidzie, ponieważ nadal obsługuje tylko Javę 7 i wygląda na to, że Java 8 nie będzie obsługiwana w najbliższym czasie. Tak więc obejścia, takie jak Retrolambda, wprowadzają wyrażenia lambda do systemu Android.

W przypadku Kotlina nie są wymagane żadne dodatkowe biblioteki ani obejścia.

Funkcje w Kotlinie

Zacznijmy od szybkiego przejrzenia składni funkcji w Kotlinie:

 fun add(x: Int, y: Int) : Int { return x + y }

Wartość zwracaną przez funkcję można pominąć iw takim przypadku funkcja zwróci Int . Warto powtórzyć, że wszystko w Kotlinie jest obiektem, rozszerzonym z Any i nie ma typów pierwotnych.

Argument funkcji może mieć wartość domyślną, na przykład:

 fun add(x: Int, y: Int = 1) : Int { return x + y; }

W takim przypadku funkcję add() można wywołać, przekazując tylko argument x . Odpowiednikiem kodu Java byłoby:

 int add(int x) { Return add(x, 1); } int add(int x, int y) { return x + y; }

Inną fajną rzeczą podczas wywoływania funkcji jest to, że można użyć nazwanych argumentów. Na przykład:

 add(y = 12, x = 5)

Więcej informacji o funkcjach znajdziesz w oficjalnej dokumentacji.

Używanie wyrażeń lambda w Kotlin

Wyrażenia lambda w Kotlinie mogą być postrzegane jako funkcje anonimowe w Javie, ale z bardziej zwięzłą składnią. Jako przykład pokażmy, jak zaimplementować odbiornik kliknięć w Javie i Kotlinie.

W Javie:

 view.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast.makeText(v.getContext(), "Clicked on view", Toast.LENGTH_SHORT).show(); } };

W Kotlinie:

 view.setOnClickListener({ view -> toast("Click") })

Wow! Tylko jedna linijka kodu! Widzimy, że wyrażenie lambda jest otoczone nawiasami klamrowymi. Parametry są deklarowane jako pierwsze, a treść znajduje się po znaku -> . W przypadku odbiornika kliknięć nie określono parametru widoku, ponieważ można go wywnioskować. Body to po prostu wywołanie funkcji toast() w celu pokazania toastu, którą zapewnia Kotlin.

Ponadto, jeśli parametry nie są używane, możemy je pominąć:

 view.setOnClickListener({ toast("Click") })

Kotlin zoptymalizował biblioteki Java, a każda funkcja, która otrzymuje interfejs z jedną metodą dla argumentu, może zostać wywołana z argumentem funkcji (zamiast interfejsu).

Ponadto, jeśli funkcja jest ostatnim parametrem, można ją usunąć z nawiasów:

 view.setOnClickListener() { toast("Click") }

Na koniec, jeśli funkcja ma tylko jeden parametr, który jest funkcją, można pominąć nawiasy:

 view.setOnClickListener { toast("Click") }

Aby uzyskać więcej informacji, zapoznaj się z książką dla programistów Kotlin dla Androida autorstwa Antonio Leivy i oficjalną dokumentacją.

Funkcje rozszerzeń

Kotlin, podobnie jak C#, daje możliwość rozszerzenia istniejących klas o nową funkcjonalność za pomocą funkcji rozszerzeń. Na przykład metoda rozszerzająca, która oblicza skrót String ciągu :

 fun String.md5(): ByteArray { val digester = MessageDigest.getInstance("MD5") digester.update(this.toByteArray(Charset.defaultCharset())) return digester.digest() }

Zauważ, że nazwa funkcji jest poprzedzona nazwą klasy rozszerzonej (w tym przypadku String ), a instancja klasy rozszerzonej jest dostępna za pośrednictwem this słowa kluczowego.

Funkcje rozszerzające są odpowiednikami funkcji narzędziowych języka Java. Przykładowa funkcja w Javie wyglądałaby tak:

 public static int toNumber(String instance) { return Integer.valueOf(instance); }

Przykładowa funkcja musi być umieszczona w klasie Utility. Oznacza to, że funkcje rozszerzające nie modyfikują oryginalnej klasy rozszerzonej, ale są wygodnym sposobem pisania metod narzędziowych.

Cecha nr 5: Zerowe bezpieczeństwo

Jedną z rzeczy, którymi zajmujesz się najbardziej w Javie, jest prawdopodobnie NullPointerException . Null-safety to funkcja, która została zintegrowana z językiem Kotlin i jest tak niejawna, że ​​zwykle nie musisz się o nią martwić. Oficjalna dokumentacja stwierdza, że ​​jedynymi możliwymi przyczynami NullPointerExceptions są:

  • Jawne wywołanie, aby NullPointerException .
  • Korzystanie z !! operator (co wyjaśnię później).
  • Zewnętrzny kod Java.
  • Jeśli dostęp do właściwości lateinit jest uzyskiwany w konstruktorze przed jej zainicjowaniem, zostanie zgłoszony UninitializedPropertyAccessException .

Domyślnie wszystkie zmienne i właściwości w Kotlinie są uważane za non-null (nie można przechowywać wartości null ), jeśli nie są wyraźnie zadeklarowane jako dopuszczające wartość null. Jak już wspomniano, aby zdefiniować zmienną akceptującą wartość null , po typie należy dodać znak zapytania. Na przykład:

 val number: Int? = null

Należy jednak pamiętać, że poniższy kod nie skompiluje się:

 val number: Int? = null number.toString()

Dzieje się tak, ponieważ kompilator wykonuje kontrole null . Aby skompilować, należy dodać sprawdzenie null :

 val number: Int? = null if(number != null) { number.toString(); }

Ten kod zostanie skompilowany pomyślnie. To, co Kotlin robi w tle, w tym przypadku, to ta number staje się nun-null ( Int zamiast Int? ) wewnątrz bloku if.

Sprawdzanie null można uprościć za pomocą operatora bezpiecznego połączenia ( ?. ):

 val number: Int? = null number?.toString()

Druga linia zostanie wykonana tylko wtedy, gdy liczba nie jest null . Możesz nawet użyć słynnego operatora Elvisa ( ?: ):

 val number Int? = null val stringNumber = number?.toString() ?: "Number is null"

Jeśli wyrażenie po lewej stronie ?: nie jest null , jest oceniane i zwracane. W przeciwnym razie zwracany jest wynik wyrażenia po prawej stronie. Kolejną fajną rzeczą jest to, że możesz użyć throw lub return po prawej stronie operatora Elvisa, ponieważ są to wyrażenia w Kotlinie. Na przykład:

 fun sendMailToUser(user: User) { val email = user?.email ?: throw new IllegalArgumentException("User email is null") //... }

Ten !! Operator

Jeśli chcesz, aby NullPointerException wyrzucony w taki sam sposób, jak w Javie, możesz to zrobić za pomocą !! operator. Poniższy kod zgłosi NullPointerException :

 val number: Int? = null number!!.toString()

Odlew

Przesyłanie zakończono za pomocą słowa kluczowego as :

 val x: String = y as String

Jest to uważane za „niebezpieczne” rzutowanie, ponieważ spowoduje ClassCastException , jeśli rzutowanie nie jest możliwe, tak jak robi to Java. Istnieje operator rzutowania „Bezpieczny”, który zwraca wartość null zamiast zgłaszania wyjątku:

 val x: String = y as? String

Aby uzyskać więcej informacji na temat rzucania, sprawdź sekcję Typ odlewy i odlewy w oficjalnej dokumentacji, a po więcej szczegółów na temat bezpieczeństwa null sprawdź sekcję Null-Bezpieczeństwo.

właściwości lateinit

Istnieje przypadek, w którym użycie właściwości lateinit może spowodować wyjątek podobny do NullPointerException . Rozważ następującą klasę:

 class InitTest { lateinit var s: String; init { val len = this.s.length } }

Ten kod skompiluje się bez ostrzeżenia. Jednak zaraz po utworzeniu wystąpienia TestClass zostanie zgłoszony UninitializedPropertyAccessException , ponieważ właściwość s jest dostępna przed jej zainicjowaniem.

Cecha #6: Funkcja with()

Funkcja with() jest przydatna i pochodzi ze standardowej biblioteki Kotlin. Można go użyć, aby zaoszczędzić trochę pisania, jeśli potrzebujesz dostępu do wielu właściwości obiektu. Na przykład:

 with(helloWorldTextView) { text = "Hello World!" visibility = View.VISIBLE }

Jako parametry otrzymuje obiekt i funkcję rozszerzenia. Blok kodu (w nawiasach klamrowych) to wyrażenie lambda dla funkcji rozszerzenia obiektu określonego jako pierwszy parametr.

Cecha nr 7: Przeciążenie operatora

Dzięki Kotlin można dostarczyć niestandardowe implementacje dla predefiniowanego zestawu operatorów. Aby zaimplementować operator, należy podać funkcję członkowską lub funkcję rozszerzenia o podanej nazwie.

Na przykład, aby zaimplementować operator mnożenia, należy podać funkcję członkowską lub funkcję rozszerzenia o nazwie times(argument) :

 operator fun String.times(b: Int): String { val buffer = StringBuffer() for (i in 1..b) { buffer.append(this) } return buffer.toString() }

Powyższy przykład pokazuje implementację binarnego operatora * na String . Na przykład następujące wyrażenie przypisze wartość „TestTestTestTest” do zmiennej newString :

 val newString = "Test" * 4

Ponieważ można używać funkcji rozszerzających, oznacza to, że można zmienić domyślne zachowanie operatorów dla wszystkich obiektów. Jest to miecz obosieczny i należy go używać ostrożnie. Lista nazw funkcji dla wszystkich operatorów, które mogą być przeciążone, znajduje się w oficjalnej dokumentacji.

Kolejną dużą różnicą w porównaniu z Javą są operatory == i != . Operator == przekłada się na:

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

Podczas gdy operator != przekłada się na:

 !(a?.equals(b) ?:

Oznacza to, że użycie == nie powoduje sprawdzenia tożsamości jak w Javie (porównaj, jeśli instancje obiektu są takie same), ale zachowuje się tak samo jak metoda equals() wraz ze sprawdzaniem null .

Aby przeprowadzić kontrolę tożsamości, w Kotlinie muszą być użyte operatory === i !== .

Cecha nr 8: Delegowane właściwości

Niektóre właściwości mają pewne wspólne zachowania. Na przykład:

  • Właściwości zainicjowane z opóźnieniem, które są inicjowane przy pierwszym dostępie.
  • Właściwości implementujące Observable we wzorcu Observer.
  • Właściwości, które są przechowywane na mapie zamiast jako oddzielne pola.

Aby ułatwić wdrożenie takich przypadków, Kotlin obsługuje delegowane właściwości :

 class SomeClass { var p: String by Delegate() }

Oznacza to, że funkcje pobierające i ustawiające dla właściwości p są obsługiwane przez instancję innej klasy, Delegate .

Przykład delegata dla właściwości String :

 class Delegate { operator fun getValue(thisRef: Any?, property: KProperty<*>): String { return "$thisRef, thank you for delegating '${property.name}' to me!" } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { println("$value has been assigned to '${property.name} in $thisRef.'") } }

Powyższy przykład wyświetla komunikat, gdy właściwość jest przypisywana lub odczytywana.

Delegaty można tworzyć dla właściwości mutable ( var ) i tylko do odczytu ( val ).

W przypadku właściwości tylko do odczytu należy zaimplementować metodę getValue . Przyjmuje dwa parametry (zaczerpnięte z oficjalnej dokumentacji):

  • odbiorca - musi być tym samym lub nadtypem właściciela właściwości (w przypadku właściwości rozszerzających jest to typ, który jest rozszerzany).
  • metadane - muszą być typu KProperty<*> lub jego nadtypu.

Ta funkcja musi zwracać ten sam typ co właściwość lub jej podtyp.

W przypadku właściwości mutable delegat musi dodatkowo udostępnić funkcję o nazwie setValue , która przyjmuje następujące parametry:

  • odbiornik - tak samo jak dla getValue() .
  • metadane - tak samo jak w przypadku getValue() .
  • nowa wartość — musi być tego samego typu co właściwość lub jej nadtyp.

Istnieje kilka standardowych delegatów dołączonych do Kotlina, które obejmują najczęstsze sytuacje:

  • Leniwy
  • Zauważalny
  • Możliwość weta

Leniwy

Lazy to standardowy delegat, który przyjmuje wyrażenie lambda jako parametr. Przekazane wyrażenie lambda jest wykonywane przy pierwszym wywołaniu metody getValue() .

Domyślnie ocena właściwości opóźnionych jest synchronizowana. Jeśli nie interesuje Cię wielowątkowość, możesz użyć lazy(LazyThreadSafetyMode.NONE) { … } , aby uzyskać dodatkową wydajność.

Zauważalny

Delegates.observable() jest dla właściwości, które powinny zachowywać się jak Observables we wzorcu Observer. Przyjmuje dwa parametry, wartość początkową i funkcję, która ma trzy argumenty (właściwość, stara wartość i nowa wartość).

Podane wyrażenie lambda będzie wykonywane przy każdym wywołaniu metody setValue() :

 class User { var email: String by Delegates.observable("") { prop, old, new -> //handle the change from old to new value } }

Możliwość weta

Ten standardowy delegat jest specjalnym rodzajem Observable, który pozwala zdecydować, czy nowa wartość przypisana do właściwości będzie przechowywana, czy nie. Może służyć do sprawdzania niektórych warunków przed przypisaniem wartości. Podobnie jak w przypadku Delegates.observable() , przyjmuje dwa parametry: wartość początkową i funkcję.

Różnica polega na tym, że funkcja zwraca wartość logiczną. Jeśli zwróci true , nowa wartość przypisana do właściwości zostanie zapisana lub w inny sposób odrzucona.

 var positiveNumber = Delegates.vetoable(0) { d, old, new -> new >= 0 }

W podanym przykładzie będą przechowywane tylko liczby dodatnie, które są przypisane do właściwości.

Więcej szczegółów znajdziesz w oficjalnej dokumentacji.

Cecha nr 9: Mapowanie obiektu na mapę

Typowym przypadkiem użycia jest przechowywanie wartości właściwości wewnątrz mapy. Dzieje się tak często w aplikacjach, które współpracują z interfejsami API RESTful i analizują obiekty JSON. W takim przypadku wystąpienia mapy można użyć jako delegata dla delegowanej właściwości. Przykład z oficjalnej dokumentacji:

 class User(val map: Map<String, Any?>) { val name: String by map val age: Int by map }

W tym przykładzie User ma konstruktora podstawowego, który pobiera mapę. Dwie właściwości przyjmą wartości z mapy, które są mapowane pod klucze, które są równe nazwom właściwości:

 val user = User(mapOf( "name" to "John Doe", "age" to 25 ))

Własności name nowej instancji użytkownika zostanie przypisana wartość „Jan Kowalski”, a właściwości wiek wartość 25.

Działa to również dla właściwości var w połączeniu z MutableMap :

 class MutableUser(val map: MutableMap<String, Any?>) { var name: String by map var age: Int by map }

Cecha nr 10: Kolekcje i operacje funkcjonalne

Dzięki wsparciu dla lambd w Kotlinie kolekcje mogą wznieść się na nowy poziom.

Przede wszystkim Kotlin rozróżnia kolekcje mutowalne i niezmienne. Na przykład istnieją dwie wersje interfejsu Iterable :

  • Iterowalny
  • Zmienne iterowalne

To samo dotyczy interfejsów Collection , List , Set i Map .

Na przykład ta any operacja zwraca true , jeśli przynajmniej jeden element pasuje do podanego predykatu:

 val list = listOf(1, 2, 3, 4, 5, 6) assertTrue(list.any { it % 2 == 0 })

Obszerną listę operacji funkcjonalnych, które można wykonać na kolekcjach, znajdziesz w tym wpisie na blogu.

Wniosek

Właśnie zarysowaliśmy powierzchnię tego, co oferuje Kotlin. Dla zainteresowanych dalszą lekturą i nauką więcej sprawdź:

  • Wpisy na blogu i książka Antonio Leivy Kotlin.
  • Oficjalna dokumentacja i samouczki od JetBrains.

Podsumowując, Kotlin oferuje Ci możliwość zaoszczędzenia czasu podczas pisania natywnych aplikacji na Androida dzięki zastosowaniu intuicyjnej i zwięzłej składni. Jest to wciąż młody język programowania, ale moim zdaniem jest teraz wystarczająco stabilny, aby można go było używać do budowania aplikacji produkcyjnych.

Korzyści ze stosowania Kotlina:

  • Wsparcie przez Android Studio jest płynne i doskonałe.
  • Łatwo jest przekonwertować istniejący projekt Java na Kotlin.
  • Kod Java i Kotlin mogą współistnieć w tym samym projekcie.
  • W aplikacji nie ma narzutu na prędkość.

Wady:

  • Kotlin doda swoje biblioteki do wygenerowanego .apk , więc ostateczny rozmiar .apk będzie większy o około 300 KB.
  • W przypadku nadużycia przeciążenie operatora może prowadzić do nieczytelnego kodu.
  • IDE i Autouzupełnianie zachowują się nieco wolniej podczas pracy z Kotlin niż w przypadku czystych projektów Java Android.
  • Czasy kompilacji mogą być nieco dłuższe.