Bu codelab hakkında
1. Hoş geldiniz!
Bu codelab'de, kodunuzu Java'dan Kotlin'e nasıl dönüştüreceğinizi öğreneceksiniz. Ayrıca, Kotlin dili kongrelerinin ne olduğunu ve yazdığınız kodun bu kurallara uygun olmasını nasıl sağlayacağınızı da öğreneceksiniz.
Bu codelab, projesini Kotlin'e taşımayı düşünen Java kullanan tüm geliştiriciler için uygundur. IDE 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 atarak kodu daha deyimsel hale getirerek ve yaptığımız hatalardan nasıl kaçınabileceğimizi göreceğiz.
Neler öğreneceksiniz?
Java'yı Kotlin'e nasıl dönüştüreceğinizi öğreneceksiniz. Bu şekilde, aşağıdaki Kotlin dili özelliklerini ve kavramlarını öğrenebilirsiniz:
- Boş değer işleme
- Tektonları uygulama
- Veri sınıfları
- Dizeleri işleme
- Elvis operatörü
- Yıkım aracı
- Mülkler ve yedekleme özellikleri
- 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 kelime
Tahminler
Java'ya aşina olmanız gerekir.
Gerekenler
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 Etkinlik içermeyen yeni bir proje oluşturun.
Kod
Bir User
model nesnesi ve User
nesneleriyle çalışan ve kullanıcıların listeleri ile biçimlendirilmiş kullanıcı adlarını gösteren Repository
tekli sınıf oluşturacağız.
app/java/<paketinizin adı> altında User.java
adında yeni bir dosya oluşturun ve aşağıdaki koda 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;
}
}
Projenizin türüne bağlı olarak, bir Android projesi kullanıyorsanız androidx.annotation.Nullable
içe aktarın. Aksi takdirde org.jetbrains.annotations.Nullable
içe aktarın.
Repository.java
adlı yeni bir dosya oluşturun ve aşağıdaki koda 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ş değer, değer, değişken ve veri sınıflarını bildirme
IDE'miz, Java kodunu otomatik olarak Kotlin kodu olarak yeniden düzenleyebilir ancak bazen biraz yardıma ihtiyaç duyar. Öncelikle bu işlemi gerçekleştireceğiz ve ardından bu kodun nasıl yeniden yapılandırıldığını anlamak için kodu yeniden düzenleyeceğiz.
User.java
dosyasına gidin ve bunu Kotlin biçimine dönüştürün: Menü çubuğu -> Kod -> Java Dosyasını Kotlin Dosyasına Dönüştürün.
IDE'niz dönüşümden sonra düzeltme yapılmasını isterse Evet'e basın.
Aşağıdaki Kotlin kodunu görürsünüz:
class User(var firstName: String?, var lastName: String?)
User.java
öğesinin User.kt
olarak yeniden adlandırıldığını unutmayın. Kotlin dosyalarının .kt uzantısı vardır.
Java User
sınıfımızda iki mülk vardı: firstName
ve lastName
. Her birinin değeri ve değişkenliği, dolayısıyla değişken olabilir. Kotlin’in değişken değişkenler için anahtar kelimesi var
. Bu nedenle, dönüştürücü bu özelliklerin her biri için var
kullanır. Java özelliklerimizde yalnızca alıcı varsa bunlar sabit olmalı ve val
değişkenleri olarak açıklanmalıdır. val
, Java'daki final
anahtar kelimeye benzer.
Kotlin ve Java arasındaki temel farklılıklardan biri, Kotlin'in bir değişkenin null değerini kabul edip edemeyeceğini açıkça belirtmesidir. Bunu, tür beyanına "?
" ekleyerek yapar.
firstName
ve lastName
değerlerini boş olarak işaretlediğimiz için otomatik dönüştürücü, özellikleri otomatik olarak String?
ile boş değer olarak işaretledi. Java üyelerinize boş olmayan (org.jetbrains.annotations.NotNull
veya androidx.annotation.NonNull
kullanarak) ek açıklamalar eklerseniz dönüştürücü bunu tanır ve Kotlin'deki alanları da null olmaz.
Temel yeniden düzenleme işlemi zaten tamamlanmıştır. Ancak biz bunu daha deyimsel bir şekilde yazabiliriz. Bunu nasıl yapacağınıza bakalım.
Veri sınıfı
User
sınıfımız yalnızca veri içerir. Kotlin'in şu rolü olan sınıflar için bir anahtar kelimesi var: data
. Bu sınıfı data
sınıfı olarak işaretlediğinizde, derleyici bizim için otomatik olarak alıcı ve işleyiciler oluşturur. Ayrıca equals()
, hashCode()
ve toString()
işlevlerini türetecektir.
data
anahtar kelimesini User
sınıfımıza ekleyelim:
data class User(var firstName: String, var lastName: String)
Java'da olduğu gibi Kotlin'in de birincil kurucusu ve bir veya daha fazla ikincil kurucusu olabilir. Yukarıdaki örnekte bulunan kullanıcı, Kullanıcı sınıfının birincil kurucusudur. Birden fazla oluşturucuya sahip bir Java sınıfını dönüştürüyorsanız dönüştürücü de Kotlin'de otomatik olarak birden fazla oluşturucu oluşturur. Bunlar 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")
Eşitlik
Kotlin'in iki tür eşitliği vardır:
- Yapısal eşitlik, iki operatörün eşit olup olmadığını belirlemek için
==
operatörünü kullanır veequals()
işlevini çağırır. - Referans eşitlik,
===
operatörünü kullanır ve iki referansın aynı nesneye 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 içinde, işlev çağrılarındaki bağımsız değişkenlere varsayılan değerler atayabiliriz. Bağımsız değişken çıkarıldığında varsayılan değer kullanılır. Kotlin'de oluşturucular da işlevdir. Bu nedenle, lastName
özelliğinin varsayılan değerinin null
olduğunu belirtmek için varsayılan bağımsız değişkenleri kullanabiliriz. Bunu yapmak için lastName
adlı yöneticiye null
atıyoruz.
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")
İşlev parametreleri, işlevler çağrılırken adlandırılabilir:
val john = User(firstName = "John", lastName = "Doe")
Farklı bir kullanım örneğinde, firstName
öğesinin varsayılan değer olarak null
olduğunu ve lastName
değerinin olmadığını varsayalım. Bu durumda, varsayılan parametre varsayılan değeri olmayan bir parametrenin önünde yer alacağı için ada sahip bağımsız değişkenlerle işlevi ç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")
5. Nesne başlatma, tamamlayıcı nesne ve tekliler
Devam etmeden önce, User
sınıfınızın data
sınıfı olduğundan emin olun. Repository
sınıfını Kotlin biçimine dönüştürelim. Otomatik dönüşüm sonucu aşağıdaki gibi görünecektir:
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 işlevini görelim:
- Bir
init
bloku eklendi (Repository.kt#L50) static
alanı artık bircompanion object
blokunun (Repository.kt#L33) parçası- Nesne bildirim zamanında örneklendirilmediği için
users
listesi boş olabilir (Repository.kt#L7) getFormattedUserNames()
yöntemi artıkformattedUserNames
(Repository.kt#L11) adlı bir mülktür- Kullanıcı listesi üzerinde yineleme (başlangıçta
getFormattedUserNames(
parçasıydı) Java sözdiziminden farklı bir söz dizimine sahiptir (Repository.kt#L15)
Devam etmeden önce kodu biraz temizleyelim. Dönüştürücünün, users
listemizi boş değerli nesneler içeren değiştirilebilir bir liste haline getirdiğini görebiliriz. Liste gerçekten boş değerli olabilir, ancak kullanıcıların boş değerli olamayacağını söyleyelim. Bu nedenle aşağıdakileri yapalım:
users
türü bildirimindekiUser?
öğesindeki?
öğesini kaldırıngetUsers
,List<User>?
değerini döndürmelidir
Ayrıca, otomatik dönüştürücü, kullanıcı değişkenlerinin ve init blokunda tanımlanan değişkenlerin değişken bildirimlerini gereksiz bir şekilde 2 satıra böler. Her değişken bildirimini tek bir satıra yerleştirelim. Kodumuz şu şekilde 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 bloku
Kotlin'de birincil oluşturucu herhangi bir kod içeremez. Bu nedenle, ilk kullanıma hazırlama kodu init
bloka yerleştirilir. Aynı işleve sahiptir.
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 çoğu başlatma özelliklerini işler. 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, beyana göre kullanıcı özelliğinin başlatıldığını görüyoruz.
private var users: MutableList<User>? = null
Kotlin's static
özellikleri ve yöntemleri
Java'da, bir sınıfa ait olduklarını ancak sınıfın bir örneğine ait olmadığını söyleyen alanlar veya işlevler için static
anahtar kelimesini kullanırız. Bu nedenle, Repository
sınıfımızda INSTANCE
statik alanını oluşturduk. Bunun için Kotlin eşdeğeri companion object
bloktur. Burada ayrıca, statik alanları ve statik işlevleri de bildirirsiniz. Dönüştürücü, INSTANCE
alanını oluşturup buraya taşımıştır.
Tek tekleri 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 ile class
parametresini object
ile değiştirerek bu kalıbı derleyici düzeyinde zorunlu kılabilirsiniz.
Özel oluşturucuyu ve tamamlayıcı nesnesini kaldırın ve 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 nesne üzerinde çağırırız. Örneğin:
val users = Repository.users
Yıkıcı
Kotlin, yıkma bildirimi adı verilen bir söz dizimi kullanarak bir nesnenin birkaç değişkende yok edilmesine olanak tanır. Birden çok değişken oluşturur ve bunları bağımsız olarak kullanabiliriz.
Örneğin, veri sınıfları for
nesnesindeki User
nesnesinin (firstName, lastName)
olarak yok edilebilmesi için yıkmayı destekler. Böylece firstName
ve lastName
değerleriyle doğrudan çalışabiliyoruz. 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)
}
6. Boş değer işleme
Repository
sınıfı Kotlin'e dönüştürülürken otomatik dönüştürücü, bildirildiğinde bir nesnede ilk kullanıma sunulmadığı için kullanıcı listesini boş değerli hale getirdi. users
nesnesinin tüm kullanımları için, boş olmayan onaylama operatörü !!
kullanılır. Herhangi bir değişkeni null olmayan bir türe dönüştürür ve değer null ise istisna oluşturur. !!
özelliğini kullandığınızda, çalışma zamanında istisna alma riskiyle karşı karşıya kalırsınız.
Bunun yerine, aşağıdaki yöntemlerden birini kullanarak null değerini işlemeyi tercih edin:
- Boş çek (
if (users != null) {...}
) - elvis operatörünü
?:
(kod laboratuvarının ilerleyen kısımlarında ele alınmıştır) kullanma - Kotlin standart işlevlerinin bazılarını kullanma (kod laboratuvarının ilerleyen bölümlerinde ele alınan)
Bu örnekte, nesne oluşturulduktan hemen sonra başlatıldığından, kullanıcı listesinin null (boş) olmaması gerektiğini biliyoruz. Bu nedenle, nesneyi bildirdiğimizde nesneyi anında örneklendirebiliriz.
Kotlin, koleksiyon türü örnekleri oluştururken kodunuzu daha okunabilir ve esnek hale getirmek için çeşitli yardımcı işlevler sağlar. users
için bir MutableList
kullanıyoruz:
private var users: MutableList<User>? = null
Basitlik sağlaması için mutableListOf()
işlevini kullanabilir, liste öğesi türünü sağlayabilir, ArrayList
oluşturucu çağrısını init
blokundan kaldırabilir ve users
özelliğinin açık tür bildirimini kaldırabiliriz.
private val users = mutableListOf<User>()
Kullanıcılar kullanıcı listesine sabit bir referans içerdiğinden de var değerini val olarak değiştirdik. Referansın değiştirilemez ancak listenin kendisi değişebilir (öğe ekleyebilir veya kaldırabilirsiniz).
Bu değişiklikler sayesinde users
özelliğimiz artık null değil ve gereksiz tüm !!
operatör örneklerini 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, ilk kullanıma hazırlama işlemini init
blokundan 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ı listesi oluştururken null değeri belirlememiz gerekir. İki addan biri eksikse "Unknown"
kelimesini göstermek istediğimiz için tür beyanından ?
ifadesini kaldırarak adı boş olamaz.
val name: String
lastName
boşsa name
, 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, ?:
elvis operatörü kullanılarak daha deyimsel bir şekilde yazılabilir. elvis operatörü, boş değilse ifadenin sol tarafını, sol tarafta boşken ifadenin sağ tarafında ifade döndürür.
Aşağıdaki kodda boş değilse user.firstName
döndürülür. user.firstName
, null ise ifade, sağdaki değeri ("Unknown"
) döndürür.
if (lastName != null) {
...
} else {
name = firstName ?: "Unknown"
}
7. Dize şablonları ve if ifadesi
Kotlin, String şablonlarıyla String
s ile çalışmayı kolaylaştırır. Dize şablonları, dize bildirimleri içindeki değişkenlere referans vermenize olanak tanır.
Otomatik dönüştürücü, $
simgesini kullanarak değişken adını doğrudan ifade içinde bulmak ve adın arasına { }
girmek için ad ve soyadının birleştirilmesini güncelledi .
// Java
name = user.getFirstName() + " " + user.getLastName();
// Kotlin
name = "${user.firstName} ${user.lastName}"
Kodda, Dize birleştirmeyi şununla değiştirin:
name = "$firstName $lastName"
Kotlin if
, when
, for
ve while
ifadeleridir; bir değer döndürürler. IDE'niz, atamanın if
dışına kaldırılması gerektiğine dair bir uyarı da gösteriyor:
IDE'nin önerisini takip edelim ve ödevi her iki if
ifadesi için kaldıralım. if ifadesi için son satır atanır. Böylece, bu engellemenin tek amacının ad değerini başlatmak olduğunu anlıyoruz:
name = if (lastName != null) {
if (firstName != null) {
"$firstName $lastName"
} else {
lastName
}
} else {
firstName ?: "Unknown"
}
Şimdi, name
bildiriminin ödevle birleştirilebileceğine dair bir uyarı alırsınız. Bunu da uygulayalım. Ad değişkeninin türü çıkarılabilir. Bu nedenle, açık tür bildirimini kaldırabiliriz. Şimdi formattedUserNames
aşağıdaki gibi 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
}
8. Koleksiyonlarla ilgili işlemler
formattedUserNames
alıcısını daha ayrıntılı bir şekilde inceleyerek nasıl deyimsel hale getirebileceğimizi inceleyelim. Kod şu anda aşağıdakileri yapmaktadır:
- Yeni bir dize listesi oluşturur
- Kullanıcı listesinden tekrarlanır
- 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, JavaCollection API'nin özelliklerini genişleterek geliştirmeyi daha hızlı ve güvenli hale getiren koleksiyon dönüşümlerinin kapsamlı bir listesini sunar. Bunlardan biri map
işlevidir. Bu işlev, belirtilen dönüşüm işlevinin orijinal listedeki her öğeye uygulanmasıyla ilgili sonuçları içeren yeni bir liste döndürür. Bu nedenle, yeni bir liste oluşturmak ve kullanıcı listesini manuel olarak yinelemek yerine map
işlevini kullanabilir ve for
döngüsündeki mantığı map
gövdesinin içine taşıyabiliriz. Varsayılan olarak, map
içinde kullanılan mevcut liste öğesinin adı it
'dir ancak okunabilirlik için it
öğesini kendi değişken adınızla değiştirebilirsiniz. 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"
}
}
}
9. Mülkler ve yedekleme özellikleri
Otomatik dönüştürücünün, getFormattedUserNames()
işlevini, özel alıcısı olan formattedUserNames
adlı bir özellikle değiştirdiğini gördük. Kotlin altında, Kotlin yine de List
değeri döndüren bir getFormattedUserNames()
yöntemi oluşturur.
Java'da sınıf özelliklerimizi getter ve setter işlevleriyle açığa çıkarırdık. Kotlin, alanın özellikleri ve işlevler, sınıfın yapabildiği işlemler ve işlevlerle ifade edilerek sınıfın daha iyi ayırt edilebilmesini sağlar. Bu örnekte, Repository
sınıfı çok basittir ve yalnızca alanlar içerdiğinden herhangi bir işlem yapmaz.
Java getFormattedUserNames()
işlevinde tetiklenen mantık, formattedUserNames
Kotlin mülkünün alıcısı çağrılırken tetiklenir.
formattedUserNames
mülküne karşılık açık bir alanımız olmasa da Kotlin, field
adlı bir otomatik yedekleme alanı sunuyor. Otomatik yedekleme alanı, gerekirse özel alıcı ve ayarlayıcılardan erişmemizi sağlıyor.
Ancak bazen otomatik yedekleme alanının sağlamadığı bazı ek işlevler istiyoruz. Aşağıdaki örneği inceleyelim.
Repository
sınıfımızın içinde, Java kodumuzdan oluşturulan getUsers()
işlevinde açığa çıkan çeşitli kullanıcılardan oluşan bir listemiz var:
fun getUsers(): List<User>? {
return users
}
Buradaki sorun, users
deposunu döndürerek kullanıcı listemizi herhangi bir tüketicinin değiştirmesidir. Bu iyi bir fikir değildir. Bir yedekleme özelliği kullanarak bu sorunu çözelim.
Önce, users
adını _users
olarak değiştirelim. Şimdi, kullanıcıların listesini döndüren herkese açık sabit bir mülk ekleyin. 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 yedek mülkü haline gelir. Sınıfın tüketicileri listeye yalnızca users
üzerinden erişebildiğinden, Repository
sınıfı dışında _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)
}
}
10. Üst düzey ve uzantı işlevleri ve özellikleri
Şu anda Repository
sınıfı, bir User
nesnesi için biçimlendirilmiş kullanıcı adının nasıl hesaplanacağını biliyor. Ancak aynı biçimlendirme mantığını diğer sınıflarda da kullanmak istiyorsak, kopyalama ve yapıştırma veya User
sınıfına taşımamız gerekir.
Kotlin; herhangi bir sınıfın, nesnenin veya arayüzün dışındaki işlevleri ve özellikleri beyan etme olanağı sağlar. Örneğin, List
öğesinin yeni bir örneğini oluşturmak için kullandığımız mutableListOf()
işlevi, doğrudan Standart Kitaplık'taki Collections.kt
öğesinde tanımlanır.
Java'da bazı yardımcı program işlevlerine 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ınız olmadan üst düzey işlevleri bildirebilirsiniz. Ancak Kotlin, uzantı işlevleri oluşturma olanağı da sağlar. Bunlar, belirli bir türü genişleten ancak türün dışında beyan edilen işlevlerdir. Dolayısıyla, bu türden bir yakın ilgi alanları vardır.
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ı özelliğinde tutabiliriz. Repository
sınıfının dışına aynı dosyadan 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ı gibi kullanabiliriz.
Biçimlendirilmiş ad, Repository
sınıfının işlevlerinden değil de 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şlevlerini genişletmek için uzantı işlevlerini kullanır. Iterable
ve Collection
alanlarındaki birçok işlev, uzantı işlevleri olarak uygulanır. Örneğin, önceki adımda kullandığımız map
işlevi, Iterable
üzerindeki bir uzantı işlevidir.
11. Kapsam işlevleri: izin ver, uygula, ile, çalıştır
Repository
sınıf kodumuzda _users
listesine birkaç kullanıcı nesnesi ekliyoruz. Kapsam işlevlerinin yardımıyla bu çağrılar daha deyimsel hale getirilebilir.
Kotlin, kodu nesneye göre erişmesi gerekmeden yalnızca belirli bir nesne bağlamında kod 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
) olabilir, bir bağımsız değişken (it
) olabilir ve bir değer döndürebilir. Ulaşmak istediğiniz şeye bağlı olarak hangisini kullanacağınıza karar verirsiniz.
Bunu hatırlamanıza yardımcı olacak pratik bir yardımcı kısa bilgiyi aşağıda bulabilirsiniz:
Repository
nesnesindeki _users
nesnemizi yapılandırdığımız için apply
işlevini kullanarak kodu daha deyimli 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 yeniden düzenlemeye başlamak için ihtiyacınız olan temel bilgileri ele aldık. Bu yeniden düzenleme, geliştirme platformunuzdan bağımsızdır ve yazdığınız kodun deyimsel olmasına yardımcı olur.
Deyimsel Kotlin kodu, kodun kısa ve öz olmasını sağlar. Kotlin'in sunduğu tüm özellikler sayesinde kodunuzu daha güvenli, daha öz ve daha okunaklı hale getirmenin birçok yolu vardır. Örneğin, init
blokunu ortadan kaldırarak _users
listesini doğrudan beyandaki kullanıcılarla örnekleyerek Repository
sınıfımızı optimize edebiliriz:
private val users = mutableListOf(User("Jane", ""), User("John", null), User("Anne", "Doe"))
Boş değerler, tekliler, dizeler ve koleksiyonlar gibi 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, şu anda şöyle görünen iki Kotlin sınıfına geçtik:
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 TL TL ve Kotlin eşlemeleri aşağıda verilmiştir:
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 aşağıdaki kaynaklara göz atın:
- Kotlin Koanları
- Kotlin Eğitimleri
- Kotlin ile Android uygulamaları geliştirme - ücretsiz kurs
- Programcılar için Kotlin Eğitim Programı
- Java geliştiricileri için Kotlin - Denetim modunda ücretsiz kurs