(Desteği sonlandırıldı) Kotlin'e dönüştürme

1. Hoş geldiniz!

Bu kod laboratuvarında, kodunuzu Java'dan Kotlin'e nasıl dönüştüreceğinizi öğreneceksiniz. Ayrıca Kotlin dil kurallarının ne olduğunu ve yazdığınız kodun bu kurallara uymasını nasıl sağlayacağınızı da öğreneceksiniz.

Bu codelab, Java kullanan ve projelerini Kotlin'e taşımayı düşünen tüm geliştiricilere uygundur. IDE'yi kullanarak Kotlin'e dönüştüreceğiniz birkaç Java sınıfıyla başlayacağız. Ardından, dönüştürülmüş koda göz atıp daha anlaşılır hale getirerek ve yaygın hatalardan kaçınarak nasıl iyileştirebileceğimizi göreceğiz.

Öğrenecekleriniz

Java'yı Kotlin'e nasıl dönüştüreceğinizi öğreneceksiniz. Bu süreçte aşağıdaki Kotlin dili özelliklerini ve kavramlarını öğreneceksiniz:

  • Null değer alabilme özelliğini yönetme
  • Tekil nesneleri uygulama
  • Veri sınıfları
  • Dizeleri işleme
  • Elvis operatörü
  • Yapıyı bozma
  • Mülkler ve destekleyici mülkler
  • Varsayılan bağımsız değişkenler ve adlandırılmış parametreler
  • Koleksiyonlarla çalışma
  • Uzantı işlevleri
  • Üst düzey işlevler ve parametreler
  • let, apply, with ve run anahtar kelimeleri

Varsayımlar

Java hakkında bilgi sahibi olmanız gerekir.

İhtiyacınız olanlar

2. Hazırlanma

Yeni proje oluşturma

IntelliJ IDEA kullanıyorsanız Kotlin/JVM ile yeni bir Java projesi oluşturun.

Android Studio kullanıyorsanız Etkinlik Yok şablonuyla yeni bir proje oluşturun. Proje dili olarak Kotlin'i seçin. Minimum SDK herhangi bir değer olabilir. Sonuçları etkilemez.

Kod

Bir User model nesnesi ve User nesneleriyle çalışan, kullanıcı listelerini ve biçimlendirilmiş kullanıcı adlarını gösteren bir Repository tekil sınıfı oluşturacağız.

app/java/<paketadınız> altında User.java adlı yeni bir dosya oluşturun ve aşağıdaki kodu yapıştırın:

public class User {

    @Nullable
    private String firstName;
    @Nullable
    private String lastName;

    public User(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = 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;
    }

}

IDE'nizin @Nullable değerinin tanımlanmadığını bildirdiğini görürsünüz. Bu nedenle, Android Studio kullanıyorsanız androidx.annotation.Nullable dosyasını, IntelliJ kullanıyorsanız org.jetbrains.annotations.Nullable dosyasını içe aktarın.

Repository.java adlı yeni bir dosya oluşturun ve aşağıdaki kodu yapıştırın:

import java.util.ArrayList;
import java.util.List;

public class Repository {

    private static Repository INSTANCE = null;

    private List<User> users = null;

    public static Repository getInstance() {
        if (INSTANCE == null) {
            synchronized (Repository.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Repository();
                }
            }
        }
        return INSTANCE;
    }

    // keeping the constructor private to enforce the usage of getInstance
    private Repository() {

        User user1 = new User("Jane", "");
        User user2 = new User("John", null);
        User user3 = new User("Anne", "Doe");

        users = new ArrayList();
        users.add(user1);
        users.add(user2);
        users.add(user3);
    }

    public List<User> getUsers() {
        return users;
    }

    public List<String> getFormattedUserNames() {
        List<String> userNames = new ArrayList<>(users.size());
        for (User user : users) {
            String name;

            if (user.getLastName() != null) {
                if (user.getFirstName() != null) {
                    name = user.getFirstName() + " " + user.getLastName();
                } else {
                    name = user.getLastName();
                }
            } else if (user.getFirstName() != null) {
                name = user.getFirstName();
            } else {
                name = "Unknown";
            }
            userNames.add(name);
        }
        return userNames;
    }
}

3. Boşluk, val, var ve veri sınıflarını bildirme

IDE'miz, Java kodunu Kotlin koduna otomatik olarak dönüştürme konusunda oldukça iyi bir iş çıkarabilir ancak bazen biraz yardıma ihtiyacı olur. IDE'mizin dönüşüm için ilk geçişini yapalım. Ardından, nasıl ve neden bu şekilde dönüştürüldüğünü anlamak için ortaya çıkan kodu inceleyeceğiz.

User.java dosyasına gidin ve dosyayı Kotlin'e dönüştürün: Menü çubuğu -> Kod -> Java Dosyasını Kotlin Dosyasına Dönüştür.

