# 在 Kotlin 中調用 Java 代碼
[TOC]
Kotlin 在設計時就考慮了 Java 互操作性。可以從 Kotlin 中自然地調用現存的 Java 代碼,并且在 Java 代碼中也可以很順利地調用 Kotlin 代碼。在本節中我們會介紹從 Kotlin 中調用 Java 代碼的一些細節。
幾乎所有 Java 代碼都可以使用而沒有任何問題:
```kotlin
import java.util.*
fun demo(source: List<Int>) {
val list = ArrayList<Int>()
// “for”-循環用于 Java 集合:
for (item in source) {
list.add(item)
}
// 操作符約定同樣有效:
for (i in 0..source.size - 1) {
list[i] = source[i] // 調用 get 和 set
}
}
```
## Getter 和 Setter
遵循 Java 約定的 getter 和 setter 的方法(名稱以 `get` 開頭的無參數方法和以 `set` 開頭的單參數方法)在 Kotlin 中表示為屬性。`Boolean` 訪問器方法(其中 getter 的名稱以 `is` 開頭而 setter 的名稱以 `set` 開頭)會表示為與 getter 方法具有相同名稱的屬性。
例如:
```kotlin
import java.util.Calendar
fun calendarDemo() {
val calendar = Calendar.getInstance()
if (calendar.firstDayOfWeek == Calendar.SUNDAY) { // 調用 getFirstDayOfWeek()
calendar.firstDayOfWeek = Calendar.MONDAY // 調用ll setFirstDayOfWeek()
}
if (!calendar.isLenient) { // 調用 isLenient()
calendar.isLenient = true // 調用 setLenient()
}
}
```
請注意,如果 Java 類只有一個 setter,它在 Kotlin 中不會作為屬性可見,因為 Kotlin 目前不支持只寫(set-only)屬性。
## 返回 void 的方法
如果一個 Java 方法返回 void,那么從 Kotlin 調用時中返回 `Unit`。萬一有人使用其返回值,它將由 Kotlin 編譯器在調用處賦值,因為該值本身是預先知道的(是 `Unit`)。
## 將 Kotlin 中是關鍵字的 Java 標識符進行轉義
一些 Kotlin 關鍵字在 Java 中是有效標識符:*in*、 *object*、 *is*等等。如果一個 Java 庫使用了 Kotlin 關鍵字作為方法,你仍然可以通過反引號(`)字符轉義它來調用該方法:
```kotlin
foo.`is`(bar)
```
## 空安全與平臺類型
Java 中的任何引用都可能是 *null*,這使得 Kotlin 對來自 Java 的對象要求嚴格空安全是不現實的。Java 聲明的類型在 Kotlin 中會被特別對待并稱為*平臺類型*。對這種類型的空檢測會放寬,因此它們的安全保證與在 Java 中相同(更多請參見[下文](http://www.kotlincn.net/docs/reference/java-interop.html#%E5%B7%B2%E6%98%A0%E5%B0%84%E7%B1%BB%E5%9E%8B))。
考慮以下示例:
```kotlin
val list = ArrayList<String>() // 非空(構造函數結果)
list.add("Item")
val size = list.size // 非空(原生 int)
val item = list[0] // 推斷為平臺類型(普通 Java 對象)
```
當我們調用平臺類型變量的方法時,Kotlin 不會在編譯時報告可空性錯誤,但在運行時調用可能會失敗,因為空指針異常或者 Kotlin 生成的阻止空值傳播的斷言:
```kotlin
item.substring(1) // 允許,如果 item == null 可能會拋出異常
```
平臺類型是*不可標示*的,意味著不能在語言中明確地寫下它們。當把一個平臺值賦值給一個 Kotlin 變量時,可以依賴類型推斷(該變量會具有推斷出的的平臺類型,如上例中 `item` 所具有的類型),或者我們可以選擇我們期望的類型(可空或非空類型均可):
```kotlin
val nullable: String? = item // 允許,沒有問題
val notNull: String = item // 允許,運行時可能失敗
```
如果我們選擇非空類型,編譯器會在賦值時觸發一個斷言。這防止 Kotlin 的非空變量保存空值。當我們把平臺值傳遞給期待非空值等的 Kotlin 函數時,也會觸發斷言。
總的來說,編譯器盡力阻止空值通過程序向遠傳播(盡管鑒于泛型的原因,有時這不可能完全消除)。
### 平臺類型表示法
如上所述,平臺類型不能在程序中顯式表述,因此在語言中沒有相應語法。然而,編譯器和 IDE 有時需要(在錯誤信息中、參數信息中等)顯示他們,所以我們用一個助記符來表示他們:
* `T!` 表示“`T` 或者 `T?`”,
* `(Mutable)Collection<T>!` 表示“可以可變或不可變、可空或不可空的 `T` 的 Java 集合”,
* `Array<(out) T>!` 表示“可空或者不可空的 `T`(或 `T` 的子類型)的 Java 數組”
### 可空性注解
具有可空性注解的Java類型并不表示為平臺類型,而是表示為實際可空或非空的Kotlin 類型。編譯器支持多種可空性注解,包括:
* [JetBrains](https://www.jetbrains.com/idea/help/nullable-and-notnull-annotations.html)
(`org.jetbrains.annotations` 包中的 `@Nullable` 和 `@NotNull`)
* Android(`com.android.annotations` 和 `android.support.annotations`)
* JSR-305(`javax.annotation`,詳見下文)
* FindBugs(`edu.umd.cs.findbugs.annotations`)
* Eclipse(`org.eclipse.jdt.annotation`)
* Lombok(`lombok.NonNull`)。
你可以在 [Kotlin 編譯器源代碼](https://github.com/JetBrains/kotlin/blob/master/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/JvmAnnotationNames.kt)中找到完整的列表。
### 注解類型參數
可以標注泛型類型的類型參數,以便同時為其提供可空性信息。例如,考慮這些 Java 聲明的注解:
```java
@NotNull
Set<@NotNull String> toSet(@NotNull Collection<@NotNull String> elements) { …… }
```
在 Kotlin 中可見的是以下簽名:
```kotlin
fun toSet(elements: (Mutable)Collection<String>) : (Mutable)Set<String> { …… }
```
請注意 `String` 類型參數上的 `@NotNull` 注解。如果沒有的話,類型參數會是平臺類型:
```kotlin
fun toSet(elements: (Mutable)Collection<String!>) : (Mutable)Set<String!> { …… }
```
標注類型參數適用于面向 Java 8 或更高版本環境,并且要求可空性注解支持 `TYPE_USE` 目標(`org.jetbrains.annotations` 15 或以上版本支持)。
> 注:由于當前的技術限制,IDE 無法正確識別用作依賴的已編譯 Java 庫中類型參數上的這些注解。
### JSR-305 支持
已支持 [JSR-305](https://jcp.org/en/jsr/detail?id=305) 中定義的 [`@Nonnull`](https://aalmiray.github.io/jsr-305/apidocs/javax/annotation/Nonnull.html)注解來表示 Java 類型的可空性。
如果 `@Nonnull(when = ...)` 值為 `When.ALWAYS`,那么該注解類型會被視為非空;`When.MAYBE` 與
`When.NEVER` 表示可空類型;而 `When.UNKNOWN` 強制類型為[平臺類型](http://www.kotlincn.net/docs/reference/java-interop.html#%E7%A9%BA%E5%AE%89%E5%85%A8%E4%B8%8E%E5%B9%B3%E5%8F%B0%E7%B1%BB%E5%9E%8B)。
可針對 JSR-305 注解編譯庫,但不需要為庫的消費者將注解構件(如 `jsr305.jar`)指定為編譯依賴。Kotlin 編譯器可以從庫中讀取 JSR-305 注解,并不需要該注解出現在類路徑中。
自 Kotlin 1.1.50 起,也支持[自定義可空限定符(KEEP-79)](https://github.com/Kotlin/KEEP/blob/41091f1cc7045142181d8c89645059f4a15cc91a/proposals/jsr-305-custom-nullability-qualifiers.md)(見下文)。
#### 類型限定符別稱(自 1.1.50 起)
如果一個注解類型同時標注有
[`@TypeQualifierNickname`](https://aalmiray.github.io/jsr-305/apidocs/javax/annotation/meta/TypeQualifierNickname.html)與 JSR-305 `@Nonnull`(或者它的其他別稱,如 `@CheckForNull`),那么該注解類型自身將用于檢索精確的可空性,且具有與該可空性注解相同的含義:
```java
@TypeQualifierNickname
@Nonnull(when = When.ALWAYS)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyNonnull {
}
@TypeQualifierNickname
@CheckForNull // 另一個類型限定符別稱的別稱
@Retention(RetentionPolicy.RUNTIME)
public @interface MyNullable {
}
interface A {
@MyNullable String foo(@MyNonnull String x);
// 在 Kotlin(嚴格模式)中:`fun foo(x: String): String?`
String bar(List<@MyNonnull String> x);
// 在 Kotlin(嚴格模式)中:`fun bar(x: List<String>!): String!`
}
```
#### 類型限定符默認值(自 1.1.50 起)
[`@TypeQualifierDefault`](https://aalmiray.github.io/jsr-305/apidocs/javax/annotation/meta/TypeQualifierDefault.html)
引入應用時在所標注元素的作用域內定義默認可空性的注解。
這些注解類型應自身同時標注有 `@Nonnull`(或其別稱)與 `@TypeQualifierDefault(...)`注解,后者帶有一到多個 `ElementType` 值:
* `ElementType.METHOD` 用于方法的返回值;
* `ElementType.PARAMETER` 用于值參數;
* `ElementType.FIELD` 用于字段;以及
* `ElementType.TYPE_USE`(自 1.1.60 起)適用于任何類型,包括類型參數、類型參數的上界與通配符類型。
當類型并未標注可空性注解時使用默認可空性,并且該默認值是由最內層標注有帶有與所用類型相匹配的`ElementType` 的類型限定符默認注解的元素確定。
```java
@Nonnull
@TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER})
public @interface NonNullApi {
}
@Nonnull(when = When.MAYBE)
@TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE_USE})
public @interface NullableApi {
}
@NullableApi
interface A {
String foo(String x); // fun foo(x: String?): String?
@NotNullApi // 覆蓋來自接口的默認值
String bar(String x, @Nullable String y); // fun bar(x: String, y: String?): String
// 由于 `@NullableApi` 具有 `TYPE_USE` 元素類型,
// 因此認為 List<String> 類型參數是可空的:
String baz(List<String> x); // fun baz(List<String?>?): String?
// “x”參數仍然是平臺類型,因為有顯式
// UNKNOWN 標記的可空性注解:
String qux(@Nonnull(when = When.UNKNOWN) String x); // fun baz(x: String!): String?
}
```
> 注意:本例中的類型只在啟用了嚴格模式時出現,否則仍是平臺類型。參見 [`@UnderMigration` 注解](http://www.kotlincn.net/docs/reference/java-interop.html#undermigration-%E6%B3%A8%E8%A7%A3%E8%87%AA-1160-%E8%B5%B7)與[編譯器配置](http://www.kotlincn.net/docs/reference/java-interop.html#%E7%BC%96%E8%AF%91%E5%99%A8%E9%85%8D%E7%BD%AE)兩節。
也支持包級的默認可空性:
```java
// 文件:test/package-info.java
@NonNullApi // 默認將“test”包中所有類型聲明為不可空
package test;
```
#### `@UnderMigration` 注解(自 1.1.60 起)
庫的維護者可以使用 `@UnderMigration` 注解(在單獨的構件 `kotlin-annotations-jvm` 中提供)來定義可為空性類型限定符的遷移狀態。
`@UnderMigration(status = ...)` 中的狀態值指定了編譯器如何處理 Kotlin 中注解類型的不當用法(例如,使用 `@MyNullable` 標注的類型值作為非空值):
* `MigrationStatus.STRICT` 使注解像任何純可空性注解一樣工作,即對不當用法報錯并影響注解聲明內的類型在 Kotlin 中的呈現;
* 對于 `MigrationStatus.WARN`,不當用法報為警告而不是錯誤;但注解聲明內的類型仍是平臺類型;而
* `MigrationStatus.IGNORE` 則使編譯器完全忽略可空性注解。
庫的維護者還可以將 `@UnderMigration` 狀態添加到類型限定符別稱與類型限定符默認值:
```java
@Nonnull(when = When.ALWAYS)
@TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER})
@UnderMigration(status = MigrationStatus.WARN)
public @interface NonNullApi {
}
// 類中的類型是非空的,但是只報警告
// 因為 `@NonNullApi` 標注了 `@UnderMigration(status = MigrationStatus.WARN)`
@NonNullApi
public class Test {}
```
注意:可空性注解的遷移狀態并不會從其類型限定符別稱繼承,而是適用于默認類型限定符的用法。
如果默認類型限定符使用類型限定符別稱,并且它們都標注有 `@UnderMigration`,那么使用默認類型限定符的狀態。
#### 編譯器配置
可以通過添加帶有以下選項的 `-Xjsr305` 編譯器標志來配置 JSR-305 檢測:
* `-Xjsr305={strict|warn|ignore}` 設置非 `@UnderMigration` 注解的行為。自定義的可空性限定符,尤其是
`@TypeQualifierDefault` 已經在很多知名庫中流傳,而用戶更新到包含 JSR-305 支持的 Kotlin 版本時可能需要平滑遷移。自 Kotlin 1.1.60 起,這一標志只影響非 `@UnderMigration` 注解。
* `-Xjsr305=under-migration:{strict|warn|ignore}`(自 1.1.60 起)覆蓋 `@UnderMigration` 注解的行為。用戶可能對庫的遷移狀態有不同的看法:他們可能希望在官方遷移狀態為 `WARN` 時報錯誤,反之亦然,他們可能希望推遲錯誤報告直到他們完成遷移。
* `-Xjsr305=@<fq.name>:{strict|warn|ignore}`(自 1.1.60 起)覆蓋單個注解的行為,其中 `<fq.name>`是該注解的完整限定類名。對于不同的注解可以多次出現。這對于管理特定庫的遷移狀態非常有用。
其中 `strict`、 `warn` 與 `ignore` 值的含義與 `MigrationStatus` 中的相同,并且只有 `strict` 模式會影響注解聲明中的類型在 Kotlin 中的呈現。
> 注意:內置的 JSR-305 注解 [`@Nonnull`](https://aalmiray.github.io/jsr-305/apidocs/javax/annotation/Nonnull.html)、 [`@Nullable`](https://aalmiray.github.io/jsr-305/apidocs/javax/annotation/Nullable.html) 與 [`@CheckForNull`](https://aalmiray.github.io/jsr-305/apidocs/javax/annotation/CheckForNull.html) 總是啟用并影響所注解的聲明在 Kotlin 中呈現,無論如何配置編譯器的 `-Xjsr305` 標志。
例如,將 `-Xjsr305=ignore -Xjsr305=under-migration:ignore -Xjsr305=@org.library.MyNullable:warn` 添加到編譯器參數中,會使編譯器對由`@org.library.MyNullable` 標注的不當用法生成警告,而忽略所有其他 JSR-305 注解。
對于 kotlin 1.1.50+/1.2 版本,其默認行為等同于 `-Xjsr305=warn`。`strict` 值應認為是實驗性的(以后可能添加更多檢測)。
## 已映射類型
Kotlin 特殊處理一部分 Java 類型。這樣的類型不是“按原樣”從 Java 加載,而是 _映射_ 到相應的 Kotlin 類型。
映射只發生在編譯期間,運行時表示保持不變。
Java 的原生類型映射到相應的 Kotlin 類型(請記住[平臺類型](http://www.kotlincn.net/docs/reference/java-interop.html#%E7%A9%BA%E5%AE%89%E5%85%A8%E4%B8%8E%E5%B9%B3%E5%8F%B0%E7%B1%BB%E5%9E%8B)):
| **Java 類型** | **Kotlin 類型** |
|---------------|------------------|
| `byte` | `kotlin.Byte` |
| `short` | `kotlin.Short` |
| `int` | `kotlin.Int` |
| `long` | `kotlin.Long` |
| `char` | `kotlin.Char` |
| `float` | `kotlin.Float` |
| `double` | `kotlin.Double` |
| `boolean` | `kotlin.Boolean` |
一些非原生的內置類型也會作映射:
| **Java 類型** | **Kotlin 類型** |
|---------------|------------------|
| `java.lang.Object` | `kotlin.Any!` |
| `java.lang.Cloneable` | `kotlin.Cloneable!` |
| `java.lang.Comparable` | `kotlin.Comparable!` |
| `java.lang.Enum` | `kotlin.Enum!` |
| `java.lang.Annotation` | `kotlin.Annotation!` |
| `java.lang.CharSequence` | `kotlin.CharSequence!` |
| `java.lang.String` | `kotlin.String!` |
| `java.lang.Number` | `kotlin.Number!` |
| `java.lang.Throwable` | `kotlin.Throwable!` |
Java 的裝箱原始類型映射到可空的 Kotlin 類型:
| **Java type** | **Kotlin type** |
|-------------------------|------------------|
| `java.lang.Byte` | `kotlin.Byte?` |
| `java.lang.Short` | `kotlin.Short?` |
| `java.lang.Integer` | `kotlin.Int?` |
| `java.lang.Long` | `kotlin.Long?` |
| `java.lang.Character` | `kotlin.Char?` |
| `java.lang.Float` | `kotlin.Float?` |
| `java.lang.Double` | `kotlin.Double?` |
| `java.lang.Boolean` | `kotlin.Boolean?` |
請注意,用作類型參數的裝箱原始類型映射到平臺類型:例如,`List<java.lang.Integer>` 在 Kotlin 中會成為 `List<Int!>`。
集合類型在 Kotlin 中可以是只讀的或可變的,因此 Java 集合類型作如下映射:(下表中的所有 Kotlin 類型都駐留在 `kotlin.collections`包中):
| **Java 類型** | **Kotlin 只讀類型** | **Kotlin 可變類型** | **加載的平臺類型** |
|---------------|------------------|----|----|
| `Iterator<T>` | `Iterator<T>` | `MutableIterator<T>` | `(Mutable)Iterator<T>!` |
| `Iterable<T>` | `Iterable<T>` | `MutableIterable<T>` | `(Mutable)Iterable<T>!` |
| `Collection<T>` | `Collection<T>` | `MutableCollection<T>` | `(Mutable)Collection<T>!` |
| `Set<T>` | `Set<T>` | `MutableSet<T>` | `(Mutable)Set<T>!` |
| `List<T>` | `List<T>` | `MutableList<T>` | `(Mutable)List<T>!` |
| `ListIterator<T>` | `ListIterator<T>` | `MutableListIterator<T>` | `(Mutable)ListIterator<T>!` |
| `Map<K, V>` | `Map<K, V>` | `MutableMap<K, V>` | `(Mutable)Map<K, V>!` |
| `Map.Entry<K, V>` | `Map.Entry<K, V>` | `MutableMap.MutableEntry<K,V>` | `(Mutable)Map.(Mutable)Entry<K, V>!` |
Java 的數組按[下文](http://www.kotlincn.net/docs/reference/java-interop.html#java-%E6%95%B0%E7%BB%84)所述映射:
| **Java 類型** | **Kotlin 類型** |
|---------------|------------------|
| `int[]` | `kotlin.IntArray!` |
| `String[]` | `kotlin.Array<(out) String>!` |
{:.zebra}
注意:這些 Java 類型的靜態成員不能在相應 Kotlin 類型的[伴生對象](http://www.kotlincn.net/docs/reference/object-declarations.html#%E4%BC%B4%E7%94%9F%E5%AF%B9%E8%B1%A1)中直接訪問。要調用它們,請使用 Java 類型的完整限定名,例如 `java.lang.Integer.toHexString(foo)`。
## Kotlin 中的 Java 泛型
Kotlin 的泛型與 Java 有點不同(參見[泛型](http://www.kotlincn.net/docs/reference/generics.html))。當將 Java 類型導入 Kotlin 時,我們會執行一些轉換:
* Java 的通配符轉換成類型投影,
* `Foo<? extends Bar>` 轉換成 `Foo<out Bar!>!`,
* `Foo<? super Bar>` 轉換成 `Foo<in Bar!>!`;
* Java的原始類型轉換成星投影,
* `List` 轉換成 `List<*>!`,即 `List<out Any?>!`。
和 Java 一樣,Kotlin 在運行時不保留泛型,即對象不攜帶傳遞到他們構造器中的那些類型參數的實際類型。
即 `ArrayList<Integer>()` 和 `ArrayList<Character>()` 是不能區分的。這使得執行 *is*{: .keyword }-檢測不可能照顧到泛型。Kotlin 只允許 *is*{: .keyword }-檢測星投影的泛型類型:
```kotlin
if (a is List<Int>) // 錯誤:無法檢測它是否真的是一個 Int 列表
// but
if (a is List<*>) // OK:不保證列表的內容
```
### Java 數組
與 Java 不同,Kotlin 中的數組是不型變的。這意味著 Kotlin 不允許我們把一個 `Array<String>` 賦值給一個`Array<Any>`,從而避免了可能的運行時故障。Kotlin 也禁止我們把一個子類的數組當做超類的數組傳遞給 Kotlin 的方法,但是對于 Java 方法,這是允許的(通過 `Array<(out) String>!` 這種形式的[平臺類型](http://www.kotlincn.net/docs/reference/java-interop.html#%E7%A9%BA%E5%AE%89%E5%85%A8%E4%B8%8E%E5%B9%B3%E5%8F%B0%E7%B1%BB%E5%9E%8B))。
Java 平臺上,數組會使用原生數據類型以避免裝箱/拆箱操作的開銷。由于 Kotlin 隱藏了這些實現細節,因此需要一個變通方法來與 Java 代碼進行交互。對于每種原生類型的數組都有一個特化的類(`IntArray`、 `DoubleArray`、 `CharArray` 等等)來處理這種情況。它們與 `Array` 類無關,并且會編譯成 Java 原生類型數組以獲得最佳性能。
假設有一個接受 int 數組索引的 Java 方法:
``` java
public class JavaArrayExample {
public void removeIndices(int[] indices) {
// 在此編碼……
}
}
```
在 Kotlin 中你可以這樣傳遞一個原生類型的數組:
```kotlin
val javaObj = JavaArrayExample()
val array = intArrayOf(0, 1, 2, 3)
javaObj.removeIndices(array) // 將 int[] 傳給方法
```
當編譯為 JVM 字節代碼時,編譯器會優化對數組的訪問,這樣就不會引入任何開銷:
```kotlin
val array = arrayOf(1, 2, 3, 4)
array[1] = array[1] * 2 // 不會實際生成對 get() 和 set() 的調用
for (x in array) { // 不會創建迭代器
print(x)
}
```
即使當我們使用索引定位時,也不會引入任何開銷:
```kotlin
for (i in array.indices) {// 不會創建迭代器
array[i] += 2
}
```
最后,*in*-檢測也沒有額外開銷:
```kotlin
if (i in array.indices) { // 同 (i >= 0 && i < array.size)
print(array[i])
}
```
## Java 可變參數
Java 類有時聲明一個具有可變數量參數(varargs)的方法來使用索引:
``` java
public class JavaArrayExample {
public void removeIndicesVarArg(int... indices) {
// 在此編碼……
}
}
```
在這種情況下,你需要使用展開運算符 `*` 來傳遞 `IntArray`:
```kotlin
val javaObj = JavaArrayExample()
val array = intArrayOf(0, 1, 2, 3)
javaObj.removeIndicesVarArg(*array)
```
目前無法傳遞 *null*給一個聲明為可變參數的方法。
## 操作符
由于 Java 無法標記用于運算符語法的方法,Kotlin 允許具有正確名稱和簽名的任何 Java 方法作為運算符重載和其他約定(`invoke()` 等)使用。
不允許使用中綴調用語法調用 Java 方法。
## 受檢異常
在 Kotlin 中,所有異常都是非受檢的,這意味著編譯器不會強迫你捕獲其中的任何一個。因此,當你調用一個聲明受檢異常的 Java 方法時,Kotlin 不會強迫你做任何事情:
```kotlin
fun render(list: List<*>, to: Appendable) {
for (item in list) {
to.append(item.toString()) // Java 會要求我們在這里捕獲 IOException
}
}
```
## 對象方法
當 Java 類型導入到 Kotlin 中時,類型 `java.lang.Object` 的所有引用都成了 `Any`。而因為 `Any` 不是平臺指定的,它只聲明了 `toString()`、`hashCode()` 和 `equals()` 作為其成員,所以為了能用到 `java.lang.Object` 的其他成員,Kotlin 要用到[擴展函數](http://www.kotlincn.net/docs/reference/extensions.html)。
### wait()/notify()
類型 `Any` 的引用沒有提供 `wait()` 與 `notify()` 方法。通常不鼓勵使用它們,而建議使用 `java.util.concurrent`。
如果確實需要調用這兩個方法的話,那么可以將引用轉換為 `java.lang.Object`:
```kotlin
(foo as java.lang.Object).wait()
```
### getClass()
要取得對象的 Java 類,請在[類引用](http://www.kotlincn.net/docs/reference/reflection.html#%E7%B1%BB%E5%BC%95%E7%94%A8)上使用 `java` 擴展屬性:
```kotlin
val fooClass = foo::class.java
```
上面的代碼使用了自 Kotlin 1.1 起支持的[綁定的類引用](http://www.kotlincn.net/docs/reference/reflection.html#%E7%BB%91%E5%AE%9A%E7%9A%84%E7%B1%BB%E5%BC%95%E7%94%A8%E8%87%AA-11-%E8%B5%B7)。你也可以使用 `javaClass` 擴展屬性:
```kotlin
val fooClass = foo.javaClass
```
### clone()
要覆蓋 `clone()`,需要繼承 `kotlin.Cloneable`:
```kotlin
class Example : Cloneable {
override fun clone(): Any { …… }
}
```
不要忘記[《Effective Java》第三版](http://www.oracle.com/technetwork/java/effectivejava-136174.html) 的第 13 條: *謹慎地改寫clone*。
### finalize()
要覆蓋 `finalize()`,所有你需要做的就是簡單地聲明它,而不需要 *override*關鍵字:
```kotlin
class C {
protected fun finalize() {
// 終止化邏輯
}
}
```
根據 Java 的規則,`finalize()` 不能是 *private*的。
## 從 Java 類繼承
在 kotlin 中,類的超類中最多只能有一個 Java 類(以及按你所需的多個 Java 接口)。
## 訪問靜態成員
Java 類的靜態成員會形成該類的“伴生對象”。我們無法將這樣的“伴生對象”作為值來傳遞,但可以顯式訪問其成員,例如:
```kotlin
if (Character.isLetter(a)) { …… }
```
要訪問[已映射](http://www.kotlincn.net/docs/reference/java-interop.html#%E5%B7%B2%E6%98%A0%E5%B0%84%E7%B1%BB%E5%9E%8B)到 Kotlin 類型的 Java 類型的靜態成員,請使用 Java 類型的完整限定名:`java.lang.Integer.bitCount(foo)`。
## Java 反射
Java 反射適用于 Kotlin 類,反之亦然。如上所述,你可以使用 `instance::class.java`,`ClassName::class.java` 或者 `instance.javaClass` 通過 `java.lang.Class` 來進入 Java 反射。
其他支持的情況包括為一個 Kotlin 屬性獲取一個 Java 的 getter/setter 方法或者幕后字段、為一個 Java 字段獲取一個 `KProperty`、為一個 `KFunction` 獲取一個 Java 方法或者構造函數,反之亦然。
## SAM 轉換
就像 Java 8 一樣,Kotlin 支持 SAM 轉換。這意味著 Kotlin 函數字面值可以被自動的轉換成只有一個非默認方法的 Java 接口的實現,只要這個方法的參數類型能夠與這個 Kotlin 函數的參數類型相匹配。
你可以這樣創建 SAM 接口的實例:
```kotlin
val runnable = Runnable { println("This runs in a runnable") }
```
……以及在方法調用中:
```kotlin
val executor = ThreadPoolExecutor()
// Java 簽名:void execute(Runnable command)
executor.execute { println("This runs in a thread pool") }
```
如果 Java 類有多個接受函數式接口的方法,那么可以通過使用將 lambda 表達式轉換為特定的 SAM 類型的適配器函數來選擇需要調用的方法。這些適配器函數也會按需由編譯器生成:
```kotlin
executor.execute(Runnable { println("This runs in a thread pool") })
```
請注意,SAM 轉換只適用于接口,而不適用于抽象類,即使這些抽象類也只有一個抽象方法。
還要注意,此功能只適用于 Java 互操作;因為 Kotlin 具有合適的函數類型,所以不需要將函數自動轉換為 Kotlin 接口的實現,因此不受支持。
## 在 Kotlin 中使用 JNI
要聲明一個在本地(C 或 C++)代碼中實現的函數,你需要使用 `external` 修飾符來標記它:
```kotlin
external fun foo(x: Int): Double
```
其余的過程與 Java 中的工作方式完全相同。
- 前言
- Kotlin簡介
- IntelliJ IDEA技巧總結
- idea設置類注釋和方法注釋模板
- 像Android Studion一樣創建工程
- Gradle
- Gradle入門
- Gradle進階
- 使用Gradle創建一個Kotlin工程
- 環境搭建
- Androidstudio平臺搭建
- Eclipse的Kotlin環境配置
- 使用IntelliJ IDEA
- Kotlin學習路線
- Kotlin官方中文版文檔教程
- 概述
- kotlin用于服務器端開發
- kotlin用于Android開發
- kotlin用于JavaScript開發
- kotlin用于原生開發
- Kotlin 用于數據科學
- 協程
- 多平臺
- 新特性
- 1.1的新特性
- 1.2的新特性
- 1.3的新特性
- 開始
- 基本語法
- 習慣用法
- 編碼規范
- 基礎
- 基本類型
- 包與導入
- 控制流
- 返回與跳轉
- 類與對象
- 類與繼承
- 屬性與字段
- 接口
- 可見性修飾符
- 擴展
- 數據類
- 密封類
- 泛型
- 嵌套類
- 枚舉類
- 對象
- 類型別名
- 內嵌類
- 委托
- 委托屬性
- 函數與Lambda表達式
- 函數
- Lambda表達式
- 內聯函數
- 集合
- 集合概述
- 構造集合
- 迭代器
- 區間與數列
- 序列
- 操作概述
- 轉換
- 過濾
- 加減操作符
- 分組
- 取集合的一部分
- 取單個元素
- 排序
- 聚合操作
- 集合寫操作
- List相關操作
- Set相關操作
- Map相關操作
- 多平臺程序設計
- 平臺相關聲明
- 以Gradle創建
- 更多語言結構
- 解構聲明
- 類型檢測與轉換
- This表達式
- 相等性
- 操作符重載
- 空安全
- 異常
- 注解
- 反射
- 作用域函數
- 類型安全的構造器
- Opt-in Requirements
- 核心庫
- 標準庫
- kotlin.test
- 參考
- 關鍵字與操作符
- 語法
- 編碼風格約定
- Java互操作
- Kotlin中調用Java
- Java中調用Kotlin
- JavaScript
- 動態類型
- kotlin中調用JavaScript
- JavaScript中調用kotlin
- JavaScript模塊
- JavaScript反射
- JavaScript DCE
- 原生
- 并發
- 不可變性
- kotlin庫
- 平臺庫
- 與C語言互操作
- 與Object-C及Swift互操作
- CocoaPods集成
- Gradle插件
- 調試
- FAQ
- 協程
- 協程指南
- 基礎
- 取消與超時
- 組合掛起函數
- 協程上下文與調度器
- 異步流
- 通道
- 異常處理與監督
- 共享的可變狀態與并發
- Select表達式(實驗性)
- 工具
- 編寫kotlin代碼文檔
- 使用Kapt
- 使用Gradle
- 使用Maven
- 使用Ant
- Kotlin與OSGI
- 編譯器插件
- 編碼規范
- 演進
- kotlin語言演進
- 不同組件的穩定性
- kotlin1.3的兼容性指南
- 常見問題
- FAQ
- 與Java比較
- 與Scala比較(官方已刪除)
- Google開發者官網簡介
- Kotlin and Android
- Get Started with Kotlin on Android
- Kotlin on Android FAQ
- Android KTX
- Resources to Learn Kotlin
- Kotlin樣品
- Kotlin零基礎到進階
- 第一階段興趣入門
- kotlin簡介和學習方法
- 數據類型和類型系統
- 入門
- 分類
- val和var
- 二進制基礎
- 基礎
- 基本語法
- 包
- 示例
- 編碼規范
- 代碼注釋
- 異常
- 根類型“Any”
- Any? 可空類型
- 可空性的實現原理
- kotlin.Unit類型
- kotlin.Nothing類型
- 基本數據類型
- 數值類型
- 布爾類型
- 字符型
- 位運算符
- 變量和常量
- 語法和運算符
- 關鍵字
- 硬關鍵字
- 軟關鍵字
- 修飾符關鍵字
- 特殊標識符
- 操作符和特殊符號
- 算術運算符
- 賦值運算符
- 比較運算符
- 邏輯運算符
- this關鍵字
- super關鍵字
- 操作符重載
- 一元操作符
- 二元操作符
- 字符串
- 字符串介紹和屬性
- 字符串常見方法操作
- 字符串模板
- 數組
- 數組介紹創建及遍歷
- 數組常見方法和屬性
- 數組變化以及下標越界問題
- 原生數組類型
- 區間
- 正向區間
- 逆向區間
- 步長
- 類型檢測與類型轉換
- is、!is、as、as-運算符
- 空安全
- 可空類型變量
- 安全調用符
- 非空斷言
- Elvis操作符
- 可空性深入
- 可空性和Java
- 函數
- 函數式編程概述
- OOP和FOP
- 函數式編程基本特性
- 組合與范疇
- 在Kotlin中使用函數式編程
- 函數入門
- 函數作用域
- 函數加強
- 命名參數
- 默認參數
- 可變參數
- 表達式函數體
- 頂層、嵌套、中綴函數
- 尾遞歸函數優化
- 函數重載
- 控制流
- if表達式
- when表達式
- for循環
- while循環
- 循環中的 Break 與 continue
- return返回
- 標簽處返回
- 集合
- list集合
- list集合介紹和操作
- list常見方法和屬性
- list集合變化和下標越界
- set集合
- set集合介紹和常見操作
- set集合常見方法和屬性
- set集合變換和下標越界
- map集合
- map集合介紹和常見操作
- map集合常見方法和屬性
- map集合變換
- 集合的函數式API
- map函數
- filter函數
- “ all ”“ any ”“ count ”和“ find ”:對集合應用判斷式
- 別樣的求和方式:sumBy、sum、fold、reduce
- 根據人的性別進行分組:groupBy
- 扁平化——處理嵌套集合:flatMap、flatten
- 惰性集合操作:序列
- 區間、數組、集合之間轉換
- 面向對象
- 面向對象-封裝
- 類的創建及屬性方法訪問
- 類屬性和字段
- 構造器
- 嵌套類(內部類)
- 枚舉類
- 枚舉類遍歷&枚舉常量常用屬性
- 數據類
- 密封類
- 印章類(密封類)
- 面向對象-繼承
- 類的繼承
- 面向對象-多態
- 抽象類
- 接口
- 接口和抽象類的區別
- 面向對象-深入
- 擴展
- 擴展:為別的類添加方法、屬性
- Android中的擴展應用
- 優化Snackbar
- 用擴展函數封裝Utils
- 解決煩人的findViewById
- 擴展不是萬能的
- 調度方式對擴展函數的影響
- 被濫用的擴展函數
- 委托
- 委托類
- 委托屬性
- Kotlin5大內置委托
- Kotlin-Object關鍵字
- 單例模式
- 匿名類對象
- 伴生對象
- 作用域函數
- let函數
- run函數
- with函數
- apply函數
- also函數
- 標準庫函數
- takeIf 與 takeUnless
- 第二階段重點深入
- Lambda編程
- Lambda成員引用高階函數
- 高階函數
- 內聯函數
- 泛型
- 泛型的分類
- 泛型約束
- 子類和子類型
- 協變與逆變
- 泛型擦除與實化類型
- 泛型類型參數
- 泛型的背后:類型擦除
- Java為什么無法聲明一個泛型數組
- 向后兼容的罪
- 類型擦除的矛盾
- 使用內聯函數獲取泛型
- 打破泛型不變
- 一個支持協變的List
- 一個支持逆變的Comparator
- 協變和逆變
- 第三階段難點突破
- 注解和反射
- 聲明并應用注解
- DSL
- 協程
- 協程簡介
- 協程的基本操作
- 協程取消
- 管道
- 慕課霍丙乾協程筆記
- Kotlin與Java互操作
- 在Kotlin中調用Java
- 在Java中調用Kotlin
- Kotlin與Java中的操作對比
- 第四階段專題練習
- 朱凱Kotlin知識點總結
- Kotlin 基礎
- Kotlin 的變量、函數和類型
- Kotlin 里那些「不是那么寫的」
- Kotlin 里那些「更方便的」
- Kotlin 進階
- Kotlin 的泛型
- Kotlin 的高階函數、匿名函數和 Lambda 表達式
- Kotlin協程
- 初識
- 進階
- 深入
- Kotlin 擴展
- 會寫「18.dp」只是個入門——Kotlin 的擴展函數和擴展屬性(Extension Functions / Properties)
- Kotlin實戰-開發Android