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
verun
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.
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çinequals()
'ı ç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şlevlerfun
değiştiricisiyle tanımlanır getFormattedUserNames()
yöntemi artıkformattedUserNames
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 bircompanion 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çindekiUser?
içinde?
'ü kaldırıngetUsers()
döndürme türü içinUser?
'teki?
öğesini kaldırarakList<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:
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 |
|
|
|
|
|
|
Yalnızca veri içeren sınıf |
|
Oluşturucuda başlatma |
|
|
|
Singleton sınıfı |
|
Kotlin ve platformunuzda nasıl kullanılacağı hakkında daha fazla bilgi edinmek için şu kaynaklara göz atın:
- Kotlin Koans
- Kotlin Eğitimleri
- Android Kotlin Hakkında Temel Bilgiler
- Kotlin Bootcamp for Programmers
- Java geliştiricileri için Kotlin - Denetleme modunda ücretsiz kurs