IDE'niz dönüşümden sonra düzeltme isteğinde bulunursa Evet'e basın.

e6f96eace5dabe5f.png

Aşağıdaki Kotlin kodunu görürsünüz:

class User(var firstName: String?, var lastName: String?)

User.java, User.kt olarak yeniden adlandırıldı. Kotlin dosyalarının uzantısı .kt'dir.

Java User sınıfımızda iki özelliğimiz vardı: firstName ve lastName. Her birinin bir alıcı ve ayarlayıcı yöntemi vardı. Bu da değerlerini değiştirilebilir hale getiriyordu. Kotlin'in değişken değişkenler için anahtar kelimesi var olduğundan dönüştürücü bu özelliklerin her biri için var kullanır. Java mülklerimizde yalnızca alıcı yöntemler olsaydı bunlar salt okunur olur ve val değişkenleri olarak tanımlanırdı. val, Java'daki final anahtar kelimesine benzer.

Kotlin ile Java arasındaki temel farklardan biri, Kotlin'in bir değişkenin boş değer kabul edip edemeyeceğini açıkça belirtmesidir. Bunu, tür beyanına bir ? ekleyerek yapar.

firstName ve lastName'ü boş değer kabul eden olarak işaretlediğimiz için otomatik dönüştürücü, mülkleri String? ile boş değer kabul eden olarak otomatik olarak işaretledi. Java üyelerinizi org.jetbrains.annotations.NotNull veya androidx.annotation.NonNull kullanarak null olmayan olarak ek açıklamalarla belirtirseniz dönüştürücü bunu tanır ve alanları Kotlin'de de null olmayan olarak ayarlar.

Temel dönüşüm zaten tamamlanmıştır. Ancak bunu daha doğal bir şekilde yazabiliriz. Nasıl yapacağınıza bakalım.

Veri sınıfı

User sınıfımız yalnızca veri içerir. Kotlin'de bu role sahip sınıflar için data anahtar kelimesi vardır. Bu sınıfı data sınıfı olarak işaretlediğimizde derleyici, bizim için otomatik olarak alıcı ve ayarlayıcı oluşturur. Ayrıca equals(), hashCode() ve toString() işlevleri de türetilir.

data anahtar kelimesini User sınıfımıza ekleyelim:

data class User(var firstName: String?, var lastName: String?)

Kotlin'de de Java'da olduğu gibi birincil bir kurucu ve bir veya daha fazla ikincil kurucu olabilir. Yukarıdaki örnekte gösterilen, User sınıfının birincil kurucusudur. Birden fazla kurucu yöntemi olan bir Java sınıfını dönüştürüyorsanız dönüştürücü, Kotlin'de de otomatik olarak birden fazla kurucu yöntemi oluşturur. Bunlar constructor anahtar kelimesi kullanılarak tanımlanır.

Bu sınıfın bir örneğini oluşturmak istersek bunu şu şekilde yapabiliriz:

val user1 = User("Jane", "Doe")

Eşitlik

Kotlin'de iki tür eşitlik vardır:

  • Yapısal eşitlik, == operatörünü kullanır ve iki örneğin eşit olup olmadığını belirlemek için equals()'ı çağırır.
  • Referanssal eşitlik, === operatörünü kullanır ve iki referansın aynı nesneyi gösterip göstermediğini kontrol eder.

Veri sınıfının birincil kurucusunda tanımlanan özellikler, yapısal eşitlik kontrolleri için kullanılır.

val user1 = User("Jane", "Doe")
val user2 = User("Jane", "Doe")
val structurallyEqual = user1 == user2 // true
val referentiallyEqual = user1 === user2 // false

4. Varsayılan bağımsız değişkenler, adlandırılmış bağımsız değişkenler

Kotlin'de, işlev çağrılarındaki bağımsız değişkenlere varsayılan değerler atayabiliriz. Parametre atlanmadığında varsayılan değer kullanılır. Kotlin'de kurucular da işlev olduğundan lastName değerinin varsayılan değerinin null olduğunu belirtmek için varsayılan bağımsız değişkenleri kullanabiliriz. Bunun için null değerini lastName değerine atamamız yeterlidir.

data class User(var firstName: String?, var lastName: String? = null)

// usage
val jane = User("Jane") // same as User("Jane", null)
val joe = User("Joe", "Doe")

Kotlin, işlevleriniz çağrılırken bağımsız değişkenlerinizi etiketlemenize olanak tanır:

val john = User(firstName = "John", lastName = "Doe") 

Farklı bir kullanım alanı olarak, firstName özelliğinin varsayılan değerinin null, lastName özelliğinin ise varsayılan değerinin olmadığını varsayalım. Bu durumda, varsayılan parametre varsayılan değeri olmayan bir parametreden önce geleceği için işlevi adlandırılmış bağımsız değişkenlerle çağırmanız gerekir:

