Kotlin-Java इंटरऑप गाइड

यह दस्तावेज़, Java और Kotlin में पब्लिक एपीआई बनाने के लिए नियमों का एक सेट है इस उद्देश्य से उपयोग किए जाने पर कोड दूसरे से उपयोग किए जाने पर मुहावरेदार महसूस करे भाषा.

पिछला अपडेट: 29-07-2024

Java (Kotlin की मदद से इस्तेमाल करने के लिए)

कोई हार्ड कीवर्ड नहीं है

तरीकों के नाम के तौर पर, Kotlin के किसी भी हार्ड कीवर्ड का इस्तेमाल न करें या फ़ील्ड में कॉपी करना. इनसे कॉल करते समय, एस्केप के लिए बैकटिक का इस्तेमाल करना ज़रूरी है Kotlin. सॉफ़्ट कीवर्ड, मॉडिफ़ायर कीवर्ड, और खास आइडेंटिफ़ायर इस्तेमाल करने की अनुमति है.

उदाहरण के लिए, Kotlin से किए जाने पर मॉकिटो के when फ़ंक्शन को बैकटिक की ज़रूरत होती है:

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

Any एक्सटेंशन नामों से बचें

इन कामों के लिए Any पर एक्सटेंशन फ़ंक्शन के नामों का इस्तेमाल करने से बचें तरीकों या Any पर एक्सटेंशन प्रॉपर्टी के नाम ज़रूरी नहीं है. जबकि सदस्य के तरीके और फ़ील्ड हमेशा Any के एक्सटेंशन फ़ंक्शन या प्रॉपर्टी पर प्राथमिकता है, तो यह कोड को पढ़ते समय यह जानने में कठिनाई होती है कि किसे कॉल किया जा रहा है.

शून्य वाले एनोटेशन

सार्वजनिक एपीआई में हर नॉन-प्रीमिटिव पैरामीटर, रिटर्न, और फ़ील्ड टाइप को शून्य होने वाली टिप्पणी शामिल है. एनोटेट नहीं किए गए टाइप को ऐसे समझा जाता है "प्लैटफ़ॉर्म" टाइप, जिनमें साफ़ तौर पर अमान्य वैल्यू दी गई है.

डिफ़ॉल्ट रूप से, Kotlin कंपाइलर फ़्लैग, JSR 305 एनोटेशन के हिसाब से काम करता है, लेकिन उन्हें फ़्लैग करता है चेतावनियों के साथ. आप एक फ़्लैग भी सेट कर सकते हैं, ताकि कंपाइलर एनोटेशन को गड़बड़ियों के रूप में दिखा सकते हैं.

Lambda पैरामीटर आखिरी

एसएएम कन्वर्ज़न के लिए ज़रूरी शर्तें पूरी करने वाले पैरामीटर टाइप आखिरी होने चाहिए.

उदाहरण के लिए, RxJava 2 का Flowable.create() मेथड सिग्नेचर इस तरह से परिभाषित किया गया है:

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

फ़्लोableOnSubscribe एसएएम कन्वर्ज़न के लिए मंज़ूरी होने की वजह से, फ़ंक्शन कॉल Kotlin से यह तरीका कुछ ऐसा दिखता है:

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

अगर पैरामीटर, मेथड सिग्नेचर में उलटे जाते हैं, तो फ़ंक्शन कॉल ट्रेलिंग-लैम्डा सिंटैक्स का इस्तेमाल किया जा सकता है:

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

प्रॉपर्टी के प्रीफ़िक्स

Kotlin में किसी तरीके को प्रॉपर्टी के तौर पर दिखाने के लिए, सख्त "बीन"-स्टाइल उपसर्ग का उपयोग किया जाना चाहिए.

ऐक्सेसर के तरीकों के लिए get प्रीफ़िक्स या बूलियन रिटर्न करने वाले तरीकों के लिए is की ज़रूरत होती है का इस्तेमाल किया जा सकता है.

public final class User {
  public String getName() { /* … */ }
  public boolean isActive() { /* … */ }
}
val name = user.name // Invokes user.getName()
val active = user.isActive // Invokes user.isActive()

जोड़े गए म्यूटर के तरीकों में set प्रीफ़िक्स होना ज़रूरी है.

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)

