參考官方文檔:https://developer.android.google.cn/kotlin/interop?hl=zh-cn
一、Java(供 Kotlin 使用)
1、不得使用硬關鍵字
不要使用 Kotlin 的任何硬關鍵字作為方法的名稱 或字段。允許使用 Kotlin 的軟關鍵字、修飾符關鍵字和特殊標識符。
-
硬關鍵字
as、as?、break、class、continue、do、else、 false、for、fun、if、in、!in、interface、is、!is、null、object、package、super、this、throw、true、typealias、typeof、val、var、when、while。 -
軟關鍵字、修飾符關鍵字和特殊標識符
https://kotlinlang.org/docs/keyword-reference.html#hard-keywords
2、避免使用 Any 的擴展函數或屬性的名稱
3、可為 null 性注釋
- 公共 API 中的每個非基礎參數類型、返回類型和字段類型都應 具有可為 null 性注解。
- 未加注解的類型會被解釋為 “平臺”類型,這些類型是否可為 null 性不明確。
4、Lambda 參數位于最后
- 符合 SAM 轉換條件的參數類型應位于最后。例如,RxJava 2 的 Flowable.create() 方法簽名定義為:
public static <T> Flowable<T> create(FlowableOnSubscribe<T> source,BackpressureStrategy mode) { /* … */ }// 在 kotlin 中調用時顯示為Flowable.create({ /* … */ }, BackpressureStrategy.LATEST)
- 如果方法簽名中的參數顛倒順序,則函數會調用 可以使用尾隨 lambda 語法:
public static <T> Flowable<T> create(BackpressureStrategy mode,FlowableOnSubscribe<T> source) { /* … */ }// 在 kotlin 中調用時顯示為Flowable.create(BackpressureStrategy.LATEST) { /* … */ }
5、屬性前綴
- 對于在 Kotlin 中要表示為屬性的方法,需要嚴格的**“bean”樣式** 前綴。
- 訪問器方法需要 get 前綴;對于布爾值返回方法,則為 is 前綴。
- 更改器方法需要 set 前綴。
- 如果希望方法作為屬性公開,請不要使用非標準前綴,例如 has、set 或無 get 前綴的訪問器。帶有非標準前綴的方法 也可作為函數進行調用,具體取決于 方法的行為。
public final class User {public String getName() { /* … */ }public void setName(String name) { /* … */ }public boolean isActive() { /* … */ }public void setActive(boolean active) { /* … */ }}// 對應的 kotlin 代碼val name = user.name // Invokes user.getName()val active = user.isActive // Invokes user.isActive()user.name = "Bob" // Invokes user.setName(String)user.isActive = true // Invokes user.setActive(boolean)
6、運算符過載
- 允許特殊調用點語法。
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);}}// kotlin 代碼val one = IntBox(1)val two = IntBox(2)val three = one + two // Invokes one.plus(two)
二、Kotlin(供 Java 使用)
1、文件名
- 如果文件包含頂級函數或屬性,請始終為其添加注解 使用 @file:JvmName(“Foo”) 提供一個好記的名稱。
- 默認情況下,MyClass.kt 文件中的頂級成員最終將位于名為 MyClassKt 文件中,該名字沒有吸引力,并且會泄露作為實現的語言 。
- 建議您添加“@file:JvmMultifileClass”,它是 Kotlin 中的一個注解,用于支持將一個 Kotlin 文件拆分成多個部分,這些部分在 Java 中被視為同一個類的一部分。
- 使用 @file:JvmMultifileClass 注解時,通常會結合 @file:JvmName 注解來指定生成的 Java 類的名稱。這樣,多個 Kotlin 文件可以合并成一個 Java 類,而不會出現命名沖突。
2、Lambda 參數
- 使用 Java 定義的單一方法接口 (SAM) 可以用 Kotlin 語言實現,也可以使用 lambda 語法的 Java 語言以慣用方式內嵌實現。
(1)首選定義
- 要在 Java 中使用的高階函數,不應接受會返回 Unit 的函數類型,而建議使用功能 (SAM) 接口。
- 即使函數類型不會返回 Unit,仍建議您將其設為命名接口,以便調用方使用命名類來實現它,而非只使用 lambda(在 Kotlin 和 Java 中)。
- 在定義預期用作 lambda 的接口時,優先考慮使用功能 (SAM) 接口,而不是常規接口 ,用以支持 Kotlin 中的慣用用法。
// 高階函數,函數類型為 (String) -> Unitfun sayHi(greeter: (String) -> Unit)// 建議使用 SAM 接口fun interface GreeterCallback {fun greetName(String name)}fun sayHi(greeter: GreeterCallback) = /* … */// kotlin 中調用sayHi { println("Hello, $it!") }// java 中調用sayHi(name -> System.out.println("Hello, " + name + "!"));// 實現接口的命名類class MyGreeterCallback : GreeterCallback {override fun greetName(name: String) {println("Hello, $name!");}}
(2)避免使用會返回 Unit 的函數類型
- 返回 Unit 的函數類型要求 Java 調用方返回 Unit.INSTANCE
// kotlinfun sayHi(greeter: (String) -> Unit) = /* … */// 對應的 java 調用sayHi(name -> {System.out.println("Hello, " + name + "!");return Unit.INSTANCE;});
(3)如果接口實現持有狀態,請避免使用功能接口
- 當接口實現需要持有狀態時,使用 lambda 語法是沒有意義的。Comparable 是一個典型的例子,因為它需要比較 this 和 other,而 lambda 表達式沒有 this。不使用 fun 修飾接口會迫使調用者使用 object : … 語法,這允許實現中持有狀態,同時也為調用者提供了一個提示。
- 不使用 fun 修飾的接口無法在 Kotlin 中使用 lambda 語法。
// No "fun" prefix.interface Counter {fun increment()}runCounter(object : Counter {private var increments = 0 // State override fun increment() {increments++}})
3、避免使用 Nothing 類屬
- 泛型參數為 Nothing 的類型會作為原始類型提供給 Java。原始 類型在 Java 中很少使用,應予以避免使用。
4、防御性復制
- 在從公共API返回共享的或無主的只讀集合時,應將其包裝在一個不可修改的容器中,或者執行防御性拷貝。盡管Kotlin強制執行了它們的只讀屬性,但Java端并沒有這樣的強制性。如果沒有包裝器或防御性拷貝,返回一個長期存在的集合引用可能會破壞不變性。
5、伴生函數
- 伴生對象中的公共函數必須帶有 @JvmStatic 注解使其公開為靜態方法,如果沒有該注解,這些函數在 Java 中只能作為實例方法使用。
// 不正確,沒有 @JvmStatic 注解class KotlinClass {companion object {fun doWork() {/* … */}}}// 在 java 中調用public final class JavaClass {public static void main(String... args) {KotlinClass.Companion.doWork();}}// 正確,添加 @JvmStatic 注解class KotlinClass {companion object {@JvmStatic fun doWork() {/* … */}}}// 在 java 中調用public final class JavaClass {public static void main(String... args) {KotlinClass.doWork();}}
6、伴生常量
- 作為 companion object 中的有效常量的公共非 const 屬性必須帶有 @JvmField 注解,java 調用時才能作為靜態字段提供。
- 如果沒有該注解,這些屬性只能作為靜態Companion字段上奇怪命名的實例“getter”方法使用。
- 而使用@JvmStatic替代@JvmField,則會將這些奇怪命名的“getter”方法移動到類的靜態方法中,但這仍然是不正確的。
// 1、不正確,沒有注解class KotlinClass {companion object {const val INTEGER_ONE = 1val BIG_INTEGER_ONE = BigInteger.ONE}}// java 中調用public final class JavaClass {public static void main(String... args) {System.out.println(KotlinClass.INTEGER_ONE);System.out.println(KotlinClass.Companion.getBIG_INTEGER_ONE());}}// 2、不正確:@JvmStatic 注釋class KotlinClass {companion object {const val INTEGER_ONE = 1@JvmStatic val BIG_INTEGER_ONE = BigInteger.ONE}}// java 中調用public final class JavaClass {public static void main(String... args) {System.out.println(KotlinClass.INTEGER_ONE);System.out.println(KotlinClass.getBIG_INTEGER_ONE());}}//3、正確:@JvmField 注釋class KotlinClass {companion object {const val INTEGER_ONE = 1@JvmField val BIG_INTEGER_ONE = BigInteger.ONE}}// java 中調用public final class JavaClass {public static void main(String... args) {System.out.println(KotlinClass.INTEGER_ONE);System.out.println(KotlinClass.BIG_INTEGER_ONE);}}
7、符合語言習慣的命名
- 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);}
8、默認值的函數過載
- 參數具有默認值的函數必須使用 @JvmOverloads。如果沒有此注解,則無法使用任何默認值來調用函數。
- 在使用@JvmOverloads時,要檢查生成的方法,確保每個方法都合理。如果它們不合理,請執行以下一種或兩種重構操作,直到滿意為止:
- 調整參數順序,將帶有默認值的參數放在最后。
- 將默認值移入手動實現的函數重載中。
// 不正確:沒有 @JvmOverloadsclass Greeting {fun sayHello(prefix: String = "Mr.", name: String) {println("Hello, $prefix $name")}}// java 調用public class JavaClass {public static void main(String... args) {Greeting greeting = new Greeting();greeting.sayHello("Mr.", "Bob");}}// 正確:@JvmOverloads 注釋class Greeting {@JvmOverloadsfun sayHello(prefix: String = "Mr.", name: String) {println("Hello, $prefix $name")}}// java 調用public class JavaClass {public static void main(String... args) {Greeting greeting = new Greeting();greeting.sayHello("Bob");}}
三、Lint 檢查
- 在 Android 開發中,Lint 檢查 是一種靜態代碼分析工具,用于檢查代碼中的潛在問題,幫助開發者在編譯之前發現并修復代碼中的錯誤、性能問題、安全問題、可維護性問題等。
1、環境要求
- Android Studio 版本:3.2 Canary 10 或更高版本
- Android Gradle 插件版本:3.2 或更高版本
2、支持的檢查
- 支持的檢查包括:
- 未知 Null 性
- 屬性訪問
- 不得使用 Kotlin 硬關鍵字
- Lambda 參數位于最后
3、Android Studio 中啟用檢查
- Android Studio 中要啟用這些檢查,請依次點擊 File > Settings >Editor >Inspections,在 “Android Lint: Interoperability” 下選中您要啟用的規則。
- 選中要啟用的規則后,新的檢查將 在運行代碼檢查 (Code > Inspect Code…) 時運行。