Kotlin-Java birlikte çalışabilirlik kılavuzu

Bu doküman, Java ve Kotlin'de herkese açık API'ler yazmaya yönelik bir kural grubudur bu kodu kullanabilirsiniz. Bunun amacı, kodun diğer dili'ne dokunun.

Son güncelleme: 29.07.2024

Java (Kotlin tüketimi için)

Sabit anahtar kelime yok

Yöntem adı olarak Kotlin'in katı anahtar kelimelerinden hiçbirini kullanmayın girin. Bunlar, Kotlin. Geçici 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şareti gerektirir:

val callable = Mockito.mock(Callable::class.java)
Mockito.`when`(callable.call()).thenReturn(/* … */)

Any uzantı adından kaçının

Şu URL'ler için Any eklentisindeki uzantı işlevlerinin adlarını kullanmaktan kaçının: yöntemlerini veya Any eklentisindeki uzantı özelliklerinin adlarını alanlarına girmenizi öneririz. Üye yöntemleri ve alanları her zaman Any uzantılı işlevlere veya özelliklerine göre öncelikliyse olduğunu anlamak zor olabilir.

Boş değer atanabilirlik ek açıklamaları

Genel bir API'deki primitif olmayan her parametre, döndürme ve alan türü değer atanabilirlik ek açıklamasına sahip. Ek açıklaması olmayan türler, "platform" türler olarak ayarlanmıştır.

Varsayılan olarak, Kotlin derleyici işaretleri JSR 305 ek açıklamalarını dikkate alır ancak işaretler uyarı içeriyor. Ayrıca, derleyicinin ek açıklamaları hata olarak gösterir.

Lambda parametreleri sonuncu

SAM dönüşümü için uygun parametre türleri "son" olmalıdır.

Örneğin, RxJava 2'nin Flowable.create() yöntem imzası şu şekilde tanımlanır:

public static <T> Flowable<T> create(
    FlowableOnSubscribe<T> source,
    BackpressureStrategy mode) { /* … */ }

FlowableOnChannel, SAM dönüşümü için uygun olduğundan Kotlin'deki bu yöntem aşağıdaki gibi görünür:

Flowable.create({ /* … */ }, BackpressureStrategy.LATEST)

Ancak parametreler yöntem imzasında ters çevrildiyse fonksiyon şöyle bir tercihte bulunacağız:

Flowable.create(BackpressureStrategy.LATEST) { /* … */ }

Mülk önekleri

Bir yöntemin Kotlin'de özellik olarak temsil edilmesi için katı "fasulye" stili önek kullanılmalıdır.

Erişimci yöntemleri, get öneki veya boole döndüren yöntemler için is gerektirir ö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öntemleri için 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 özellik olarak gösterilmesini istiyorsanız has, set veya get olmayan önekli erişimciler. Standart olmayan ön ekler içeren yöntemler işlevi olarak çağrılabilir; Bunlar, işleve bağlı olarak yöntemidir.

Operatör aşırı yüklemesi

Özel çağrı-site söz dizimine izin veren yöntem adlarına (ör. operatör aşırı yüklemesi). Yöntemin kısaltılmış söz dizimiyle kullanılması mantıklıdır.

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 ek açıklama ekleyin güzel bir ad için @file:JvmName("Foo") ile değiştirin.

Varsayılan olarak, MyClass.kt dosyasındaki en üst düzey üyeler, MyClassKt, uygun değildir ve uygulama sırasında dili sızdırır bolca fırsat sunuyor.

Şu gruptaki üst düzey üyeleri birleştirmek için @file:JvmMultifileClass ekleyin: tek sınıfa dönüştürmenizi sağlar.

Lambda bağımsız değişkenleri

Java'da tanımlanan tek yöntem arayüzleri (SAM) hem Kotlin'de uygulanabilir lambda söz dizimini kullanarak Java ve Java sağlar. Kotlin, bu tür arayüzleri tanımlamak için kullanabileceğiniz çeşitli seçeneklere sahiptir. Bu seçeneklerin her birinde farkına varır.

Tercih edilen tanım

Java'dan kullanılması amaçlanan daha üst düzey işlevler Unit değerini döndüren işlev türlerini Java arayanlarının Unit.INSTANCE döndürmesini gerektirir. Fonksiyonu satır içine almak yerine İmzayı yazmak için işlevsel (SAM) arayüzleri kullanın. Ayrıca normal arayüzler yerine işlevsel (SAM) arayüzler kullanmayı düşünün lambda olarak kullanılması beklenen arayüzleri tanımlarken kullanılan arayüzler Bu, Kotlin'in deyimsel kullanımına olanak tanır.

