Bu dokümanda, Java ve Kotlin'de herkese açık API'ler yazmaya yönelik bir dizi kural yer almaktadır. Amaç, kodun diğer dilden kullanıldığında deyimsel olarak hissedilmesidir.
Java (Kotlin tüketimi için)
Sabit anahtar kelime yok
Yöntem veya alan adı olarak Kotlin katı anahtar kelimelerini kullanmayın. Bu nedenle, Kotlin'den arama yaparken çıkış karakterlerini kullanmanız gerekir. Soft anahtar kelimelere, değiştirici anahtar kelimelere ve özel tanımlayıcılara izin verilir.
Örneğin, Mockito'nun when
işlevi, Kotlin'den kullanıldığında vurgu işaretleri 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
üzerinde uzantı işlevlerinin veya Any
ürününde uzantı özelliklerinin adlarını kullanmayın. Aksi takdirde kesinlikle gereklidir. Üye yöntemlerinin ve alanlarının, Any
uzantı işlevleri veya özelliklerinden her zaman öncelikli olmasına karşın, kodu okurken hangisinin çağrıldığını bilmek zor olabilir.
Boş değerlilik ek açıklamaları
Herkese açık API'deki temel olmayan her parametre, dönüş ve alan türü boş değer ek açıklaması içermelidir. Ek açıklamalı olmayan türler, belirsiz benzersizliğe sahip olan "platform" türleri olarak yorumlanır.
Kotlin derleyici işaretleri varsayılan olarak JSR 305 ek açıklamalarını dikkate alır ancak bunları uyarıyla işaretler. Derleyicinin ek açıklamaları hata olarak değerlendirmesini sağlamak için bir işaret de ayarlayabilirsiniz.
Lambda parametreleri son
SAM dönüşümüne uygun olan parametre türleri son 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) { /* … */ }
flowableOnSubscribe, SAM dönüşümü için uygun olduğundan Kotlin'den bu yöntemle yapılan çağrılar aşağıdaki gibi görünür:
Flowable.create({ /* … */ }, BackpressureStrategy.LATEST)
Ancak yöntem imzasındaki parametreler tersine çevrildiyse 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 gösterilmesi için "fasulye" stilinde ön ek kullanılmalıdır.
Erişimci yöntemleri "get" öneki gerektirir veya boole döndürme yöntemleri için "eşittir" ö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şkilendirilmiş mutatör yöntemlerinde "set" ön eki 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)
Özelliklerin özellikler olarak gösterilmesini istiyorsanız "has"/"set" veya "get" öneki olmayan erişimciler gibi standart dışı önekler kullanmayın. Standart olmayan ön eklere sahip yöntemler, yöntemin davranışına göre kabul edilebilir işlevler olarak çağrılabilir.
Operatör aşırı yüklemesi
Kotlin içinde ö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 birlikte 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 tüketimi için)
Dosya adı
Bir dosya üst düzey işlevler veya özellikler içerdiğinde her zaman iyi bir ad sağlamak için dosyaya @file:JvmName("Foo")
ile not ekleyin.
Varsayılan olarak, MyClass.kt'deki bir üst düzey üyeler MyClassKt
adında bir sınıfa girerler. Bu sınıfta ilgi çekici değildir ve dil kullanımını uygulama ayrıntıları olarak sızdırırlar.
Birden fazla dosyadaki üst düzey üyeleri tek bir sınıfta birleştirmek için @file:JvmMultifileClass
öğesini ekleyebilirsiniz.
Lambda bağımsız değişkenleri
Java'dan kullanılması amaçlanan işlev türleri, Unit
döndürme türünü kullanmamalıdır. Bu, deyimsel olmayan açık bir return
Unit.INSTANCE;
ifadesi belirtmeyi gerektirir.
fun sayHi(callback: (String) -> Unit) = /* … */
// Kotlin caller: greeter.sayHi { Log.d("Greeting", "Hello, $it!") }
// Java caller: greeter.sayHi(name -> { Log.d("Greeting", "Hello, " + name + "!"); return Unit.INSTANCE; });
Bu söz dizimi, başka türlere uygulanabilecek, semantik olarak adlandırılmış bir türün sağlanmasına da izin vermez.
Latda türü için Kotlin'de adlandırılmış, tek bir soyut yöntem (SAM) arayüzü tanımlamak, Java sorununu düzeltir ancak lambda söz diziminin Kotlin'de kullanılmasını engeller.
interface GreeterCallback { fun greetName(name: String): Unit } fun sayHi(callback: GreeterCallback) = /* … */
// Kotlin caller: greeter.sayHi(object : GreeterCallback { override fun greetName(name: String) { Log.d("Greeting", "Hello, $name!") } })
// Java caller: greeter.sayHi(name -> Log.d("Greeting", "Hello, " + name + "!"))
Java'da adlandırılmış bir SAM arayüzünün tanımlanması, arayüz türünün açıkça belirtilmesi gereken Kotlin lambda söz diziminin biraz düşük kaliteli bir sürümünün kullanılmasına olanak tanır.
// Defined in Java: interface GreeterCallback { void greetName(String name); }
fun sayHi(greeter: GreeterCallback) = /* … */
// Kotlin caller: greeter.sayHi(GreeterCallback { Log.d("Greeting", "Hello, $it!") })
// Java caller: greeter.sayHi(name -> Log.d("Greeter", "Hello, " + name + "!"));
Java Mevcut öneri, dönüş türü Unit
olduğunda Java'da bozulmuş deneyime rağmen işlev türünü tercih etmektir.
Nothing
genel
Genel parametresi Nothing
olan bir tür, Java'ya ham türleri olarak gösterilir. Ham türler Java'da nadiren kullanılır ve kaçınılmalıdır.
Doküman istisnaları
İşaretli istisnalar atabilecek işlevler bunları @Throws
ile belgelemelidir. Çalışma zamanı istisnaları KDoc'ta belgelenmelidir.
Kotlin'in aksi halde sessizce yayılmasına izin verdiği kontrol edilen istisnaları tetikleyebileceği için bir işlevin yetki verdiği API'lere dikkat edin.
Savunma kopyaları
Herkese açık API'lerden paylaşılan veya sahibi olmayan salt okunur koleksiyonları döndürürken bunları değiştirilemez bir kapsayıcıya sarmalayın veya bir savunma metni kullanın. Kotlin salt okunur özelliğini uygulasa da Java tarafında böyle bir yaptırım yoktur. Sarmalayıcı veya savunma metni olmadan, uzun ömürlü bir koleksiyon başvurusu döndürülerek değişmezler ihlal edilebilir.
Tamamlayıcı işlevler
Tamamlayıcı nesnedeki herkese açık işlevlere statik yöntem olarak eklenmesi için @JvmStatic
ile not eklenmelidir.
Ek açıklama olmadan, bu işlevler yalnızca statik bir 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
ek açıklaması
class KotlinClass { companion object { @JvmStatic fun doWork() { /* … */ } } }
public final class JavaClass { public static void main(String... args) { KotlinClass.doWork(); } }
Tamamlayıcı sabitler
Bir companion object
işlevinde etkin sabit değerler olan const
dışındaki herkese açık mülkler, statik alan olarak gösterilecek @JvmField
ile ek açıklama eklenmelidir.
Ek açıklama olmadan, bu özellikler yalnızca statik Companion
alanında garip şekilde adlandırılmış "alıcılar" olarak kullanılabilir. @JvmField
yerine @JvmStatic
kullanıldığında garip şekilde adlandırılmış "getget" (sınıfı) statik yöntemlere taşınır. Bu da 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
ek açıklama
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
ek açıklaması
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, Java'dan farklı işlevlere sahiptir. Bu kurallar, işlevleri adlandırma şeklinizi değiştirebilir. Her iki dilin kongresinde veya birbiriyle ilişkili standart kitaplık adlarında kullanabilmeleri için adları tasarlamak üzere @JvmName
kullanın.
Bu durum, alıcı türünün konumu farklı olduğu için en sık uzantı işlevleri ve uzantı mülklerinde 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üklemesi
Varsayılan değeri olan parametrelere sahip işlevler @JvmOverloads
kullanmalıdır.
Bu ek açıklama olmadan, herhangi bir varsayılan değeri kullanarak işlevi çağırmak mümkün değildir.
@JvmOverloads
kullanırken, her birinin anlamlı olduğundan emin olmak için oluşturulan yöntemleri inceleyin. Aksi takdirde, memnun oluncaya kadar aşağıdaki yeniden düzenlemelerden birini veya ikisini birden gerçekleştirin:
- Parametre sırasını, varsayılanları sona doğru olanları tercih edecek şekilde değiştirin.
- Varsayılan ayarları 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
ek açıklaması.
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 Alma Kontrolleri
Şartlar
- Android Studio sürümü: 3.2 Canary 10 veya sonraki sürümler
- Android Gradle Plugin sürümü: 3.2 veya üzeri
Desteklenen Kontroller
Şimdi de yukarıda açıklanan birlikte çalışabilirlik sorunlarından bazılarını tespit edip belirlemenize yardımcı olacak Android Lint kontrolleri var. Şu anda yalnızca Kotlin tüketimi için Java ile ilgili sorunlar tespit edilmektedir. Tam olarak desteklenen kontroller şunlardır:
- Bilinmeyen Boş Değer
- Mülk Erişimi
- Hard Kotlin anahtar kelimesi yok
- Son Lambda Parametreleri
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ı işaretleyin:
Şekil 1. Android Studio'daki Kotlin birlikte çalışabilirlik ayarları.
Etkinleştirmek istediğiniz kuralları kontrol ettiğinizde, kod denetimlerinizi çalıştırdığınızda yeni kontroller çalıştırılır (Analiz > Kodu İncele...)
Komut satırı derlemeleri
Komut satırı derlemelerinden bu denetimleri etkinleştirmek için build.gradle
dosyanıza aşağıdaki satırı ekleyin:
Eğlenceli
android { ... lintOptions { enable 'Interoperability' } }
Kotlin
android { ... lintOptions { enable("Interoperability") } }
lintOptions'ta desteklenen yapılandırma gruplarının tamamı için Android Gradle DSL referansına bakın.
Ardından, komut satırından ./gradlew lint
'u çalıştırın.