Bu konu, Android için geliştirme yaparken Kotlin dilinin en faydalı yönlerinden bazılarına odaklanmaktadır.
Parçalarla çalışma
Aşağıdaki bölümlerde Kotlin'in en iyi özelliklerinden bazılarını öne çıkarmak için Fragment
örnekleri kullanılmaktadır.
Devralındı
class
anahtar kelimesini kullanarak Kotlin'de bir sınıf bildirebilirsiniz. Aşağıdaki örnekte LoginFragment
, Fragment
sınıfının bir alt sınıfıdır. Devralınması, :
operatörünü alt sınıf ile üst sınıfı arasında kullanarak belirtebilirsiniz:
class LoginFragment : Fragment()
Bu sınıf beyanında LoginFragment
, üst sınıfının oluşturucusunu (Fragment
) çağırmaktan sorumludur.
LoginFragment
içinde, Fragment
içindeki durum değişikliklerine yanıt vermek için bir dizi yaşam döngüsü geri çağırmasını geçersiz kılabilirsiniz. Bir işlevi geçersiz kılmak için aşağıdaki örnekte gösterildiği gibi override
anahtar kelimesini kullanın:
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.login_fragment, container, false)
}
Üst sınıftaki bir işleve başvuruda bulunmak için aşağıdaki örnekte gösterildiği gibi super
anahtar kelimesini kullanın:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
Boş değer atanabilirlik ve başlatma
Önceki örneklerde, geçersiz kılınan yöntemlerdeki bazı parametrelerde soru işareti ?
ile soneklenen türler vardır. Bu, bu parametreler için iletilen bağımsız değişkenlerin boş olabileceği anlamına gelir. Boş değerlerini güvenli bir şekilde işlediğinizden emin olun.
Kotlin'de, bir nesneyi tanımlarken nesnenin özelliklerini başlatmanız gerekir.
Bu, bir sınıfın bir örneğini elde ettiğinizde, erişilebilir özelliklerinden herhangi birine hemen referans verebileceğiniz anlamına gelir. Ancak Fragment
içindeki View
nesneleri, Fragment#onCreateView
çağrılana kadar şişirmeye hazır değildir. Bu nedenle, View
için özellik başlatmayı ertelemenin bir yolunu bulmanız gerekir.
lateinit
, mülk başlatmayı ertelemenize olanak tanır. lateinit
kullanırken mülkünüzü en kısa sürede başlatmanız gerekir.
Aşağıdaki örnek, onViewCreated
içinde View
nesneleri atamak için lateinit
kullanımını gösterir:
class LoginFragment : Fragment() {
private lateinit var usernameEditText: EditText
private lateinit var passwordEditText: EditText
private lateinit var loginButton: Button
private lateinit var statusTextView: TextView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
usernameEditText = view.findViewById(R.id.username_edit_text)
passwordEditText = view.findViewById(R.id.password_edit_text)
loginButton = view.findViewById(R.id.login_button)
statusTextView = view.findViewById(R.id.status_text_view)
}
...
}
SAM dönüşümü
OnClickListener
arayüzünü uygulayarak Android'de tıklama etkinliklerini dinleyebilirsiniz. Button
nesneleri, OnClickListener
uygulamasını üstlenen setOnClickListener()
işlevi içerir.
OnClickListener
, uygulamanız gereken tek bir soyut yönteme (onClick()
) sahip. setOnClickListener()
, OnClickListener
öğesini her zaman bağımsız değişken olarak aldığından ve OnClickListener
daima aynı soyut yönteme sahip olduğundan bu uygulama, Kotlin'de anonim bir işlev kullanılarak temsil edilebilir. Bu işlem, Tekli Soyut Yöntem dönüşümü veya SAM dönüşümü olarak bilinir.
SAM dönüşümü, kodunuzu önemli ölçüde daha temiz hale getirebilir. Aşağıdaki örnekte, Button
için OnClickListener
uygulamak üzere SAM dönüşümünün nasıl kullanılacağı gösterilmektedir:
loginButton.setOnClickListener {
val authSuccessful: Boolean = viewModel.authenticate(
usernameEditText.text.toString(),
passwordEditText.text.toString()
)
if (authSuccessful) {
// Navigate to next screen
} else {
statusTextView.text = requireContext().getString(R.string.auth_failed)
}
}
setOnClickListener()
işlevine iletilen anonim işlev içindeki kod, bir kullanıcı loginButton
öğesini tıkladığında yürütülür.
Tamamlayıcı nesneler
Tamamlayıcı nesneler, kavramsal olarak bir türe bağlı olan, ancak belirli bir nesneye bağlı olmayan değişkenleri veya işlevleri tanımlamak için bir mekanizma sunar. Tamamlayıcı nesneler, değişkenler ve yöntemler için Java'nın static
anahtar kelimesini kullanmaya benzer.
Aşağıdaki örnekte TAG
, String
sabitidir. Her LoginFragment
örneği için benzersiz bir String
örneğine ihtiyacınız yoktur. Bu nedenle, bunu bir tamamlayıcı nesnede tanımlamanız gerekir:
class LoginFragment : Fragment() {
...
companion object {
private const val TAG = "LoginFragment"
}
}
TAG
öğesini dosyanın en üst düzeyinde tanımlayabilirsiniz ancak dosyada, üst düzeyde de tanımlanmış çok sayıda değişken, işlev ve sınıf bulunabilir. Tamamlayıcı nesneler, söz konusu sınıfın belirli bir örneğine başvurmadan değişkenlerin, işlevlerin ve sınıf tanımının bağlanmasına yardımcı olur.
Mülk yetkisi
Özellikleri başlatırken Android'in daha yaygın kalıplarından bazılarını tekrarlayabilirsiniz (örneğin, bir Fragment
içindeki ViewModel
öğesine erişme). Fazla sayıda yinelenen koddan kaçınmak için Kotlin'in özellik yetkisi söz dizimini kullanabilirsiniz.
private val viewModel: LoginViewModel by viewModels()
Mülk yetkilendirme, uygulamanız genelinde tekrar kullanabileceğiniz yaygın bir uygulama sağlar. Android KTX, size mülk için yetki verilmiş bazı kullanıcılar sağlar.
Örneğin viewModels
, kapsamı geçerli Fragment
öğesine ayarlanmış bir ViewModel
getirir.
Mülk yetkisi, yansımayı kullanır ve bu da bir miktar performans ek yüküne neden olur. Ödün verme, geliştirme süresinden tasarruf sağlayan kısa ve öz bir söz dizimidir.
Boş değer atanabilirliği
Kotlin, uygulamanızın genelinde tür güvenliğini koruyan katı null kullanılabilirlik kuralları sağlar. Kotlin'de nesne referansları varsayılan olarak null değerler içeremez. Bir değişkene null değer atamak için temel türün sonuna ?
ekleyerek nullable değişken türünü bildirmeniz gerekir.
Örneğin, aşağıdaki ifade Kotlin'de yasa dışıdır. name
, String
türündedir ve boş bırakılamaz:
val name: String = null
Boş değere izin vermek için, aşağıdaki örnekte gösterildiği gibi null özellikli bir String
türü (String?
) kullanmanız gerekir:
val name: String? = null
Birlikte çalışabilirlik
Kotlin'in katı kuralları, kodunuzu daha güvenli ve özlü hale getirir. Bu kurallar, uygulamanızın kilitlenmesine neden olacak NullPointerException
olasılığını azaltır. Ayrıca, kodunuzda yapmanız gereken boş denetimlerin sayısını da azaltır.
Çoğu Android API, Java programlama dilinde yazıldığından, bir Android uygulaması yazarken Kotlin dışı kodları da çağırmanız gerekir.
Boş değer atanabilirliği, Java ve Kotlin'in davranış açısından farklılık gösterdiği önemli bir alandır. Java, null değer söz dizimiyle daha az katıdır.
Örnek olarak, Account
sınıfının name
adlı String
özelliği de dahil olmak üzere birkaç özelliği vardır. Java, null ile ilgili Kotlin kurallarına sahip değildir. Bunun yerine, null değer atayıp atayamayacağınızı açıkça belirtmek için isteğe bağlı null değer ek açıklamalarını kullanır.
Android çerçevesi birincil olarak Java'da yazıldığından nullability ek açıklamaları olmadan API'leri çağırırken bu senaryoyla karşılaşabilirsiniz.
Platform türleri
Java Account
sınıfında tanımlanmış ek açıklamalı bir name
üyesine referans vermek için Kotlin kullanırsanız derleyici, String
öğesinin Kotlin'deki bir String
ile mi yoksa String?
ile mi eşlendiğini bilemez. Bu belirsizlik, bir platform türü (String!
) ile temsil edilir.
String!
, Kotlin derleyici için özel bir anlama sahip değildir. String!
, String
veya String?
öğesini temsil edebilir. Derleyici, her iki türde değer atamanıza olanak tanır. Türü String
olarak temsil eder ve boş değer atarsanız bir NullPointerException
atama riskiyle karşı karşıya olduğunuzu unutmayın.
Bu sorunu çözmek için Java'da her kod yazdığınızda null değer ek açıklamalarını kullanmanız gerekir. Bu ek açıklamalar hem Java hem de Kotlin geliştiricilerine yardımcı olur.
Örneğin, Java'da tanımlandığı şekliyle Account
sınıfı şu şekildedir:
public class Account implements Parcelable {
public final String name;
public final String type;
private final @Nullable String accessId;
...
}
Üye değişkenlerinden biri olan accessId
, boş değer alabileceğini belirten @Nullable
ek açıklamasına sahiptir. Bu durumda Kotlin, accessId
öğesini
String?
olarak değerlendirir.
Bir değişkenin hiçbir zaman boş olamayacağını belirtmek için @NonNull
ek açıklamasını kullanın:
public class Account implements Parcelable {
public final @NonNull String name;
...
}
Bu senaryoda name
, Kotlin'de null olmayan bir String
olarak kabul edilir.
Tüm yeni Android API'lerinde ve mevcut birçok Android API'sinde boş değer atanabilirlik ek açıklamaları yer almaktadır. Birçok Java kitaplığı, hem Kotlin hem de Java geliştiricilerini daha iyi desteklemek için null değer ek açıklamaları eklemiştir.
Boş değer kullanılabilirliğini işleme
Bir Java türünden emin değilseniz boş değer olarak kabul etmelisiniz.
Örneğin, Account
sınıfının name
üyesine ek açıklama eklenmemiştir. Bu nedenle, bunun boş değer atan bir String
olduğunu varsaymanız gerekir.
name
değerini, başında veya sonunda boşluk olmayacak şekilde kırpmak isterseniz Kotlin'in trim
işlevini kullanabilirsiniz. String?
öğesini birkaç farklı şekilde güvenle kırpabilirsiniz. Bu yöntemlerden biri, aşağıdaki örnekte gösterildiği gibi not-null onaylama operatörünü (!!
) kullanmaktır:
val account = Account("name", "type")
val accountName = account.name!!.trim()
!!
operatörü, sol tarafındaki her şeyi null olmayan değer olarak değerlendirir. Bu nedenle bu durumda name
, boş olmayan bir String
olarak ele alınır. Soldaki ifadenin sonucu null ise uygulamanız NullPointerException
döndürür.
Bu operatör hızlı ve kolay bir işlemdir ancak kodunuza NullPointerException
örneklerini yeniden ekleyebileceği için dikkatli kullanılmalıdır.
Aşağıdaki örnekte gösterildiği gibi güvenli arama operatörünü (?.
) kullanmak daha güvenli bir seçenektir:
val account = Account("name", "type")
val accountName = account.name?.trim()
Güvenli arama operatörünü kullandığınızda name
null değilse name?.trim()
işlevinin sonucu, başında veya sonunda boşluk olmayan bir ad değeridir. name
null ise name?.trim()
işlevinin sonucu null
olur. Bu durum, uygulamanızın bu ifadeyi yürütürken hiçbir zaman NullPointerException
işlemi gönderemeyeceği anlamına gelir.
Güvenli arama operatörü sizi olası bir NullPointerException
işleminden kurtarsa da sonraki ifadeye boş bir değer iletir. Bunun yerine, aşağıdaki örnekte gösterildiği gibi Elvis operatörü (?:
) kullanarak null değerleri hemen işleyebilirsiniz:
val account = Account("name", "type")
val accountName = account.name?.trim() ?: "Default name"
Elvis operatörünün sol tarafındaki ifadenin sonucu null ise sağ taraftaki değer accountName
değerine atanır. Bu teknik, aksi takdirde boş olacak bir varsayılan değer sağlamak için yararlıdır.
Aşağıdaki örnekte gösterildiği gibi, Elvis operatörünü bir işlevden erken geri dönmek için de kullanabilirsiniz:
fun validateAccount(account: Account?) {
val accountName = account?.name?.trim() ?: "Default name"
// account cannot be null beyond this point
account ?: return
...
}
Android API değişiklikleri
Android API'leri giderek daha Kotlin dostu oluyor. Android'in AppCompatActivity
ve Fragment
gibi en yaygın API'lerinin çoğu boş değer atanabilirlik ek açıklamaları içerir ve Fragment#getContext
gibi bazı çağrıların Kotlin dostu daha alternatifleri vardır.
Örneğin, bir Fragment
öğesinin Context
öğesine erişim neredeyse her zaman null değildir. Bunun nedeni, Fragment
içinde yaptığınız çağrıların çoğunun Fragment
öğesi bir Activity
'ye (Context
alt sınıfı) bağlı olduğu halde gerçekleşmesidir. Bununla birlikte, Fragment
öğesinin bir Activity
öğesine eklenmediği senaryolar olduğu için Fragment#getContext
her zaman null olmayan bir değer döndürmez. Bu nedenle, Fragment#getContext
dönüş türü null olabilir.
Fragment#getContext
öğesinden döndürülen Context
null özellikli olduğundan (ve ek açıklama @Nullable olarak eklendiğinden) Kotlin kodunuzda Context?
olarak değerlendirmeniz gerekir.
Bu, özelliklerine ve işlevlerine erişmeden önce boş değer sorununu ele almak için daha önce bahsedilen operatörlerden birinin uygulanması anlamına gelir. Bu senaryoların bazılarında Android, bu kolaylığı sağlayan alternatif API'ler içerir.
Örneğin Fragment#requireContext
, null olmayan bir Context
döndürür ve bir Context
null olduğunda çağrılırsa bir IllegalStateException
döndürür. Bu şekilde, güvenli arama operatörlerine veya geçici çözümlere gerek kalmadan sonuçta ortaya çıkan Context
sonucunu null olmayan değer olarak değerlendirebilirsiniz.
Mülk başlatma
Kotlin'deki özellikler varsayılan olarak başlatılmaz. Bunlar, çevreleyen sınıf başlatıldığında başlatılmalıdır.
Özellikleri birkaç farklı şekilde başlatabilirsiniz. Aşağıdaki örnek, sınıf bildiriminde bir index
değişkeninin nasıl değer atanarak başlatılacağını gösterir:
class LoginFragment : Fragment() {
val index: Int = 12
}
Bu başlatma, bir başlatıcı bloğunda da tanımlanabilir:
class LoginFragment : Fragment() {
val index: Int
init {
index = 12
}
}
Yukarıdaki örneklerde index
, bir LoginFragment
oluşturulduğunda başlatılır.
Bununla birlikte, nesne oluşturma sırasında başlatılamayan bazı özellikleriniz olabilir. Örneğin, Fragment
içinden bir View
öğesine referans vermek isteyebilirsiniz. Bu, düzenin önce genişletilmesi gerektiği anlamına gelir. Fragment
oluşturulurken enflasyon meydana gelmez. Bunun yerine, Fragment#onCreateView
çağrılırken ses şişirilmiş olur.
Bu senaryoyu ele almanın bir yolu, görünümü boş değerli olarak bildirmek ve görünümü en kısa sürede aşağıdaki örnekte gösterildiği gibi başlatmaktır:
class LoginFragment : Fragment() {
private var statusTextView: TextView? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
statusTextView = view.findViewById(R.id.status_text_view)
statusTextView?.setText(R.string.auth_failed)
}
}
Bu beklendiği gibi çalışsa da, artık her başvuruda bulunduğunuzda View
öğesinin null değerini yönetmeniz gerekir. Daha iyi bir çözüm, aşağıdaki örnekte gösterildiği gibi View
başlatma için lateinit
kullanılmasıdır:
class LoginFragment : Fragment() {
private lateinit var statusTextView: TextView
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
statusTextView = view.findViewById(R.id.status_text_view)
statusTextView.setText(R.string.auth_failed)
}
}
lateinit
anahtar kelimesi, bir nesne oluşturulduğunda bir özelliği başlatmaktan kaçınmanızı sağlar. Mülkünüze başlatılmadan önce referans verilirse Kotlin bir UninitializedPropertyAccessException
bildirir. Bu nedenle, mülkünüzü en kısa sürede başlattığınızdan emin olun.