data class User(var firstName: String? = null, var lastName: String?)

// usage
val jane = User(lastName = "Doe") // same as User(null, "Doe")
val john = User("John", "Doe")

Varsayılan değerler, Kotlin kodunda önemli ve sık kullanılan bir kavramdır. Codelab'imizde, User nesne beyanında her zaman ad ve soyadı belirtmek istediğimizden varsayılan değerlere ihtiyacımız yoktur.

5. Nesne başlatma, tamamlayıcı nesne ve tekil nesneler

Kod laboratuvarına devam etmeden önce User sınıfınızın data sınıfı olduğundan emin olun. Şimdi Repository sınıfını Kotlin'e dönüştürelim. Otomatik dönüşüm sonucu şu şekilde görünür:

import java.util.*

class Repository private constructor() {
    private var users: MutableList<User?>? = null
    fun getUsers(): List<User?>? {
        return users
    }

    val formattedUserNames: List<String?>
        get() {
            val userNames: MutableList<String?> =
                ArrayList(users!!.size)
            for (user in users) {
                var name: String
                name = if (user!!.lastName != null) {
                    if (user!!.firstName != null) {
                        user!!.firstName + " " + user!!.lastName
                    } else {
                        user!!.lastName
                    }
                } else if (user!!.firstName != null) {
                    user!!.firstName
                } else {
                    "Unknown"
                }
                userNames.add(name)
            }
            return userNames
        }

    companion object {
        private var INSTANCE: Repository? = null
        val instance: Repository?
            get() {
                if (INSTANCE == null) {
                    synchronized(Repository::class.java) {
                        if (INSTANCE == null) {
                            INSTANCE =
                                Repository()
                        }
                    }
                }
                return INSTANCE
            }
    }

    // keeping the constructor private to enforce the usage of getInstance
    init {
        val user1 = User("Jane", "")
        val user2 = User("John", null)
        val user3 = User("Anne", "Doe")
        users = ArrayList<Any?>()
        users.add(user1)
        users.add(user2)
        users.add(user3)
    }
}

Otomatik dönüştürücünün ne yaptığını görelim:

  • Nesne, beyan sırasında oluşturulmadığı için users listesi boş olabilir.
  • Kotlin'de getUsers() gibi işlevler fun değiştiricisiyle tanımlanır
  • getFormattedUserNames() yöntemi artık formattedUserNames adlı bir mülktür
  • Kullanıcı listesi üzerinde iterasyon (başlangıçta getFormattedUserNames('ün bir parçasıydı) Java'dakinden farklı bir söz dizimine sahiptir
  • static alanı artık bir companion object bloğunun parçası
  • init bloğu eklendi

Devam etmeden önce kodu biraz temizleyelim. Oluşturucuya bakarsak dönüştürücünün users listemizi, null değer alabilecek nesneler içeren bir değişken liste haline getirdiğini görürüz. Liste gerçekten null olabilir ancak null kullanıcılar içeremeyeceğini varsayalım. Aşağıdakileri yapalım:

  • users tür beyanı içindeki User? içinde ?'ü kaldırın
  • getUsers() döndürme türü için User?'teki ? öğesini kaldırarak List<User>? döndürülür.

Başlatma bloğu

Kotlin'de birincil kurucu herhangi bir kod içeremez. Bu nedenle, başlatma kodu init bloklarına yerleştirilir. İşlevler aynıdır.

class Repository private constructor() {
    ...
    init {
        val user1 = User("Jane", "")
        val user2 = User("John", null)
        val user3 = User("Anne", "Doe")
        users = ArrayList<Any?>()
        users.add(user1)
        users.add(user2)
        users.add(user3)
    }
}

init kodunun büyük kısmı, özellikleri başlatma işlemlerini yönetir. Bu işlem, mülkün beyanında da yapılabilir. Örneğin, Repository sınıfımızın Kotlin sürümünde, kullanıcılar özelliğinin beyanda başlatıldığını görüyoruz.

private var users: MutableList<User>? = null

Kotlin'in static özellikleri ve yöntemleri

Java'da, alanların veya işlevlerin bir sınıfa ait olduğunu ancak sınıfın bir örneğine ait olmadığını belirtmek için static anahtar kelimesini kullanırız. Bu nedenle, Repository sınıfımızda INSTANCE statik alanını oluşturduk. Bunun Kotlin eşdeğeri companion object bloğudur. Burada statik alanları ve statik işlevleri de tanımlarsınız. Dönüştürücü, tamamlayıcı nesne bloğunu oluşturdu ve INSTANCE alanını buraya taşıdı.

Tekil nesneleri işleme

Repository sınıfının yalnızca bir örneğine ihtiyacımız olduğu için Java'da tekil örnek kalıbını kullandık. Kotlin'de class anahtar kelimesini object ile değiştirerek bu kalıbı derleyici düzeyinde zorunlu kılabilirsiniz.

Özel oluşturucuyu kaldırın ve sınıf tanımını object Repository ile değiştirin. Tamamlayıcı nesneyi de kaldırın.

object Repository {

    private var users: MutableList<User>? = null
    fun getUsers(): List<User>? {
       return users
    }

    val formattedUserNames: List<String>
        get() {
            val userNames: MutableList<String> =
                ArrayList(users!!.size)
        for (user in users) {
            var name: String
            name = if (user!!.lastName != null) {
                if (user!!.firstName != null) {
                    user!!.firstName + " " + user!!.lastName
                } else {
                    user!!.lastName
                }
            } else if (user!!.firstName != null) {
                user!!.firstName
            } else {
                "Unknown"
            }
            userNames.add(name)
       }
       return userNames
   }

    // keeping the constructor private to enforce the usage of getInstance
    init {
        val user1 = User("Jane", "")
        val user2 = User("John", null)
        val user3 = User("Anne", "Doe")
        users = ArrayList<Any?>()
        users.add(user1)
        users.add(user2)
        users.add(user3)
    }
}

object sınıfını kullanırken işlevleri ve özellikleri doğrudan nesnede çağırırız. Örneğin:

val formattedUserNames = Repository.formattedUserNames

Bir mülkte görünürlük değiştirici yoksa varsayılan olarak herkese açık olduğunu unutmayın (Repository nesnesinde formattedUserNames mülkü gibi).

6. Null değer alabilme özelliğini yönetme

Otomatik dönüştürücü, Repository sınıfını Kotlin'e dönüştürürken kullanıcı listesi, tanımlanırken bir nesneyle başlatılmadığı için null olabilir hale geldi. Sonuç olarak, users nesnesinin tüm kullanımlarında, null olmayan beyan operatörü !!'ün kullanılması gerekir. (Dönüştürülmüş kodda users!! ve user!! karakterlerini görürsünüz.) !! operatörü, tüm değişkenleri boş olmayan bir türe dönüştürür. Böylece, bu değişkenlerdeki özelliklere erişebilir veya işlevleri çağırabilirsiniz. Ancak değişken değeri gerçekten null ise bir istisna atılır. !! kullanırken çalışma zamanında istisna atılma riski vardır.

Bunun yerine, aşağıdaki yöntemlerden birini kullanarak boşluk değerini işlemeye tercih verin:

  • Boşluk kontrolü yapma ( if (users != null) {...} )
  • Elvis operatörünü ?: kullanma (codelab'de daha sonra ele alınacaktır)
  • Kotlin standart işlevlerinden bazılarını kullanma (codelab'de daha sonra ele alınacaktır)

Bizim durumumuzda, nesne oluşturulduktan hemen sonra (init bloğunda) başlatıldığı için kullanıcı listesinin boş değer kabul edebileceği bir türde olması gerekmediğini biliyoruz. Böylece, users nesnesini tanımladığımızda doğrudan örneklendirebiliriz.

Kotlin, koleksiyon türlerinin örneklerini oluştururken kodunuzu daha okunaklı ve esnek hale getirmek için çeşitli yardımcı işlevler sağlar. Burada users için bir MutableList kullanıyoruz:

private var users: MutableList<User>? = null

Basitlik açısından mutableListOf() işlevini kullanabilir ve liste öğesi türünü sağlayabiliriz. mutableListOf<User>(), User nesnesi tutabilecek boş bir liste oluşturur. Değişkenin veri türü artık derleyici tarafından çıkarılabildiğinden users mülkünün açık tür tanımını kaldırın.

private val users = mutableListOf<User>()

Ayrıca, kullanıcılar kullanıcı listesine salt okuma referansı içereceğinden var değerini val olarak değiştirdik. Referansın salt okunur olduğunu, dolayısıyla hiçbir zaman yeni bir listeyi gösteremeyeceğini ancak listenin yine de değiştirilebilir olduğunu (öğe ekleyebilir veya kaldırabilirsiniz) unutmayın.

users değişkeni zaten başlatılmış olduğundan bu başlatmayı init bloğundan kaldırın:

users = ArrayList<Any?>()

Ardından init bloğu şu şekilde görünmelidir:

init {
    val user1 = User("Jane", "")
    val user2 = User("John", null)
    val user3 = User("Anne", "Doe")

    users.add(user1)
    users.add(user2)
    users.add(user3)
}

Bu değişikliklerle users mülkümüz artık boş değil ve gereksiz tüm !! operatör kullanımlarını kaldırabiliriz. Android Studio'da derleme hatalarını görmeye devam edeceğinizi unutmayın. Ancak bunları çözmek için codelab'lerin sonraki birkaç adımına geçin.

val userNames: MutableList<String?> = ArrayList(users.size)
for (user in users) {
    var name: String
    name = if (user.lastName != null) {
        if (user.firstName != null) {
            user.firstName + " " + user.lastName
        } else {
            user.lastName
        }
    } else if (user.firstName != null) {
        user.firstName
    } else {
        "Unknown"
    }
    userNames.add(name)
}

Ayrıca, userNames değeri için ArrayList türünü Strings tutan olarak belirtirseniz bu tür, varsayılacağı için beyandaki açık türü kaldırabilirsiniz.

val userNames = ArrayList<String>(users.size)

Yapı bozma

Kotlin, yapılandırmayı kaldırma beyanı adlı bir söz dizimi kullanarak bir nesnenin yapısını birden çok değişkene ayırmanıza olanak tanır. Birden fazla değişken oluşturur ve bunları bağımsız olarak kullanabiliriz.

Örneğin, data sınıfları yapı bozmayı destekler. Böylece for döngüsünde User nesnesini (firstName, lastName) olarak yapı bozabiliriz. Bu sayede doğrudan firstName ve lastName değerleriyle çalışabiliriz. for döngüsünü aşağıda gösterildiği gibi güncelleyin. Tüm user.firstName örneklerini firstName ile, user.lastName örneklerini ise lastName ile değiştirin.

for ((firstName, lastName) in users) {
    var name: String
    name = if (lastName != null) {
        if (firstName != null) {
            firstName + " " + lastName
        } else {
            lastName
        }
    } else if (firstName != null) {
        firstName
    } else {
        "Unknown"
    }
    userNames.add(name)
}

if ifadesi

userNames listesindeki adlar henüz istediğimiz biçimde değil. Hem lastName hem de firstName null olabilir. Bu nedenle, biçimlendirilmiş kullanıcı adları listesini oluştururken boşluk değerini ele almamız gerekir. Her iki ad da eksikse "Unknown" gösterilir. name değişkeni bir kez ayarlandıktan sonra değiştirilmeyeceğinden var yerine val kullanabiliriz. Önce bu değişikliği yapın.

val name: String

ad değişkenini ayarlayan koda göz atın. Bir değişkenin if / else kod bloğuna eşit olacak şekilde ayarlandığını görmek size yeni gelebilir. Kotlin'de if ve when ifade olduğu için (değer döndürdükleri için) buna izin verilir. if ifadesinin son satırı name'a atanır. Bu bloğun tek amacı name değerini başlatmaktır.

Burada sunulan mantık temel olarak, lastName null ise name'un firstName veya "Unknown" olarak ayarlanmasıdır.

name = if (lastName != null) {
    if (firstName != null) {
        firstName + " " + lastName
    } else {
        lastName
    }
} else if (firstName != null) {
    firstName
} else {
    "Unknown"
}

Elvis operatörü

Bu kod, elvis operatörü ?: kullanılarak daha doğal bir şekilde yazılabilir. Elvis operatörü, sol tarafı null değilse sol tarafındaki ifadeyi, sol tarafı null ise sağ tarafındaki ifadeyi döndürür.

Bu nedenle, aşağıdaki kodda firstName null değilse döndürülür. firstName null ise ifade, sağ taraftaki değeri ("Unknown") döndürür:

name = if (lastName != null) {
    ...
} else {
    firstName ?: "Unknown"
}

7. Dize şablonları

Kotlin, dize şablonları ile String ile çalışmayı kolaylaştırır. Dize şablonları, değişkenlerden önce $ simgesini kullanarak dize beyanları içindeki değişkenlere referans vermenize olanak tanır. Bir ifadeyi { } içine yerleştirip ifadenin önüne $ simgesini ekleyerek de bir dize beyanı içine yerleştirebilirsiniz. Örnek: ${user.firstName}.

Kodunuz şu anda firstName ve lastName değerlerini kullanıcı adıyla birleştirmek için dize birleştirme işlevini kullanıyor.

if (firstName != null) {
    firstName + " " + lastName
}

Bunun yerine, dize birleştirme işlemini şununla değiştirin:

if (firstName != null) {
    "$firstName $lastName"
}

Dize şablonları kullanmak, kodunuzu basitleştirebilir.

Kodunuzu yazmanın daha doğal bir yolu varsa IDE'niz size uyarı gösterir. Kodda kıvrımlı bir alt çizgi görürsünüz. Fareyle üzerine geldiğinizde, kodunuzu nasıl yeniden yapılandıracağınızla ilgili bir öneri görürsünüz.

Şu anda name beyanının ödeve eklenebilir olduğuna dair bir uyarı göreceksiniz. Bunu uygulayalım. name değişkeninin türü çıkarılabileceğinden, açık String türü beyanını kaldırabiliriz. formattedUserNames şu şekilde görünür:

val formattedUserNames: List<String?>
    get() {
        val userNames = ArrayList<String>(users.size)
        for ((firstName, lastName) in users) {
            val name = if (lastName != null) {
                if (firstName != null) {
                    "$firstName $lastName"
                } else {
                    lastName
                }
            } else {
                firstName ?: "Unknown"
            }
            userNames.add(name)
        }
        return userNames
    }

Bir değişiklik daha yapabiliriz. Kullanıcı arayüzü mantığımız, ad ve soyadı eksik olduğunda "Unknown" gösterir. Bu nedenle, null nesneleri desteklemiyoruz. Bu nedenle, formattedUserNames veri türü için List<String?> değerini List<String> ile değiştirin.

val formattedUserNames: List<String>

8. Koleksiyonlarla ilgili işlemler

formattedUserNames alıcısını daha yakından inceleyip nasıl daha doğal hale getirebileceğimizi görelim. Şu anda kod şu işlemleri yapıyor:

  • Yeni bir dize listesi oluşturur
  • Kullanıcı listesinde iterasyon yapar
  • Her kullanıcının adını, ad ve soyadına göre biçimlendirir.
  • Yeni oluşturulan listeyi döndürür
    val formattedUserNames: List<String>
        get() {
            val userNames = ArrayList<String>(users.size)
            for ((firstName, lastName) in users) {
                val name = if (lastName != null) {
                    if (firstName != null) {
                        "$firstName $lastName"
                    } else {
                        lastName
                    }
                } else {
                    firstName ?: "Unknown"
                }
                userNames.add(name)
            }
            return userNames
        }

Kotlin, Java Collections API'nin özelliklerini genişleterek geliştirmeyi daha hızlı ve daha güvenli hale getiren kapsamlı bir koleksiyon dönüşümleri listesi sunar. Bunlardan biri map işlevidir. Bu işlev, orijinal listedeki her öğeye belirtilen dönüştürme işlevinin uygulanmasının sonuçlarını içeren yeni bir liste döndürür. Bu nedenle, yeni bir liste oluşturmak ve kullanıcı listesini manuel olarak iterlemek yerine map işlevini kullanabilir ve for döngüsünde bulunan mantığı map gövdesine taşıyabiliriz. Varsayılan olarak, map içinde kullanılan mevcut liste öğesinin adı it'dir ancak okunabilirlik için it'yi kendi değişken adınızla değiştirebilirsiniz. Bu örnekte, user olarak adlandıralım:

val formattedUserNames: List<String>
        get() {
            return users.map { user ->
                val name = if (user.lastName != null) {
                    if (user.firstName != null) {
                        "${user.firstName} ${user.lastName}"
                    } else {
                        user.lastName ?: "Unknown"
                    }
                }  else {
                    user.firstName ?: "Unknown"
                }
                name
            }
        }

user.lastName String? türüne sahip olduğundan ve name için String gerekli olduğundan, user.lastName null ise "Unknown" döndürmek için Elvis operatörünü kullandığımızı unutmayın.

...
else {
    user.lastName ?: "Unknown"
}
...

Bunu daha da basitleştirmek için name değişkenini tamamen kaldırabiliriz:

val formattedUserNames: List<String>
        get() {
            return users.map { user ->
                if (user.lastName != null) {
                    if (user.firstName != null) {
                        "${user.firstName} ${user.lastName}"
                    } else {
                        user.lastName ?: "Unknown"
                    }
                }  else {
                    user.firstName ?: "Unknown"
                }
            }
        }

9. Mülkler ve destekleyici mülkler

Otomatik dönüştürücünün, getFormattedUserNames() işlevini özel bir alıcıya sahip formattedUserNames adlı bir mülk ile değiştirdiğini gördük. Kotlin, arka planda List döndüren bir getFormattedUserNames() yöntemi oluşturur.

Java'da sınıf özelliklerimizi getter ve setter işlevleri aracılığıyla gösteririz. Kotlin, bir sınıfın alanlarla ifade edilen özellikleri ile işlevleri (bir sınıfın yapabileceği işlemler) arasında daha iyi bir ayrım yapmamıza olanak tanır. Bizim durumumuzda Repository sınıfı çok basittir ve herhangi bir işlem yapmaz. Bu nedenle yalnızca alanlara sahiptir.

Java getFormattedUserNames() işlevinde tetiklenen mantık, artık formattedUserNames Kotlin mülkünün alıcı işlevi çağrılırken tetikleniyor.

formattedUserNames mülküne karşılık gelen açık bir alanımız olmasa da Kotlin bize field adlı otomatik bir destek alanı sağlar. Gerekirse özel alıcı ve ayarlayıcılardan bu alana erişebiliriz.

Ancak bazen otomatik yedek alanın sağlamadığı bazı ek işlevler isteriz.

Bir örnek üzerinden gidelim.

Repository sınıfımızda, Java kodumuzdan oluşturulan getUsers() işlevinde gösterilen, değiştirilebilir bir kullanıcı listesi vardır:

fun getUsers(): List<User>? {
    return users
}

Repository sınıfını çağıran kullanıcıların kullanıcı listesini değiştirmesini istemediğimiz için salt okunur bir List<User> döndüren getUsers() işlevini oluşturduk. Kotlin'de bu tür durumlarda işlevler yerine özellikleri kullanmayı tercih ederiz. Daha açık belirtmek gerekirse, mutableListOf<User> tarafından desteklenen salt okunur bir List<User> sunarız.

Öncelikle users dosyasını _users olarak yeniden adlandıralım. Değişken adını vurgulayın, sağ tıklayarak değişkeni yeniden yapılandırın > yeniden adlandırın. Ardından, kullanıcıların listesini döndüren herkese açık bir salt okunur mülk ekleyin. Bu değişkeni users olarak adlandıralım:

private val _users = mutableListOf<User>()
val users: List<User>
    get() = _users

Bu noktada getUsers() yöntemini silebilirsiniz.

Yukarıdaki değişiklikle birlikte, gizli _users mülkü herkese açık users mülkünün destek mülkü olur. Sınıfın tüketicileri listeye yalnızca users aracılığıyla erişebildiğinden, Repository sınıfının dışında _users listesi değiştirilemez.

users, Kotlin kodundan çağrıldığında Kotlin Standart Kitaplığı'ndaki List uygulaması kullanılır. Bu uygulamada liste değiştirilemez. users Java'dan çağrılırsa listenin değiştirilebilir olduğu ve add() ve remove() gibi işlemlerin kullanılabildiği java.util.List uygulaması kullanılır.

Tam kod:

object Repository {

    private val _users = mutableListOf<User>()
    val users: List<User>
        get() = _users

    val formattedUserNames: List<String>
        get() {
            return _users.map { user ->
                if (user.lastName != null) {
                    if (user.firstName != null) {
                        "${user.firstName} ${user.lastName}"
                    } else {
                        user.lastName ?: "Unknown"
                    }
                }  else {
                    user.firstName ?: "Unknown"
                }
            }
        }

    init {
        val user1 = User("Jane", "")
        val user2 = User("John", null)
        val user3 = User("Anne", "Doe")

        _users.add(user1)
        _users.add(user2)
        _users.add(user3)
    }
}

10. Üst düzey ve uzantı işlevleri ve özellikleri

Şu anda Repository sınıfı, User nesnesi için biçimlendirilmiş kullanıcı adını nasıl hesaplayacağını biliyor. Ancak aynı biçimlendirme mantığını diğer sınıflarda yeniden kullanmak istersek bunu kopyalayıp yapıştırmamız veya User sınıfına taşımamız gerekir.

Kotlin, işlevleri ve özellikleri herhangi bir sınıf, nesne veya arayüzün dışında tanımlama olanağı sunar. Örneğin, List örneğini oluşturmak için kullandığımız mutableListOf() işlevi, Kotlin Standart Kitaplığı'ndaki Collections.kt içinde zaten tanımlanmıştır.

Java'da, bir yardımcı program işlevine ihtiyaç duyduğunuzda büyük olasılıkla bir Util sınıfı oluşturur ve bu işlevi statik bir işlev olarak tanımlarsınız. Kotlin'de sınıf olmadan üst düzey işlevler tanımlayabilirsiniz. Ancak Kotlin, uzantı işlevleri oluşturma olanağı da sunar. Bunlar, belirli bir türü genişleten ancak türün dışında tanımlanan işlevlerdir.

Görünürlük değiştiriciler kullanılarak uzantı işlevlerinin ve özelliklerinin görünürlüğü kısıtlanabilir. Bunlar, kullanımı yalnızca uzantılara ihtiyaç duyan sınıflarla kısıtlar ve ad alanını kirletmez.

User sınıfı için biçimlendirilmiş adı hesaplayan bir uzantı işlevi ekleyebilir veya biçimlendirilmiş adı bir uzantı mülkünde tutabiliriz. Repository sınıfının dışında, aynı dosyaya eklenebilir:

// extension function
fun User.getFormattedName(): String {
    return if (lastName != null) {
        if (firstName != null) {
            "$firstName $lastName"
        } else {
            lastName ?: "Unknown"
        }
    } else {
        firstName ?: "Unknown"
    }
}

// extension property
val User.userFormattedName: String
    get() {
        return if (lastName != null) {
            if (firstName != null) {
                "$firstName $lastName"
            } else {
                lastName ?: "Unknown"
            }
        } else {
            firstName ?: "Unknown"
        }
    }

// usage:
val user = User(...)
val name = user.getFormattedName()
val formattedName = user.userFormattedName

Ardından, uzantı işlevlerini ve özelliklerini User sınıfının bir parçasıymış gibi kullanabiliriz.

Formatlanmış ad, Repository sınıfının bir işlevi değil, User sınıfının bir özelliği olduğundan uzantı özelliğini kullanalım. Repository dosyamız şu şekilde görünür:

val User.formattedName: String
    get() {
        return if (lastName != null) {
            if (firstName != null) {
                "$firstName $lastName"
            } else {
                lastName ?: "Unknown"
            }
        } else {
            firstName ?: "Unknown"
        }
    }

object Repository {

    private val _users = mutableListOf<User>()
    val users: List<User>
      get() = _users

    val formattedUserNames: List<String>
        get() {
            return _users.map { user -> user.formattedName }
        }

    init {
        val user1 = User("Jane", "")
        val user2 = User("John", null)
        val user3 = User("Anne", "Doe")

        _users.add(user1)
        _users.add(user2)
        _users.add(user3)
    }
}

Kotlin Standart Kitaplığı, çeşitli Java API'lerinin işlevini genişletmek için uzantı işlevleri kullanır. Iterable ve Collection'teki işlevlerin çoğu uzantı işlevi olarak uygulanır. Örneğin, önceki bir adımda kullandığımız map işlevi, Iterable üzerinde bir uzantı işlevidir.

11. Kapsam işlevleri: let, apply, with, run, also

Repository sınıf kodumuzda, _users listesine birkaç User nesnesi ekliyoruz. Bu çağrılar, Kotlin kapsam işlevlerinin yardımıyla daha doğal hale getirilebilir.

Kotlin, koda yalnızca belirli bir nesnenin bağlamında, nesneye adına göre erişmek zorunda kalmadan çalıştırmak için 5 kapsam işlevi sunar: let, apply, with, run ve also. Bu işlevler, kodunuzun okunmasını kolaylaştırır ve daha kısa hale getirir. Tüm kapsam işlevlerinin bir alıcı (this) vardır, bir bağımsız değişkeni (it) olabilir ve bir değer döndürebilir.

Her işlevi ne zaman kullanacağınızı hatırlamanıza yardımcı olacak kullanışlı bir yardım sayfası aşağıda verilmiştir:

6b9283d411fb6e7b.png

Repository'ımızda _users nesnemizi yapılandırdığımız için apply işlevini kullanarak kodu daha doğal hale getirebiliriz:

init {
    val user1 = User("Jane", "")
    val user2 = User("John", null)
    val user3 = User("Anne", "Doe")
   
    _users.apply {
       // this == _users
       add(user1)
       add(user2)
       add(user3)
    }
 }

12. Son adım

Bu codelab'de, kodunuzu Java'dan Kotlin'e dönüştürmeye başlamak için ihtiyacınız olan temel bilgileri ele aldık. Bu dönüşüm, geliştirme platformunuzdan bağımsızdır ve yazdığınız kodun idiomatik Kotlin olmasını sağlar.

Deyimsel Kotlin, kod yazmayı kısa ve öz hale getirir. Kotlin'in sunduğu tüm özellikler sayesinde kodunuzu daha güvenli, daha kısa ve daha okunaklı hale getirmenin birçok yolu vardır. Örneğin, _users listesini doğrudan kullanıcılarla birlikte beyan içinde örneklendirerek init bloğundan kurtulup Repository sınıfımızı optimize edebiliriz:

private val users = mutableListOf(User("Jane", ""), User("John", null), User("Anne", "Doe"))

Boşluk, tekil, dize ve koleksiyonları işlemekten uzantı işlevleri, üst düzey işlevler, özellikler ve kapsam işlevleri gibi konulara kadar çok çeşitli konuları ele aldık. İki Java sınıfından iki Kotlin sınıfına geçtik. Bu sınıflar şu şekilde görünüyor:

User.kt

data class User(var firstName: String?, var lastName: String?)

Repository.kt

val User.formattedName: String
    get() {
       return if (lastName != null) {
            if (firstName != null) {
                "$firstName $lastName"
            } else {
                lastName ?: "Unknown"
            }
        } else {
            firstName ?: "Unknown"
        }
    }

object Repository {

    private val _users = mutableListOf(User("Jane", ""), User("John", null), User("Anne", "Doe"))
    val users: List<User>
        get() = _users

    val formattedUserNames: List<String>
        get() = _users.map { user -> user.formattedName }
}

Java işlevlerinin ve bunların Kotlin ile eşlenmesinin özetini aşağıda bulabilirsiniz:

Java

Kotlin

final nesnesi

val nesnesi

equals()

==

==

===

Yalnızca veri içeren sınıf

data sınıf

Oluşturucuda başlatma

init bloğunda başlatma

static alanları ve işlevleri

companion object içinde tanımlanan alanlar ve işlevler

Singleton sınıfı

object

Kotlin ve platformunuzda nasıl kullanılacağı hakkında daha fazla bilgi edinmek için şu kaynaklara göz atın: