המסמך הזה הוא קבוצת כללים ליצירת ממשקי API ציבוריים ב-Java וב-Kotlin מתוך כוונה שהקוד ירגיש אידיומטי כשצריך אותו מהצד השני בשפת היעד.
Java (לצריכת Kotlin)
אין להשתמש במילות מפתח קשות
אסור להשתמש במילות מפתח קשות של Kotlin כשם של שיטות או שדות. כדי שההודעות האלה יתבצעו, צריך להשתמש בגרשיים (backtics) כדי לסמן בתו בריחה (escape) Kotlin. מילות מפתח רכות, מילות מפתח עם מגבילי התאמה וכן מותר להשתמש במזהים מיוחדים.
לדוגמה, כדי להשתמש בפונקציה when
של Mockito צריך להוסיף גרשיים (backtics) כשמשתמשים מ-Kotlin:
val callable = Mockito.mock(Callable::class.java)
Mockito.`when`(callable.call()).thenReturn(/* … */)
להימנע מ-Any
שמות של תוספים
לא משתמשים בשמות של פונקציות התוספים ב-Any
בשביל
או השמות של מאפייני התוספים ב-Any
עבור
אלא אם כן יש בכך צורך. אמנם השיטות והשדות של חברי הקבוצה תמיד
יש קדימות על פני הפונקציות או מאפייני התוספים של Any
, הם יכולים להיות
כשקוראים את הקוד, קשה לדעת לאיזה קוד קוראים קוראים.
הערות לגבי ביטולי null
כל פרמטר, חזרה וסוג שדה לא פרימיטיביים ב-API ציבורי כוללים הערה לגבי יכולת null. סוגים ללא הערות מפורשים בתור "פלטפורמה" , שיכול להיות שהיא לא תהיה ברורה.
כברירת מחדל, דגלי המהדר של Kotlin מכבדים את ההערות של JSR 305 אבל מסמנים אותן עם אזהרות. תוכלו גם להגדיר דגל כדי שהמהדר יטפל בתור שגיאות.
פרמטרים של Lambda אחרונים
סוגי הפרמטרים שעומדים בקריטריונים להמרה ב-SAM צריכים להיות אחרונים.
לדוגמה, חתימת השיטה Flowable.create()
ב-RxJava 2 מוגדרת כך:
public static <T> Flowable<T> create(
FlowableOnSubscribe<T> source,
BackpressureStrategy mode) { /* … */ }
מכיוון ש-FflowableOnSubscribe עומד בדרישות להמרת SAM, קריאות לפונקציות של השיטה הזו מ-Kotlin נראית כך:
Flowable.create({ /* … */ }, BackpressureStrategy.LATEST)
אבל אם הפרמטרים מתבטלים בחתימת ה-method, הקריאות של הפונקציות יכול להשתמש בתחביר lambda בסוף:
Flowable.create(BackpressureStrategy.LATEST) { /* … */ }
תחיליות של נכסים
לשיטה שמיוצגת כנכס ב-Kotlin, שימוש מחמיר בסגנון 'שעועית' חובה להשתמש בקידומת.
ל-methods של Access צריך להוסיף קידומת get
, או ל-methods עם החזרה בוליאנית 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()
ל-methods משויכות נדרשת קידומת 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)
אם אתם רוצים ש-methods יהיו חשופות כמאפיינים, אל תשתמשו בקידומות לא סטנדרטיות כמו
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
ניתן להטמיע ממשקי method יחידה (SAM) המוגדרים ב-Java ב-Kotlin ו-Java באמצעות תחביר lambda, שמטביע את ההטמעה בדרך הזו. ב-Kotlin יש כמה אפשרויות להגדרת ממשקים כאלה, הבדל.
הגדרה מועדפת
פונקציות בסדר גבוה יותר שאמורות להשתמש בהן מ-Java
אי אפשר להשתמש בסוגי פונקציות שמחזירים Unit
כמו שצריך
לדרוש שמתקשרים ב-Java יחזירו Unit.INSTANCE
. במקום להטמיע את הפונקציה
בחתימה, להשתמש בממשקים פונקציונליים (SAM). וגם
כדאי להשתמש בממשקים פונקציונליים (SAM) במקום בממשקים רגילים
כאשר מגדירים ממשקים שצפויים לשמש כ-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
הוא עדיין יכול להיות טוב
להפוך אותו לממשק בעל שם כדי לאפשר למתקשרים ליישם אותו עם
class ולא רק 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;
});
הימנעות מממשקים פונקציונליים כשההטמעה אמורה לכלול מצב
כשהממשק אמור לכלול מצב, באמצעות lambda
לא הגיוני. דוגמה בולטת היא דומה:
כי היא אמורה להשוות בין this
ל-other
, ולקבוצות lambda אין this
. לא
הוספת קידומת לממשק ב-fun
מאלצת את המתקשר/ת להשתמש ב-object : ...
תחביר, שמאפשר לכלול מצב שמספק רמז לקורא.
נבחן את ההגדרה הבאה של Kotlin:
// No "fun" prefix.
interface Counter {
fun increment()
}
הוא מונע תחביר lambda ב-Kotlin, מה שמחייב את הגרסה הארוכה יותר הזו:
runCounter(object : Counter {
private var increments = 0 // State
override fun increment() {
increments++
}
})
הימנעות משימוש כללי בNothing
סוג שהפרמטר הגנרי שלו הוא Nothing
נחשף כסוגים גולמיים ל-Java. גולמי
ב-Java נעשה שימוש רק לעיתים רחוקות, ויש להימנע מהם.
חריגים במסמך
פונקציות שיכולות להשליך על חריגות בבדיקה צריכות לתעד אותן באמצעות
@Throws
יש לתעד חריגים בסביבת זמן הריצה ב-KDoc.
חשוב לשים לב לממשקי ה-API שהפונקציה מאצילה אליהם, כי הם עלולים לבדוק חריגים ש-Kotlin מאפשר הפצה באופן עצמאי.
עותקים הגנתיים
כשמחזירים אוספים לקריאה בלבד ששותפו או שאינם בבעלותם מממשקי API ציבוריים, צריך לשמור על אריזה אותם בקונטיינר שלא ניתן לשינוי או לבצע עותק הגנתי. למרות Kotlin אוכפים את נכס לקריאה בלבד, אין אכיפה כזו ב-Java. צד שלישי. ללא wrapper או עותק הגנתי, אפשר להפר משתנים קבועים באמצעות החזרת הפניה לאוסף לטווח ארוך.
פונקציות נלוות
חובה להוסיף הערות לפונקציות ציבוריות באובייקט נלווה באמצעות @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
מעביר את השם המוזר של 'getters' ל-methods סטטיות בכיתה,
וזה עדיין שגוי.
שגוי: אין הערה
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, שיכולות לשנות את
והפונקציות של השמות. אפשר להשתמש ב-@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");
}
}
בדיקות לאיתור שגיאות בקוד (lint)
הדרישות
- גרסת Android Studio: 3.2 Canary 10 ואילך
- גרסת הפלאגין ל-Android Gradle: 3.2 ואילך
בדיקות נתמכות
יש עכשיו בדיקות לאיתור שגיאות בקוד ב-Android שיעזרו לכם לזהות ולסמן חלק שתוארו קודם לכן על בעיות ביכולת הפעולה ההדדית. רק בעיות ב-Java (עבור Kotlin צריכה). באופן ספציפי, הבדיקות הנתמכות הן:
- ערך אפסי לא ידוע
- גישה לנכס
- אין מילות מפתח קשיחות ב-Kotlin
- פרמטרים של Lambda אחרונים
סטודיו ל-Android
כדי להפעיל את הבדיקות האלה, יש לעבור אל קובץ > העדפות > עריכה > בדיקות וגם כדי לבדוק את הכללים שרוצים להפעיל במסגרת יכולת הפעולה ההדדית של Kotlin:
אחרי שבדקת את הכללים שברצונך להפעיל, הבדיקות החדשות פועל כאשר אתה מריץ בדיקות קוד (ניתוח > בדיקת קוד...)
פיתוח גרסאות build של שורת הפקודה
כדי להפעיל את הבדיקות האלה דרך גרסאות ה-build של שורת הפקודה, צריך להוסיף את השורה הבאה
קובץ build.gradle
שלך:
מגניב
android { ... lintOptions { enable 'Interoperability' } }
Kotlin
android { ... lintOptions { enable("Interoperability") } }
כדי לראות את רשימת ההגדרות המלאה שנתמכות ב-lintOptions, אפשר לעיין חומר עזר בנושא Gradle DSL.
לאחר מכן, מריצים את הפקודה ./gradlew lint
משורת הפקודה.