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ı öğ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.
Neler öğreneceksiniz?
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 durumunu 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
,with
verun
anahtar kelimeleri
Varsayımlar
Java'yı zaten biliyor olmanız gerekir.
İhtiyacınız olanlar
Yeni proje oluşturma
IntelliJ IDEA kullanıyorsanız Kotlin/JVM ile yeni bir Java projesi oluşturun.
Android Studio kullanıyorsanız Etkinlik içermeyen yeni bir proje oluşturun.
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;
}
}
Proje türünüze bağlı olarak, Android projesi kullanıyorsanız androidx.annotation.Nullable
, aksi takdirde 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;
}
}
IDE'miz, Java kodunu Kotlin koduna otomatik olarak yeniden düzenleme konusunda oldukça iyi bir iş çıkarabilir ancak bazen biraz yardıma ihtiyacı olur. Önce bunu yapacağız, ardından nasıl ve neden bu şekilde yeniden düzenlendiğini anlamak için yeniden düzenlenmiş 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 yapmanızı 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 adının User.kt
olarak değiştirildiğini unutmayın. Kotlin dosyalarının uzantısı .kt'dir.
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 getter'lar olsaydı bu özellikler değişmez olurdu 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 yeniden düzenleme 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 alıcılar ve ayarlayıcılar 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, 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 istiyorsak bunu şu şekilde yapabiliriz:
val user1 = User("Jane", "Doe")
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
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("John", "Doe")
İşlevler çağrılırken işlev parametreleri adlandırılabilir:
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")
Devam etmeden önce User
sınıfınızın data
sınıfı olduğundan emin olun. Repository
sınıfını Kotlin'e dönüştürelim. Otomatik dönüştürme 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:
init
bloğu eklendi (Repository.kt#L50)static
alanı artık bircompanion object
bloğunun parçası (Repository.kt#L33)- Nesne bildirim sırasında oluşturulmadığından
users
listesi boş değer atanabilir (Repository.kt#L7). getFormattedUserNames()
yöntemi artıkformattedUserNames
(Repository.kt#L11) adlı bir özellik- Kullanıcı listesi üzerinde yapılan yinelemenin (başlangıçta
getFormattedUserNames(
'nın bir parçasıydı) Java'dakiyle (Repository.kt#L15) farklı bir söz dizimi var.
Devam etmeden önce kodu biraz temizleyelim. Dönüştürücünün, users
listemizi boş değer atanabilir nesneler içeren değiştirilebilir bir liste haline getirdiğini görüyoruz. Liste gerçekten de boş olabilir ancak boş kullanıcılar içeremez. Bu nedenle, aşağıdaki adımları uygulayalım:
users
türü beyanı içindekiUser?
içinde?
öğesini kaldırın.getUsers
,List<User>?
değerinde ödeme yapmalıdır.
Otomatik dönüştürücü, kullanıcı değişkenlerinin ve init bloğunda tanımlanan değişkenlerin değişken bildirimlerini de gereksiz yere 2 satıra bölüyordu. Her değişken bildirimini tek bir satıra yerleştirelim. Kodumuz aşağıdaki gibi görünmelidir:
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)
}
}
Init block
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ı özellikleri başlatmayı ele alır. 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 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ü, INSTANCE
alanını oluşturup 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 ve eşlik eden nesneyi kaldırıp sınıf tanımını object Repository
ile değiştirin.
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 users = Repository.users
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, veri sınıfları, User
nesnesini for
döngüsünde (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ü şu şekilde güncelleyelim:
for ((firstName, lastName) in users!!) {
val name: String?
if (lastName != null) {
if (firstName != null) {
name = "$firstName $lastName"
} else {
name = lastName
}
} else if (firstName != null) {
name = firstName
} else {
name = "Unknown"
}
userNames.add(name)
}
Repository
sınıfı Kotlin'e dönüştürülürken otomatik dönüştürücü, kullanıcı listesini null yapılabilir hale getirdi. Bunun nedeni, liste bildirilirken bir nesneyle başlatılmamış olmasıydı. users
nesnesinin tüm kullanımlarında, null olmayan onaylama operatörü !!
kullanılır. Tüm değişkenleri null olmayan bir türe dönüştürür ve değer null ise istisna oluşturur. !!
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ğerleri işlemeyi tercih edin:
- Boşluk kontrolü yapma (
if (users != null) {...}
) ?:
Elvis operatörünü kullanma (bu codelab'de daha sonra 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, kullanıcı listesinin nesne oluşturulduktan hemen sonra başlatıldığı için boş değer atanabilir olması gerekmediğini biliyoruz. Bu nedenle, nesneyi bildirdiğimizde doğrudan örnekleyebiliriz.
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, liste öğesi türünü sağlayabilir, ArrayList
oluşturucu çağrısını init
bloğundan kaldırabilir ve users
özelliğinin açık tür bildirimini kaldırabiliriz.
private val users = mutableListOf<User>()
Ayrıca, kullanıcılar kullanıcı listesine değişmez bir referans içereceğinden var'ı val olarak değiştirdik. Referansın değişmez olduğunu ancak listenin kendisinin değişebilir olduğunu (öğe ekleyebilir veya kaldırabilirsiniz) unutmayın.
Bu değişikliklerle birlikte users
özelliğimiz artık boş değil ve gereksiz tüm !!
operatörlerini kaldırabiliriz.
val userNames: MutableList<String?> = ArrayList(users.size)
for ((firstName, lastName) in users) {
...
}
Ayrıca, kullanıcı değişkeni zaten başlatıldığından başlatma işlemini init
bloğundan kaldırmamız gerekir:
init {
val user1 = User("Jane", "")
val user2 = User("John", null)
val user3 = User("Anne", "Doe")
users.add(user1)
users.add(user2)
users.add(user3)
}
Hem lastName
hem de firstName
null
olabileceğinden, biçimlendirilmiş kullanıcı adları listesini oluştururken nullability'yi ele almamız gerekir. Adlardan biri eksikse "Unknown"
değerini göstermek istediğimiz için tür bildiriminden ?
değerini kaldırarak adı boş olmayan bir değer haline getirebiliriz.
val name: String
lastName
değeri null ise name
değeri firstName
veya "Unknown"
olur:
if (lastName != null) {
if (firstName != null) {
name = "$firstName $lastName"
} else {
name = lastName
}
} else if (firstName != null) {
name = firstName
} else {
name = "Unknown"
}
Bu ifade, 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 boş değilse user.firstName
döndürülür. user.firstName
değeri null ise ifade, sağ taraftaki değeri ("Unknown"
) döndürür:
if (lastName != null) {
...
} else {
name = firstName ?: "Unknown"
}
Kotlin, String
ile çalışmayı String şablonları sayesinde kolaylaştırır. Dize şablonları, dize bildirimlerindeki değişkenlere referans vermenize olanak tanır.
Otomatik dönüştürücü, ad ve soyadın birleştirilmesini $
sembolünü kullanarak değişken adına doğrudan dizede referans verecek şekilde güncelledi ve ifadeyi { }
arasına yerleştirdi .
// Java
name = user.getFirstName() + " " + user.getLastName();
// Kotlin
name = "${user.firstName} ${user.lastName}"
Kodda, dize birleştirmeyi aşağıdakilerle değiştirin:
name = "$firstName $lastName"
Kotlin'de if
, when
, for
ve while
ifadelerdir. Bunlar bir değer döndürür. IDE'niz, atamanın if
dışına çıkarılması gerektiğine dair bir uyarı bile gösteriyor:
IDE'nin önerisini uygulayalım ve her iki if
ifadesi için atamayı kaldıralım. if ifadesinin son satırı atanır. Bu şekilde, bu bloğun tek amacının ad değerini başlatmak olduğu daha net anlaşılıyor:
name = if (lastName != null) {
if (firstName != null) {
"$firstName $lastName"
} else {
lastName
}
} else {
firstName ?: "Unknown"
}
Ardından, name
bildiriminin atamayla birleştirilebileceği konusunda bir uyarı alırız. Bunu da uygulayalım. Ad değişkeninin türü tahmin edilebildiğinden açık tür bildirimi kaldırılabilir. formattedUserNames
artık şu şekilde görünüyor:
val formattedUserNames: List<String?>
get() {
val userNames: MutableList<String?> = ArrayList(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
}
formattedUserNames
getter'ı daha yakından inceleyelim ve nasıl daha deyimsel hale getirebileceğimizi görelim. Ş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 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ündeki 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
}
}
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"
}
}
}
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. Aşağıdaki örneği inceleyelim.
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
}
Buradaki sorun, users
döndürülerek Repository sınıfının herhangi bir tüketicisinin kullanıcı listemizi değiştirebilmesidir. Bu iyi bir fikir değildir. Destekleyici bir özellik kullanarak bu sorunu düzeltelim.
Öncelikle users
öğesini _users
olarak yeniden adlandıralım. Şimdi kullanıcı listesi döndüren herkese açık bir değişmez özellik ekleyin. Bu işleme users
adını verelim:
private val _users = mutableListOf<User>()
val users: List<User>
get() = _users
Bu 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.
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)
}
}
Ş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, List
öğesinin yeni bir örneğini oluşturmak için kullandığımız mutableListOf()
işlevi, Standart Kitaplık'taki Collections.kt
içinde doğrudan tanımlanır.
Java'da, bir yardımcı işlevselliğe ihtiyacınız olduğunda büyük olasılıkla bir Util
sınıfı oluşturur ve bu işlevselliği 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. Bu nedenle, bu türle bir yakınlıkları vardır.
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, Repository
sınıfının işlevi değil, kullanıcının bir özelliği olduğundan 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
'deki 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.
Repository
sınıf kodumuzda, _users
listesine birkaç kullanıcı nesnesi ekliyoruz. Bu çağrılar, 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 oluşturdu: let
, apply
, with
, run
ve also
. Kısa ve güçlü olan bu işlevlerin hepsinde bir alıcı (this
) bulunur, bağımsız değişken (it
) içerebilir ve değer döndürebilir. Ne yapmak istediğinize bağlı olarak hangisini kullanacağınıza siz karar verirsiniz.
Bunu hatırlamanıza yardımcı olacak kullanışlı bir kopya kağıdı:
_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)
}
}
Bu codelab'de, kodunuzu Java'dan Kotlin'e yeniden düzenlemeye başlamak için bilmeniz gereken temel bilgileri ele aldık. Bu yeniden düzenleme, geliştirme platformunuzdan bağımsızdır ve yazdığınız kodun deyimsel olmasını sağlar.
İyi Kotlin kodu, kısa ve öz bir şekilde yazılır. 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ğerleri, tekil nesneleri, dizeleri ve koleksiyonları işleme, uzantı işlevleri, üst düzey işlevler, özellikler ve kapsam işlevleri gibi ç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 artık şu şekilde görünüyor:
User.kt
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'e eşlenmesinin 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 kullanacağınız hakkında daha fazla bilgi edinmek için şu kaynaklara göz atın:
- Kotlin Koans
- Kotlin Eğiticileri
- Kotlin ile Android uygulamaları geliştirme - ücretsiz kurs
- Kotlin Bootcamp for Programmers
- Java geliştiriciler için Kotlin - Denetim modunda ücretsiz kurs