Şu Kotlin tanımına bakı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ürmese bile iyi bir değer olabilir arayanların bunu adlandırılmış bir sınıfını da (hem lambdas hem de Kotlin ve Java'da) kapsar.

class MyGreeterCallback : GreeterCallback {
  override fun greetName(name: String) {
    println("Hello, $name!");
  }
}

Unit değerini döndüren işlev türlerini kullanmaktan kaçının

Şu Kotlin tanımına bakın:

fun sayHi(greeter: (String) -> Unit) = /* … */

Java arayanlarının Unit.INSTANCE döndürmesini gerektirir:

sayHi(name -> {
  System.out.println("Hello, " + name + "!");
  return Unit.INSTANCE;
});

Uygulamanın, durum bilgisi içereceği durumlarda işlevsel arayüzler kullanmaktan kaçının

Arayüz uygulamasının bir durum içermesi istendiğinde lambda söz dizimi mantıklı değil. Karşılaştırılabilir, bunun önemli bir örneğidir. this ile other karşılaştırması yapıldığından ve lambdalarda this yok. Değil arayüzün fun önekiyle eklenmesi çağrıyı object : ... kullanmaya zorlar söz dizimini kullanır. Bu, duruma sahip olmasına olanak tanıyarak arayana bir ipucu sağlar.

Şu Kotlin tanımına bakın:

// No "fun" prefix.
interface Counter {
  fun increment()
}

Kotlin'de lambda söz dizimini engeller ve şu daha uzun sürümü gerektirir:

runCounter(object : Counter {
  private var increments = 0 // State

  override fun increment() {
    increments++
  }
})

Nothing genel anahtar kelime kullanmaktan kaçının

Genel parametresi Nothing olan bir tür, Java'ya ham tür olarak sunulur. Çiğ türleri Java'da nadiren kullanılır ve bundan kaçınılmalıdır.

Doküman istisnaları

İşaretli istisnalar atabilen işlevler, bunları @Throws Çalışma zamanı istisnaları KDoc'ta belgelenmelidir.

Bir işlevin yetki verdiği API'lere dikkat edin, çünkü bu API'ler kontrol edilmiş olabilir Ancak bu istisnalar geçerli olabilir.

Savunma kopyaları

Herkese açık API'lerden paylaşılan veya sahip olunmayan koleksiyonları döndürürken sarmala bunları değiştirilemez bir kapsayıcıda saklama veya savunma amaçlı kopyalama yapma. Kotlin'e rağmen salt okunur özelliklerini zorunlu kılmak için yanı sıra. Sarmalayıcı veya savunma amaçlı kopya olmadan, değişmeyen değerler uzun ömürlü bir koleksiyon referansı döndürme

Tamamlayıcı işlevleri

Tamamlayıcı nesnedeki herkese açık işlevlere @JvmStatic ile ek açıklama eklenmelidir ele alacağız.

Ek açıklama olmadan, bu işlevler yalnızca örnek yöntemi olarak kullanılabilir statik Companion alanında çalışır.

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 not

class KotlinClass {
    companion object {
        @JvmStatic fun doWork() {
            /* … */
        }
    }
}
public final class JavaClass {
    public static void main(String... args) {
        KotlinClass.doWork();
    }
}

Tamamlayıcı sabit değerler

companion object içinde etkili sabit değerler olan, const olmayan herkese açık mülklerin statik alan olarak kullanıma sunulması için @JvmField ile not eklenmesi gerekir.

Ek açıklama olmadığında bu özellikler yalnızca garip şekilde adlandırılmış olabilir. örnek "alıcılar" Companion değerini alır. Bunun yerine @JvmStatic kullanılıyor /@JvmField, garip isimdeki "alıcılar" işlemini taşıyor statik yöntemlerden biriyle Bu yine de 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 not

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 not

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ğrı kuralları vardır ve bu kurallar Ad işlevleri. Deyimsel olacak şekilde adlar tasarlamak için @JvmName kullanın her iki dilin kurallarını kullanabilir veya ilgili standart kitaplıkların eşleşmesini sağlayabilirsiniz. adlandırabilirsiniz.

Bu durum en sık uzantı işlevleri ve uzantı özelliklerinde görülür. çünkü alıcı türünün konumu farklı.

sealed class Optional<T : Any>
data class Some<T : Any>(val value: T): Optional<T>()
object None : Optional<Nothing>()

@JvmName("ofNullable")
fun <T> 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";
    Optional<String> optionalString =
          Optionals.ofNullable(nullableString);
}

Varsayılanlar için işlev aşırı yüklemeleri

Varsayılan değeri olan parametrelere sahip işlevler @JvmOverloads kullanmalıdır. Bu ek açıklama olmadan, varsayılan değerlere sahiptir.

@JvmOverloads kullanırken oluşturulan yöntemleri inceleyerek her birinin mantıklı olabilir. Uymuyorsa aşağıdaki yeniden düzenleme işlemlerinin birini veya ikisini birden yapın memnun kalana kadar:

  • Parametre sırasını, varsayılanları en düşük olanları tercih edecek şekilde değiştirin. dokunun.
  • Varsayılan değerleri, 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 not.

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");
    }
}

Tiftik Kontrolleri

Gereksinimler

  • Android Studio sürümü: 3.2 Canary 10 veya sonraki sürümler
  • Android Gradle Eklentisi sürümü: 3.2 veya üzeri

Desteklenen Kontroller

Artık bu hatalardan bazılarını tespit edip işaretlemenize yardımcı olacak Android Lint kontrolleri var. Daha önce açıklanan birlikte çalışabilirlik sorunları. Yalnızca Java'daki sorunlar (Kotlin için tüketim) tespit edilir. Özellikle, desteklenen kontroller şunlardır:

  • Bilinmeyen Boşluk
  • Mülk Erişimi
  • Hard Kotlin anahtar kelimesi yok
  • Lambda Parametreleri En Son

Android Studio

Bu kontrolleri etkinleştirmek için Dosya > Tercihler > Düzenleyici > Denetimler ve Kotlin Interoperability 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 ettikten sonra yeni kontroller kod incelemelerinizi çalıştırdığınızda çalıştırma (Analiz > Kodu İncele...)

Komut satırı derlemeleri

Bu kontrolleri komut satırı derlemelerinden etkinleştirmek için aşağıdaki satırı build.gradle dosyanız:

Eski

android {

    ...

    lintOptions {
        enable 'Interoperability'
    }
}

Kotlin

android {
    ...

    lintOptions {
        enable("Interoperability")
    }
}

lintOptions içinde desteklenen yapılandırmaların tamamı için Android Gradle DSL referansı.

Ardından komut satırından ./gradlew lint komutunu çalıştırın.