Bu belge, kodun diğer dilde tüketildiğinde deyimsel olarak algılanması amacıyla Java ve Kotlin'de herkese açık API'ler yazmak için bir kurallar kümesidir.
Java (Kotlin tüketimi için)
Sabit anahtar kelimelere izin verilmez
Yöntem veya alanların adı olarak Kotlin'in kesin anahtar kelimelerinden hiçbirini kullanmayın. Bunlar, Kotlin'den çağrı yaparken kaçmak için vurgu işaretlerinin kullanılmasını gerektirir. Yumuşak anahtar kelimeler, değiştirici anahtar kelimeler ve özel tanımlayıcılara izin verilir.
Örneğin, Mockito'nun when
işlevi, Kotlin'den kullanıldığında vurgu işaretlerinin kullanılmasını gerektirir:
val callable = Mockito.mock(Callable::class.java) Mockito.`when`(callable.call()).thenReturn(/* … */)
Any
uzantı adından kaçının
Yöntemler için Any
üzerindeki uzantı işlevlerinin adlarını veya kesinlikle gerekli olmadığı sürece alanlar için Any
'deki uzantı özelliklerinin adlarını kullanmaktan kaçının. Üye yöntemleri ve alanları, her zaman Any
uzantısının uzantı işlevlerine veya özelliklerine göre önceliklidir, ancak kodu okurken hangisinin çağrıldığını anlamak zor olabilir.
Boş değer atanabilirliği ek açıklamaları
Genel bir API'deki primitif olmayan her parametre, dönüş ve alan türü, boş değer atanabilirlik ek açıklamasına sahip olmalıdır. Ek açıklamalı olmayan türler, belirsiz boşluğa sahip "platform" türleri olarak yorumlanır.
Varsayılan olarak Kotlin derleyici işaretleri, JSR 305 ek açıklamalarını dikkate alır ancak bunları uyarılarla işaretler. Ayrıca derleyicinin ek açıklamaları hata olarak işlemesi için bir işaret de ayarlayabilirsiniz.
Lambda parametreleri en son
SAM dönüşümü için uygun parametre türleri son sırada olmalıdır.
Örneğin, RxJava 2’s Flowable.create()
yöntem imzası şu şekilde tanımlanır:
public staticFlowable create( FlowableOnSubscribe source, BackpressureStrategy mode) { /* … */ }
FlowableOnAbone, SAM dönüşümü için uygun olduğundan Kotlin'den bu yöntemin işlev çağrıları şu şekilde görünür:
Flowable.create({ /* … */ }, BackpressureStrategy.LATEST)
Ancak parametreler yöntem imzasında tersine çevrilmişse, işlev çağrıları sondaki-lambda söz dizimini kullanabilir:
Flowable.create(BackpressureStrategy.LATEST) { /* … */ }
Mülk ön ekleri
Bir yöntemin Kotlin'de mülk olarak temsil edilmesi için katı "fasulye" tarzı ön ek kullanılmalıdır.
Erişimci yöntemleri "get" ön eki veya boole döndürme yöntemleri için "is" ön eki kullanılabilir.
public final class User { public String getName() { /* … */ } public boolean isActive() { /* … */ } }
val name = user.name // Invokes user.getName() val active = user.isActive // Invokes user.isActive()
İlişkili mutatör yöntemleri için bir "set" öneki gerekir.
public final class User { public String getName() { /* … */ } public void setName(String name) { /* … */ } public boolean isActive() { /* … */ } public void setActive(boolean active) { /* … */ } }
user.name = "Bob" // Invokes user.setName(String) user.isActive = true // Invokes user.setActive(boolean)
Yöntemlerin mülk olarak gösterilmesini istiyorsanız "has"/"set" veya "get" önekli olmayan "get" ön ekleri gibi standart olmayan ön ekleri kullanmayın. Standart olmayan öneklere sahip yöntemler, yöntemin davranışına bağlı olarak kabul edilebilir işlev olarak çağrılabilir.
Operatör aşırı yüklemesi
Kotlin'de özel çağrı sitesi söz dizimine (ör. operatör aşırı yüklemesi) izin veren yöntem adlarına dikkat edin. Bu tür yöntem adlarının, kısaltılmış söz dizimiyle kullanımının anlamlı olduğundan emin olun.
public final class IntBox { private final int value; public IntBox(int value) { this.value = value; } public IntBox plus(IntBox other) { return new IntBox(value + other.value); } }
val one = IntBox(1) val two = IntBox(2) val three = one + two // Invokes one.plus(two)
Kotlin (Java kullanımı için)
Dosya adı
Bir dosya üst düzey işlevler veya özellikler içerdiğinde, güzel bir ad vermek için her zaman @file:JvmName("Foo")
ile açıklama ekleyin.
Varsayılan olarak, MyClass.kt dosyasındaki üst düzey üyeler MyClassKt
adında, cazip olmayan ve uygulama ayrıntısı olarak dili sızdıran bir sınıfa dahil edilir.
Birden fazla dosyanın üst düzey üyelerini tek bir sınıfta birleştirmek için @file:JvmMultifileClass
eklemeyi düşünün.
Lambda bağımsız değişkenleri
Java'da tanımlanan tek yöntem arayüzleri (SAM), uygulamayı deyimsel bir şekilde satır içi yapan lambda söz dizimi kullanılarak hem Kotlin'de hem de Java'da uygulanabilir. Kotlin, bu tür arayüzleri tanımlamak için her biri küçük bir farka sahip birkaç seçenek sunar.
Tercih edilen tanım
Java'dan kullanılması amaçlanan üst düzey işlevler, Java çağrılarının Unit.INSTANCE
değerini döndürmesini gerektireceğinden Unit
değerini döndüren işlev türlerini almamalıdır.
İşlev türünü imzada satır içine eklemek yerine işlevsel (SAM) arayüzleri kullanın.
Ayrıca lambda olarak kullanılması beklenen arayüzleri tanımlarken, Kotlin'in deyimsel kullanımına olanak tanırken normal arayüzler yerine işlevsel (SAM) arayüzleri kullanmayı da düşünebilirsiniz.
Şu Kotlin tanımını düşünün:
fun interface GreeterCallback { fun greetName(String name) } fun sayHi(greeter: GreeterCallback) = /* … */
Kotlin'den çağrıldığında:
sayHi { println("Hello, $it!") }
Java'dan çağrıldığında:
sayHi(name -> System.out.println("Hello, " + name + "!"));
İşlev türü bir Unit
döndürmediğinde bile, çağrı yapan kişilerin bunu yalnızca lambda'larla (hem Kotlin hem de Java'da) değil, adlandırılmış bir sınıfla uygulamasına izin vermek için adlandırılmış arayüz yapmak iyi bir fikir olabilir.
class MyGreeterCallback : GreeterCallback { override fun greetName(name: String) { println("Hello, $name!"); } }
Unit
değerini döndüren işlev türlerinden kaçının
Şu Kotlin tanımını düşünün:
fun sayHi(greeter: (String) -> Unit) = /* … */
Java çağrılarının Unit.INSTANCE
değerini döndürmesini gerektirir:
sayHi(name -> { System.out.println("Hello, " + name + "!"); return Unit.INSTANCE; });
Uygulamanın belirli bir görüntüye sahip olması gerektiğinde işlevsel arayüzlerden kaçının.
Arayüz uygulamasının bir durumu olması gerektiğinde, lambda söz diziminin kullanılması anlamlı olmaz.
Karşılaştırılabilir, this
ile other
karşılaştırılması amaçlandığından ve lambda'larda this
olmadığı için belirgin bir örnektir. Arayüzün önüne fun
getirilmemesi, çağrıyı yapanı object : ...
söz dizimini kullanmaya zorlar. Bu da, arayüzün durum bilgisine sahip olmasını sağlayarak arayana ipucu sağlar.
Şu Kotlin tanımını düşünün:
// No "fun" prefix. interface Counter { fun increment() }
Kotlin'de lambda söz dizimini engelleyerek aşağıdaki daha uzun sürümü gerektirir:
runCounter(object : Counter { private var increments = 0 // State override fun increment() { increments++ } })
Nothing
genel içerikten kaçının
Genel parametresi Nothing
olan bir tür, Java'ya ham tür olarak gösterilir. Java'da ham türler nadiren kullanılır ve bundan kaçınılmalıdır.
Doküman istisnaları
İşaretlenmiş istisnalar atabilecek işlevler, bunları @Throws
ile belgelemelidir. Çalışma zamanı istisnaları KDoc'ta belgelenmelidir.
Kotlin'in aksi halde sessizce yaymasına izin verdiği işaretli istisnaları verebileceğinden, bir işlevin yetki verdiği API'lere dikkat edin.
Savunma kopyaları
Herkese açık API'lerden paylaşılan veya sahip olmadığınız salt okunur koleksiyonları geri döndürüyorsanız bunları değiştirilemez bir container'a sarmalayın veya savunma amaçlı bir kopya oluşturun. Kotlin, salt okunur özelliklerini zorunlu kılmasına rağmen, Java tarafında böyle bir zorunluluk yoktur. Sarmalayıcı veya savunma metni olmadan, uzun ömürlü bir koleksiyon referansı döndürülerek değişmez değerler ihlal edilebilir.
Tamamlayıcı işlevler
Tamamlayıcı nesnedeki herkese açık işlevlere statik yöntem olarak sunulmaları için @JvmStatic
ek açıklaması eklenmelidir.
Ek açıklama olmadan, bu işlevler yalnızca statik Companion
alanında örnek yöntemleri olarak kullanılabilir.
Yanlış: Ek açıklama yok
class KotlinClass { companion object { fun doWork() { /* … */ } } }
public final class JavaClass { public static void main(String... args) { KotlinClass.Companion.doWork(); } }
Doğru: @JvmStatic
notu
class KotlinClass { companion object { @JvmStatic fun doWork() { /* … */ } } }
public final class JavaClass { public static void main(String... args) { KotlinClass.doWork(); } }
Tamamlayıcı sabit değerleri
Bir companion object
içinde etkili sabitler olan herkese açık, const
olmayan mülklere statik alan olarak gösterilmeleri için @JvmField
ile not eklenmelidir.
Ek açıklama olmadan, bu özellikler statik Companion
alanında yalnızca garip adlandırılmış örnek "getters" olarak kullanılabilir. @JvmField
yerine @JvmStatic
kullanıldığında, garip
olarak adlandırılan "getters" sınıftaki statik yöntemlere taşınır. Bu yöntem hâlâ yanlıştır.
Yanlış: Ek açıklama yok
class KotlinClass { companion object { const val INTEGER_ONE = 1 val BIG_INTEGER_ONE = BigInteger.ONE } }
public final class JavaClass { public static void main(String... args) { System.out.println(KotlinClass.INTEGER_ONE); System.out.println(KotlinClass.Companion.getBIG_INTEGER_ONE()); } }
Yanlış: @JvmStatic
notu
class KotlinClass { companion object { const val INTEGER_ONE = 1 @JvmStatic val BIG_INTEGER_ONE = BigInteger.ONE } }
public final class JavaClass { public static void main(String... args) { System.out.println(KotlinClass.INTEGER_ONE); System.out.println(KotlinClass.getBIG_INTEGER_ONE()); } }
Doğru: @JvmField
notu
class KotlinClass { companion object { const val INTEGER_ONE = 1 @JvmField val BIG_INTEGER_ONE = BigInteger.ONE } }
public final class JavaClass { public static void main(String... args) { System.out.println(KotlinClass.INTEGER_ONE); System.out.println(KotlinClass.BIG_INTEGER_ONE); } }
Deyimsel adlandırma
Kotlin'in Java'dan farklı çağırma kuralları vardır. Bu da işlevleri adlandırma şeklinizi değiştirebilir. Her iki dilin kurallarına uygun veya ilgili standart kitaplık adlandırmalarıyla eşleşecek şekilde adlar tasarlamak için @JvmName
kullanın.
Bu durum, alıcı türünün konumu farklı olduğundan, en çok uzantı işlevleri ve uzantı özelliklerinde görülür.
sealed class Optionaldata class Some (val value: T): Optional () object None : Optional () @JvmName("ofNullable") fun T?.asOptional() = if (this == null) None else Some(this)
// FROM KOTLIN: fun main(vararg args: String) { val nullableString: String? = "foo" val optionalString = nullableString.asOptional() }
// FROM JAVA: public static void main(String... args) { String nullableString = "Foo"; OptionaloptionalString = Optionals.ofNullable(nullableString); }
Varsayılanlar için işlev aşırı yüklemeleri
Varsayılan değere sahip parametrelere sahip işlevler @JvmOverloads
kullanmalıdır.
Bu ek açıklama olmadan, işlevi herhangi bir varsayılan değer kullanarak çağırmak mümkün değildir.
@JvmOverloads
kullanırken oluşturulan yöntemleri inceleyerek her birinin anlamlı olduğundan emin olun. Uyuşmazsa memnun kalana kadar aşağıdaki yeniden düzenleme işlemlerinden birini veya ikisini birden yapın:
- Parametre sırasını, varsayılanları sona eren parametreleri tercih edecek şekilde değiştirin.
- Varsayılanları manuel işlev aşırı yüklemelerine taşıyın.
Yanlış: Hayır @JvmOverloads
class Greeting { fun sayHello(prefix: String = "Mr.", name: String) { println("Hello, $prefix $name") } }
public class JavaClass { public static void main(String... args) { Greeting greeting = new Greeting(); greeting.sayHello("Mr.", "Bob"); } }
Doğru: @JvmOverloads
notu.
class Greeting { @JvmOverloads fun sayHello(prefix: String = "Mr.", name: String) { println("Hello, $prefix $name") } }
public class JavaClass { public static void main(String... args) { Greeting greeting = new Greeting(); greeting.sayHello("Bob"); } }
Tüy Döşeme Kontrolleri
Şartlar
- Android Studio sürümü: 3.2 Canary 10 veya sonraki bir sürüm
- Android Gradle Plugin sürümü: 3.2 veya üzeri
Desteklenen Kontroller
Artık yukarıda açıklanan birlikte çalışabilirlik sorunlarından bazılarını tespit etmenize ve işaretlemenize yardımcı olacak Android Lint kontrolleri mevcuttur. Şu anda yalnızca Java'daki sorunlar (Kotlin tüketimi için) algılanmaktadır. Desteklenen kontroller şunlardır:
- Bilinmeyen Boşluk
- Mülk Erişimi
- Hard Kotlin anahtar kelimeleri yok
- Lambda Parametrelerinin Son Değeri
Android Studio
Bu kontrolleri etkinleştirmek için Dosya > Tercihler > Düzenleyici > Denetimler'e gidin ve Kotlin Birlikte Çalışabilirliği altında etkinleştirmek istediğiniz kuralları kontrol edin:
Etkinleştirmek istediğiniz kuralları kontrol ettikten sonra, kod denetlemelerinizi çalıştırdığınızda yeni denetimler çalışır (Analiz > Kodu İncele...)
Komut satırı derlemeleri
Bu denetimleri komut satırı derlemelerinden etkinleştirmek için build.gradle
dosyanıza aşağıdaki satırı ekleyin:
Modern
android { ... lintOptions { enable 'Interoperability' } }
Kotlin
android { ... lintOptions { enable("Interoperability") } }
lintOptions'ta desteklenen yapılandırmaların tamamı için Android Gradle DSL referansına bakın.
Ardından, komut satırından ./gradlew lint
komutunu çalıştırın.