1. Hoş geldiniz!
Bu codelab'de kodunuzu Java'dan Kotlin'e nasıl dönüştüreceğinizi öğreneceksiniz. Ayrıca Kotlin dil kurallarının neler olduğunu ve yazdığınız kodun bu kurallara uyduğundan nasıl emin olacağınızı da öğreneceksiniz.
Bu codelab, projesini Kotlin'e taşımayı düşünen ve Java kullanan tüm geliştiriciler için 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ülen koda göz atıp kodu daha idiomatik hale getirerek ve yaygın sorunlardan kaçınarak nasıl iyileştirebileceğimizi göreceğiz.
Öğrenecekleriniz
Java'yı Kotlin'e dönüştürmeyi öğreneceksiniz. Bu alıştırmayı yaparken aşağıdaki Kotlin dil özelliklerini ve kavramlarını öğreneceksiniz:
- Null değer alabilme özelliğini işleme
- Tekil öğeleri uygulama
- Veri sınıfları
- Dizeleri işleme
- Elvis operatörü
- Yapı bozma
- Özellikler ve destekleyici özellikler
- 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,withverunanahtar kelimeleri
Varsayımlar
Java'yı zaten biliyor 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'yu kullanıyorsanız No Activity (Etkinlik Yok) şablonuyla yeni bir proje oluşturun. Proje dili olarak Kotlin'i seçin. Minimum SDK herhangi bir değere sahip olabilir, sonucu etkilemez.
Kod
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/<yourpackagename> 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 tanımlanmadığını söylediğini fark edeceksiniz. Bu nedenle, Android Studio kullanıyorsanız androidx.annotation.Nullable, IntelliJ kullanıyorsanız org.jetbrains.annotations.Nullable öğesini 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. Nullability, val, var ve veri sınıflarını tanımlama
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 ilk dönüşümü yapmasına izin verelim. 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 Menü çubuğu -> Kod -> Java Dosyasını Kotlin Dosyasına Dönüştür'ü seçerek dosyayı Kotlin'e dönüştürün.
IDE'niz dönüştürme işleminden sonra düzeltme isterse Evet'e basın.