अगर आपको तरीकों को प्रॉपर्टी के तौर पर दिखाना है, तो बिना स्टैंडर्ड प्रीफ़िक्स का इस्तेमाल न करें. जैसे, has, set या बिना get प्रीफ़िक्स वाले ऐक्सेसर. नॉन-स्टैंडर्ड प्रीफ़िक्स वाले तरीके को अब भी फ़ंक्शन के रूप में कॉल किया जा सकता है, जो तरीका है.

ऑपरेटर ओवरलोडिंग

खास कॉल-साइट सिंटैक्स (जैसे कि) की अनुमति देने वाले तरीकों के नामों का ध्यान रखें Kotlin में ऑपरेटर ओवरलोडिंग). पक्का करें कि तरीकों के नाम इस तरह से हों इसलिए, छोटे किए गए सिंटैक्स के साथ इस्तेमाल करना सही रहेगा.

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 इस्तेमाल करने के लिए)

फ़ाइल का नाम

जब किसी फ़ाइल में टॉप-लेवल फ़ंक्शन या प्रॉपर्टी होती हैं, तो हमेशा उसकी व्याख्या करें @file:JvmName("Foo") से संपर्क करें.

डिफ़ॉल्ट रूप से, MyClass.kt फ़ाइल में टॉप-लेवल के सदस्य MyClassKt जो दिलचस्प नहीं है. साथ ही, लागू करने के लिए इस्तेमाल की गई भाषा की जानकारी लीक कर देता है जानकारी.

चैनल के टॉप लेवल सदस्यों को एक साथ जोड़ने के लिए, @file:JvmMultifileClass जोड़ें कई फ़ाइलों को एक क्लास में शामिल करना.

Lambda के तर्क

Java में तय किए गए सिंगल-मेथड इंटरफ़ेस (एसएएम) को Kotlin दोनों में लागू किया जा सकता है और Java का इस्तेमाल करके, जो लैम्डा सिंटैक्स का इस्तेमाल करता है. इससे एक मुहावरेदार तरीके से लागू किया जा सकता है तरीका है. Kotlin में इस तरह के इंटरफ़ेस को परिभाषित करने के लिए कई विकल्प होते हैं. इनमें से हर एक अंतर.

पसंदीदा परिभाषा

हाई-ऑर्डर फ़ंक्शन, जो Java से इस्तेमाल होने के लिए होते हैं को ऐसे फ़ंक्शन टाइप नहीं लेने चाहिए जो Unit को उसी तरह दिखाते हैं जैसा कि वे करते हैं Unit.INSTANCE लौटाने के लिए Java कॉलर ज़रूरी है. फ़ंक्शन को इनलाइन करने के बजाय हस्ताक्षर करने के लिए, फ़ंक्शनल (एसएएम) इंटरफ़ेस का इस्तेमाल करें. साथ ही सामान्य के बजाय फ़ंक्शनल (एसएएम) इंटरफ़ेस का इस्तेमाल करने पर विचार करें ऐसे इंटरफ़ेस तय करते समय दिखने वाले इंटरफ़ेस जिन्हें lambdas के तौर पर इस्तेमाल किया जा सकता है. यह Kotlin के मुहावरे इस्तेमाल करने की अनुमति देता है.

Kotlin की इस परिभाषा पर ध्यान दें:

fun interface GreeterCallback {
  fun greetName(String name)
}

fun sayHi(greeter: GreeterCallback) = /* … */

Kotlin से अनुरोध करने पर:

sayHi { println("Hello, $it!") }

Java से इस्तेमाल करने पर:

sayHi(name -> System.out.println("Hello, " + name + "!"));

फ़ंक्शन टाइप, Unit रिटर्न न करने पर भी काम का हो सकता है इसे एक नाम वाला इंटरफ़ेस बनाने की आइडिया है, ताकि कॉल करने वाले लोग इसे नाम वाले इंटरफ़ेस के साथ लागू कर सकें क्लास के साथ-साथ lambdas को भी (Kotlin और Java, दोनों में).

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

Unit रिटर्न करने वाले फ़ंक्शन टाइप से बचें

Kotlin की इस परिभाषा पर ध्यान दें:

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

इसके लिए Java कॉलर को Unit.INSTANCE लौटाने की ज़रूरत है:

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

जब लागू करने की वजह राज्य की हो, तो फ़ंक्शनल इंटरफ़ेस का इस्तेमाल न करें

जब लैम्डा का इस्तेमाल करते हुए, इंटरफ़ेस लागू करने के लिए कोई स्थिति तय की गई हो सिंटैक्स का कोई मतलब नहीं बनता. तुलना किया जा सकने वाला एक अहम उदाहरण है, क्योंकि इसका मकसद this की तुलना other से करना है और लैम्डा के पास this नहीं है. नहीं इंटरफ़ेस की शुरुआत में fun जोड़ने से कॉलर को object : ... का इस्तेमाल करना पड़ता है सिंटैक्स की मदद से, कॉलर को संकेत देते हुए स्थिति की जानकारी मिलती है.

Kotlin की इस परिभाषा पर ध्यान दें:

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

यह Kotlin में लैम्डा सिंटैक्स को रोकता है, जिसके लिए इस लंबे वर्शन की ज़रूरत होती है:

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

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

Nothing जेनरिक इस्तेमाल करने से बचें

वह टाइप जिसका सामान्य पैरामीटर Nothing है, उसे Java में रॉ टाइप के तौर पर दिखाया जाता है. रॉ डेटा टाइप का इस्तेमाल Java में बहुत कम ही किया जाता है. इसलिए, इनका इस्तेमाल करने से बचना चाहिए.

दस्तावेज़ के अपवाद

जांचे गए अपवादों को फेंकने वाले फ़ंक्शन को दस्तावेज़ के तौर पर @Throws. रनटाइम के अपवादों को KDoc में दर्ज किया जाना चाहिए.

इस बात का ध्यान रखें कि कोई फ़ंक्शन जिन एपीआई को ऐक्सेस करता है वे सही का निशान लगा सकते हैं अपवाद के तौर पर, जिन्हें Kotlin वैसे तो लागू करने की अनुमति देती है.

डिफ़ेंसिव कॉपी

सार्वजनिक एपीआई से, शेयर किए गए या बिना मालिकाना हक वाले रीड-ओनली कलेक्शन लौटाते समय, रैप करें उन्हें एक ऐसे कंटेनर में रखें जिसमें बदलाव न किया जा सके या डिफ़ेंसिव कॉपी बनाएं. Kotlin के बावजूद सिर्फ़ रीड-ओनली प्रॉपर्टी लागू करना हो, तो Java पर ऐसी कोई नीति लागू नहीं होती की ओर. रैपर या डिफ़ेंसिव कॉपी के बिना, इनवैरिएंट का उल्लंघन जो लंबे समय से मौजूद कलेक्शन का रेफ़रंस देता हो.

कंपैनियन फ़ंक्शन

किसी कंपैनियन ऑब्जेक्ट में सार्वजनिक फ़ंक्शन के बारे में @JvmStatic के साथ एनोटेट किया जाना चाहिए को स्टैटिक तरीके के रूप में दिखाया जाना चाहिए.

एनोटेशन के बिना, ये फ़ंक्शन सिर्फ़ इंस्टेंस तरीकों के तौर पर उपलब्ध हैं स्टैटिक Companion फ़ील्ड में डालें.

गलत: कोई एनोटेशन नहीं

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

सही: @JvmStatic एनोटेशन

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

कंपैनियन कॉन्सटेंट

सार्वजनिक और const प्रॉपर्टी, जो companion object में असरदार कॉन्सटेंट होती हैं उन्हें @JvmField के साथ एनोटेट करना चाहिए, ताकि उन्हें स्टैटिक फ़ील्ड के तौर पर दिखाया जा सके.

एनोटेशन के बिना, ये प्रॉपर्टी सिर्फ़ अजीब नाम के साथ उपलब्ध होती हैं इंस्टेंस "getters" स्टैटिक Companion फ़ील्ड में. इसके बजाय, @JvmStatic का इस्तेमाल किया जा रहा है @JvmField में से 100 से ज़्यादा खिलाड़ी ने अजीब नाम वाले "गेटर" को मूव किया क्लास पर स्टैटिक तरीके से जो अब भी गलत है.

गलत: कोई एनोटेशन नहीं

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

गलत: @JvmStatic एनोटेशन

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

सही: @JvmField एनोटेशन

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

इडियोमैटिक नेमिंग

Kotlin में कॉल करने के तरीके, Java के मुकाबले अलग हैं. इसकी वजह से, name फ़ंक्शन. @JvmName का इस्तेमाल करके नाम डिज़ाइन करें, ताकि वे मुहावरे लगे दोनों भाषाओं के तौर-तरीकों के हिसाब से व्यवस्थित करें. नाम देना.

ऐसा अक्सर एक्सटेंशन फ़ंक्शन और एक्सटेंशन प्रॉपर्टी के लिए होता है क्योंकि रिसीवर टाइप की जगह अलग है.

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

डिफ़ॉल्ट के लिए फ़ंक्शन ओवरलोड

जिन फ़ंक्शन में डिफ़ॉल्ट वैल्यू वाले पैरामीटर मौजूद हैं उन्हें @JvmOverloads का इस्तेमाल करना होगा. इस एनोटेशन के बिना किसी भी डिफ़ॉल्ट वैल्यू.

@JvmOverloads का इस्तेमाल करते समय, जनरेट किए गए तरीकों की जांच करें, ताकि यह पक्का किया जा सके कि उनमें से हर एक समझ में आता है. अगर ऐसा नहीं होता है, तो इनमें से एक या दोनों रीफ़ैक्टरिंग करें संतुष्ट होने तक:

  • पैरामीटर का क्रम बदलें, ताकि डिफ़ॉल्ट सेटिंग की वैल्यू को प्राथमिकता दी जा सके खत्म.
  • डिफ़ॉल्ट फ़ंक्शन को मैन्युअल फ़ंक्शन ओवरलोड पर ले जाएं.

गलत: नहीं @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");
    }
}

सही: @JvmOverloads एनोटेशन.

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

लिंट चेक

ज़रूरी शर्तें

  • Android Studio वर्शन: 3.2 Canary 10 या इसके बाद का वर्शन
  • Android Gradle प्लग इन का वर्शन: 3.2 या उसके बाद का वर्शन

इस्तेमाल की जा सकने वाली जांच

अब Android Lit की जांच की सुविधा उपलब्ध है. इससे आपको कुछ डिवाइसों का पता लगाने और उन्हें फ़्लैग करने में मदद मिलेगी इंटरऑपरेबिलिटी से जुड़ी समस्याएं, जिनके बारे में ऊपर बताया गया है. सिर्फ़ Java में समस्याएं (Kotlin की सुविधा के लिए) इस्तेमाल) का पता चलता है. खास तौर पर, ये जांच की जा सकती हैं:

  • अज्ञात शून्य
  • प्रॉपर्टी का ऐक्सेस
  • कोई हार्ड Kotlin कीवर्ड नहीं है
  • लैम्डा पैरामीटर अंतिम

Android Studio

इन जांचों को चालू करने के लिए, फ़ाइल > प्राथमिकताएं > एडिटर > जांच और उन नियमों को देखें जिन्हें आपको Kotlin इंटरऑपरेबिलिटी के तहत चालू करना है:

पहला डायग्राम. Android Studio में Kotlin इंटरऑपरेबिलिटी सेटिंग.

आपको जिन नियमों को लागू करना है उनकी जांच करने के बाद, नई जांच कोड को जांच करने के दौरान ट्रिगर करें (विश्लेषण करें > कोड की जांच करें...)

कमांड-लाइन बिल्ड

कमांड-लाइन बिल्ड से इन जांचों को चालू करने के लिए, नीचे दी गई लाइन जोड़ें आपकी build.gradle फ़ाइल:

ग्रूवी

android {

    ...

    lintOptions {
        enable 'Interoperability'
    }
}

Kotlin

android {
    ...

    lintOptions {
        enable("Interoperability")
    }
}

lintOptions में काम करने वाले कॉन्फ़िगरेशन के पूरे सेट के लिए, इसे देखें Android Gradle DSL से जुड़ा रेफ़रंस.

इसके बाद, कमांड लाइन से ./gradlew lint चलाएं.