Aşağıdaki Kotlin kodunu görmeniz gerekir:
class User(var firstName: String?, var lastName: String?)
User.java seçeneğinin User.kt olarak yeniden adlandırıldığını unutmayın. Kotlin dosyaları .kt uzantısına sahiptir.
Java User sınıfımızda iki özellik vardı: firstName ve lastName. Her birinin getter ve setter yöntemi vardı, bu da değerini değiştirilebilir hale getiriyordu. Kotlin'de değiştirilebilir değişkenler için kullanılan anahtar kelime var olduğundan dönüştürücü, bu özelliklerin her biri için var kullanır. Java özelliklerimizde yalnızca alıcılar olsaydı bu özellikler salt okunur olur ve val değişkenleri olarak bildirilirdi. 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. Bu işlem, tür bildirimine ? eklenerek yapılır.
firstName ve lastName değerlerini boş bırakılabilir olarak işaretlediğimiz için otomatik dönüştürücü, özellikleri String? ile boş bırakılabilir olarak işaretledi. Java üyelerinizi null olmayan olarak açıklama ekleyerek (org.jetbrains.annotations.NotNull veya androidx.annotation.NonNull kullanarak) işaretlerseniz dönüştürücü bunu tanır ve alanları Kotlin'de de null olmayan olarak ayarlar.
Temel dönüşüm zaten yapılmıştır. Ancak bunu daha deyimsel bir şekilde yazabiliriz. Nasıl yapacağınızı görelim.
Veri sınıfı
User sınıfımız yalnızca verileri tutar. Kotlin'de bu role sahip sınıflar için data anahtar kelimesi bulunur. Bu sınıfı data sınıfı olarak işaretlediğimizde derleyici, bizim için otomatik olarak getter ve setter oluşturur. Ayrıca equals(), hashCode() ve toString() işlevlerini de türetir.
data anahtar kelimesini User sınıfımıza ekleyelim:
data class User(var firstName: String?, var lastName: String?)
Kotlin, Java gibi birincil oluşturucuya ve bir veya daha fazla ikincil oluşturucuya sahip olabilir. Yukarıdaki örnekte yer alan, User sınıfının birincil oluşturucusudur. Birden fazla oluşturucuya sahip bir Java sınıfını dönüştürüyorsanız dönüştürücü, Kotlin'de de otomatik olarak birden fazla oluşturucu oluşturur. constructor anahtar kelimesi kullanılarak tanımlanır.
Bu sınıfın bir örneğini oluşturmak istersek şu şekilde yapabiliriz:
val user1 = User("Jane", "Doe")
Eşitlik (Equality)
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çinequals()işlevini çağırır. - Referans eşitliği,
===operatörünü kullanır ve iki referansın aynı nesneyi işaret edip etmediğini kontrol eder.
Veri sınıfının birincil oluşturucusunda 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. Bağımsız değişken atlandığında varsayılan değer kullanılır. Kotlin'de oluşturucular da işlevdir. Bu nedenle, lastName varsayılan değerinin null olduğunu belirtmek için varsayılan bağımsız değişkenleri kullanabiliriz. Bunu yapmak için null değerini lastName değişkenine 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ıldığında 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 olduğunu ve lastName özelliğinin varsayılan değeri olmadığını varsayalım. Bu durumda, varsayılan parametre varsayılan değeri olmayan bir parametreden önce geleceğinden 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 her zaman ad ve soyadı bir User nesne bildiriminde belirtmek istiyoruz. Bu nedenle varsayılan değerlere ihtiyacımız yok.
5. Nesne başlatma, eşlik eden nesne ve tekil nesneler
Codelab'e 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ünmelidir:
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ığına bakalım:
- Nesne bildirim sırasında oluşturulmadığından
userslistesi boş değer atanabilir. - Kotlin'deki
getUsers()gibi işlevlerfundeğiştiricisiyle tanımlanır. getFormattedUserNames()yöntemi artıkformattedUserNamesadlı bir özelliktir.- Kullanıcı listesi üzerinde yineleme (başlangıçta
getFormattedUserNames('nın parçasıydı) Java'dakiyle farklı bir söz dizimine sahiptir. staticalanı artıkcompanion objectbloğunun bir parçasıinitbloğu eklendi
Devam etmeden önce kodu biraz temizleyelim. Yapıcıya baktığımızda dönüştürücünün users listemizi, null değer atanabilir nesneler içeren değiştirilebilir bir liste haline getirdiğini görüyoruz. Liste gerçekten boş olabilir ancak boş kullanıcıları tutamayacağını varsayalım. Bu nedenle, aşağıdaki adımları uygulayalım:
userstürü beyanındaUser?içindeki?öğesini kaldırın.getUsers()dönüş türü içinUser?içindeki?öğesini kaldırarakList<User>?döndürülmesini sağlayın.
Başlangıç bloğu
Kotlin'de birincil oluşturucu herhangi bir kod içermez. Bu nedenle, başlatma kodu init bloklarına yerleştirilir. İşlev 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 bir kısmı özelliklerin başlatılmasını sağlar. Bu işlem, tesisin beyanında da yapılabilir. Örneğin, Repository sınıfımızın Kotlin sürümünde, kullanıcılar özelliğinin bildirimde 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'deki karşılığı companion object bloğudur. Burada statik alanları ve statik işlevleri de bildirirsiniz. Dönüştürücü, tamamlayıcı nesne bloğunu oluşturdu ve INSTANCE alanını buraya taşıdı.
Tekil öğeleri işleme
Repository sınıfının yalnızca bir örneğine ihtiyacımız olduğundan Java'da singleton 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 şu şekilde çağırırız:
val formattedUserNames = Repository.formattedUserNames
Bir özelliğin görünürlük değiştiricisi yoksa Repository nesnesindeki formattedUserNames özelliği örneğinde olduğu gibi varsayılan olarak herkese açık olduğunu unutmayın.
6. Null değer alabilme durumunu işleme
Repository sınıfı Kotlin'e dönüştürülürken otomatik dönüştürücü, liste tanımlandığında bir nesneyle ilk kullanıma hazırlanmadığı için kullanıcı listesini null yapılabilir hale getirdi. Sonuç olarak, users nesnesinin tüm kullanımlarında null olmayan onaylama operatörü !! kullanılmalıdır. (Dönüştürülen kodda users!! ve user!! simgelerini görürsünüz.) !! operatörü, tüm değişkenleri null olmayan bir türe dönüştürür. Böylece, özelliklere erişebilir veya üzerinde işlev çağırabilirsiniz. Ancak değişken değeri gerçekten null ise bir istisna oluşturulur. !! kullanarak çalışma zamanında istisnaların oluşturulma riskini alırsınız.
Bunun yerine, aşağıdaki yöntemlerden birini kullanarak boş değer alabilme durumunu işlemeyi tercih edin:
- Boşluk kontrolü yapma (
if (users != null) {...}) - Elvis operatörünü
?:kullanma (bu operatör, codelab'in ilerleyen bölümlerinde ele alınacaktır) - Kotlin standart işlevlerinden bazılarını kullanma (bu işlevler, codelab'in ilerleyen bölümlerinde 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 atanabilir olması gerekmediğini biliyoruz. Bu nedenle, users nesnesini bildirdiğimizde doğrudan oluşturabiliriz.
Kotlin, koleksiyon türlerinin örneklerini oluştururken kodunuzu daha okunabilir ve esnek hale getirmek için çeşitli yardımcı işlevler sağlar. Burada users için MutableList kullanıyoruz:
private var users: MutableList<User>? = null
Basitlik için mutableListOf() işlevini kullanabilir ve liste öğesi türünü sağlayabiliriz. mutableListOf<User>(), User nesnelerini tutabilecek boş bir liste oluşturur. Değişkenin veri türü artık derleyici tarafından çıkarılabildiğinden users özelliğinin açık tür bildirimini kaldırın.
private val users = mutableListOf<User>()
Ayrıca, kullanıcılar kullanıcı listesine salt okunur bir referans içereceğinden var simgesini val olarak değiştirdik. Referansın salt okunur olduğunu, bu nedenle hiçbir zaman yeni bir listeyi işaret edemeyeceğini ancak listenin kendisinin yine de değiştirilebilir olduğunu (öğe ekleyebilir veya kaldırabilirsiniz) unutmayın.
users değişkeni zaten başlatıldığından 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 birlikte users özelliğimiz artık boş değil ve gereksiz tüm !! operatörlerini kaldırabiliriz. Android Studio'da derleme hatalarını görmeye devam edeceğinizi ancak bunları çözmek için codelab'in sonraki adımlarına geçeceğinizi unutmayın.
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 olarak belirtirseniz bildirimi çıkarılacağından, bildirimdeki açık türü kaldırabilirsiniz.
val userNames = ArrayList<String>(users.size)
Destructuring
Kotlin, yapı bozma bildirimi adı verilen bir söz dizimi kullanarak bir nesnenin değişkenlere ayrılmasına olanak tanır. Birden fazla değişken oluşturup bunları bağımsız olarak kullanabiliriz.
Örneğin, data sınıfları, for döngüsündeki User nesnesini (firstName, lastName) olarak yapılandırabilmemiz için yapılandırmayı bozmayı destekler. 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
userName listesindeki adlar henüz istediğimiz biçimde değil. Hem lastName hem de firstName null olabileceğinden, biçimlendirilmiş kullanıcı adları listesini oluştururken null değer alabilme durumunu ele almamız gerekir. Adlardan biri eksikse "Unknown" simgesini göstermek istiyoruz. 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 olarak ayarlandığını görmek size yeni gelebilir. Kotlin'de if ve when ifadeler olduğundan (bir değer döndürürler) buna izin verilir. if ifadesinin son satırı name değişkenine atanır. Bu bloğun tek amacı name değerini başlatmaktır.
Temel olarak, burada sunulan mantık şöyledir: lastName değeri null ise name değeri firstName veya "Unknown" olarak ayarlanı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 deyimsel bir şekilde yazılabilir. Elvis operatörü, sol tarafı boş değilse sol taraftaki ifadeyi, sol tarafı boşsa sağ taraftaki ifadeyi döndürür.
Bu nedenle, aşağıdaki kodda null değilse firstName döndürülür. firstName değeri null ise ifade, sağ taraftaki değeri ("Unknown") döndürür:
name = if (lastName != null) {
...
} else {
firstName ?: "Unknown"
}
7. Dize şablonları
Kotlin, String ile çalışmayı dize şablonları sayesinde kolaylaştırır. Dize şablonları, değişkenin önüne $ sembolünü ekleyerek dize bildirimlerindeki değişkenlere referans vermenize olanak tanır. İfadeyi { } içine yerleştirip önüne $ simgesini ekleyerek bir dize tanımının içine de ifade koyabilirsiniz. Örnek: ${user.firstName}.
Kodunuz şu anda firstName ve lastName değerlerini kullanıcı adında birleştirmek için dize birleştirme kullanıyor.
if (firstName != null) {
firstName + " " + lastName
}
Bunun yerine, dize birleştirmeyi şu şekilde değiştirin:
if (firstName != null) {
"$firstName $lastName"
}
Dize şablonları kullanmak kodunuzu basitleştirebilir.
IDE'niz, kodunuzu yazmanın daha deyimsel bir yolu varsa size uyarılar gösterir. Koddaki dalgalı alt çizgiyi fark edersiniz ve üzerine geldiğinizde kodunuzu yeniden düzenleme önerisi görürsünüz.
Şu anda, name bildiriminin ödevle birleştirilebileceğine dair bir uyarı görmeniz gerekir. Şimdi bunu uygulayalım. name değişkeninin türü çıkarılabildiğinden, açık String türü bildirimi kaldırılabilir. formattedUserNames artık şu şekilde görünüyor:
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ı eksikse "Unknown" karakterini gösterir. Bu nedenle, boş nesneler desteklenmez. Bu nedenle, formattedUserNames veri türü için List<String?> yerine List<String> yazın.
val formattedUserNames: List<String>
8. Koleksiyonlarla ilgili işlemler
formattedUserNames getter'ı daha yakından inceleyelim ve nasıl daha deyimsel hale getirebileceğimize bakalım. Şu anda kod şunları yapıyor:
- Yeni bir dizeler listesi oluşturur.
- Kullanıcı listesinde yinelenir.
- Kullanıcının adını ve soyadını temel alarak her kullanıcı için biçimlendirilmiş adı oluşturur.
- 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 güvenli hale getiren kapsamlı bir koleksiyon dönüşümleri listesi sunar. Bunlardan biri de map işlevidir. Bu işlev, verilen dönüştürme işlevinin orijinal listedeki her öğeye uygulanmasıyla elde edilen sonuçları içeren yeni bir liste döndürür. Bu nedenle, yeni bir liste oluşturup kullanıcı listesini manuel olarak yinelemek yerine map işlevini kullanabilir ve for döngüsünde kullandığımız mantığı map gövdesine taşıyabiliriz. Varsayılan olarak, map içinde kullanılan mevcut liste öğesinin adı it'dir. Ancak okunabilirliği artırmak için it yerine kendi değişken adınızı kullanabilirsiniz. Bu örnekte, user adını verelim:
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ünde olduğundan ve name için String gerektiğinden user.lastName boşsa "Unknown" döndürmek için Elvis operatörünü kullandığımızı unutmayın.
...
else {
user.lastName ?: "Unknown"
}
...
Bu işlemi 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. Özellikler ve destekleyici özellikler
Otomatik dönüştürücünün, getFormattedUserNames() işlevini özel bir alıcıya sahip formattedUserNames adlı bir özellikle değiştirdiğini gördük. Kotlin, arka planda List döndüren bir getFormattedUserNames() yöntemi oluşturmaya devam eder.
Java'da sınıf özelliklerimizi getter ve setter işlevleri aracılığıyla kullanıma sunarız. Kotlin, alanlarla ifade edilen bir sınıfın özellikleri ve işlevlerle ifade edilen, bir sınıfın yapabileceği işlemler arasında daha iyi bir ayrım yapmamıza olanak tanır. Bizim örneğimizde Repository sınıfı çok basit ve herhangi bir işlem yapmıyor. Bu nedenle yalnızca alanları var.
Java getFormattedUserNames() işlevinde tetiklenen mantık artık formattedUserNames Kotlin özelliğinin alıcı işlevi çağrıldığında tetikleniyor.
formattedUserNames özelliğiyle açıkça eşleşen bir alan olmasa da Kotlin, gerekirse özel alıcılar ve ayarlayıcılardan erişebileceğimiz field adlı otomatik bir destek alanı sağlar.
Ancak bazen, otomatik destek alanı tarafından sağlanmayan bazı ek işlevler isteriz.
Bir örnek üzerinden gidelim.
Repository sınıfımızda, Java kodumuzdan oluşturulan getUsers() işlevinde kullanıma sunulan, değiştirilebilir bir kullanıcı listemiz var:
fun getUsers(): List<User>? {
return users
}
Repository sınıfının arayanlarını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 doğrusu, mutableListOf<User> tarafından desteklenen salt okunur bir List<User> kullanıma sunulur.
Öncelikle users öğesini _users olarak yeniden adlandıralım. Değişken adını vurgulayın, sağ tıklayarak Yeniden düzenle > Değişkeni yeniden adlandır'ı seçin. Ardından, kullanıcı listesi döndüren herkese açık salt okunur bir özellik ekleyin. Bu işleme users adını verelim:
private val _users = mutableListOf<User>()
val users: List<User>
get() = _users
Bu noktada getUsers() yöntemini silebilirsiniz.
Yukarıdaki değişiklikle birlikte, özel _users mülkü, herkese açık users mülkünün destekleyici mülkü haline gelir. Repository sınıfının dışında, sınıfın tüketicileri listeye yalnızca users üzerinden erişebildiğinden _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 liste değiştirilebilir ve add() ile remove() gibi işlemler kullanılabilir. Bu durumda 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 ile özellikleri
Şu anda Repository sınıfı, bir 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 da kullanmak istiyorsak bu mantığı 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, mutableListOf() işlevi, Kotlin Standart Kitaplığı'ndaki Collections.kt içinde zaten tanımlanmıştır. Bu işlev, List öğesinin yeni bir örneğini oluşturmak için kullanılırdı.
Java'da, bazı yardımcı işlevlere ihtiyacınız olduğunda büyük olasılıkla bir Util sınıfı oluşturur ve bu işlevleri statik işlev olarak tanımlarsınız. Kotlin'de sınıf olmadan üst düzey işlevler bildirebilirsiniz. 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. Bu özellikler, kullanımı yalnızca uzantıların gerekli olduğu 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ı özelliğinde tutabiliriz. Aynı dosyada Repository sınıfının dışında 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
Daha sonra, uzantı işlevlerini ve özelliklerini User sınıfının bir parçasıymış gibi kullanabiliriz.
Biçimlendirilmiş ad, User sınıfının bir özelliği olduğundan ve Repository sınıfının bir işlevi olmadığından uzantı özelliğini kullanalım. Repository dosyamız artık şu şekilde görünüyor:
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şlevselliğini genişletmek için uzantı işlevlerini kullanır. Iterable ve Collection üzerindeki işlevlerin çoğu uzantı işlevleri olarak uygulanır. Örneğin, önceki 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şlevleri yardımıyla daha deyimsel hale getirilebilir.
Kotlin, koda yalnızca belirli bir nesne bağlamında, nesneye adına göre erişmeye gerek kalmadan yürütmek 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 olmasını sağlar. Tüm kapsam işlevlerinin bir alıcısı (this) vardır, bir bağımsız değişkeni (it) olabilir ve bir değer döndürebilir.
Hangi işlevi ne zaman kullanacağınızı hatırlamanıza yardımcı olacak kullanışlı bir yardımcı kısa bilgiyi aşağıda bulabilirsiniz:

_users nesnemizi Repository içinde yapılandırdığımız için apply işlevini kullanarak kodu daha deyimsel 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. Özet
Bu codelab'de, kodunuzu Java'dan Kotlin'e dönüştürmeye başlamak için bilmeniz gereken temel bilgileri ele aldık. Bu dönüştürme, geliştirme platformunuzdan bağımsızdır ve yazdığınız kodun deyimsel Kotlin olmasını sağlar.
Deyimsel Kotlin, kod yazmayı kısa ve öz hale getirir. Kotlin'in sunduğu tüm özelliklerle kodunuzu daha güvenli, daha kısa ve daha okunabilir hale getirmenin birçok yolu vardır. Örneğin, Repository sınıfımızı, _users listesini doğrudan bildirimde kullanıcılarla oluşturarak optimize edebilir ve init bloğunu kaldırabiliriz:
private val users = mutableListOf(User("Jane", ""), User("John", null), User("Anne", "Doe"))
Boş değer alabilme, tekil nesneler, dizeler ve koleksiyonları işlemeyle ilgili konuların yanı sıra uzantı işlevleri, üst düzey işlevler, özellikler ve kapsam işlevleri gibi birçok konuyu ele aldık. İki Java sınıfından iki Kotlin sınıfına geçtik. Bu sınıflar artık ş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şlemesinin kısa bir özeti:
Java | Kotlin |
|
|
|
|
|
|
Yalnızca veri içeren sınıf |
|
Oluşturucuda başlatma |
|
|
|
Singleton sınıfı |
|
Kotlin ve platformunuzda nasıl kullanıldığı hakkında daha fazla bilgi edinmek için şu kaynaklara göz atın: