# Java 中調用 Kotlin
[TOC]
Java 可以輕松調用 Kotlin 代碼。
例如,Kotlin 類的實例可以在 Java 方法中無縫創建和操作。但是,在將 Kotlin 代碼集成到 Java 中時,Java 和 Kotlin 之間存在某些差異。在此頁上,我們將介紹將 Kotlin 代碼與其 Java 客戶端的互操作定制的方法。
## 屬性
Kotlin 屬性會編譯成以下 Java 元素:
* 一個 getter 方法,名稱通過加前綴 `get` 算出;
* 一個 setter 方法,名稱通過加前綴 `set` 算出(只適用于 `var` 屬性);
* 一個私有字段,與屬性名稱相同(僅適用于具有幕后字段的屬性)。
例如,`var firstName: String` 編譯成以下 Java 聲明:
``` java
private String firstName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
```
如果屬性的名稱以 `is` 開頭,則使用不同的名稱映射規則:getter 的名稱與屬性名稱相同,并且 setter 的名稱是通過將 `is` 替換為 `set` 獲得。
例如,對于屬性 `isOpen`,其 getter 會稱做 `isOpen()`,而其 setter 會稱做 `setOpen()`。這一規則適用于任何類型的屬性,并不僅限于 `Boolean`。
## 包級函數
在 `org.example` 包內的 `app.kt` 文件中聲明的所有的函數和屬性,包括擴展函數,都編譯成一個名為 `org.example.AppKt` 的 Java 類的靜態方法
```kotlin
// app.kt
package org.example
class Util
fun getTime() { /*……*/ }
```
``` java
// Java
new org.example.Util();
org.example.AppKt.getTime();
```
可以使用 `@JvmName` 注解修改生成的 Java 類的類名:
```kotlin
@file:JvmName("DemoUtils")
package org.example
class Util
fun getTime() { /*……*/ }
```
``` java
// Java
new org.example.Util();
org.example.DemoUtils.getTime();
```
如果多個文件中生成了相同的 Java 類名(包名相同并且類名相同或者有相同的[`@JvmName`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-jvm-name/index.html) 注解)通常是錯誤的。然而,編譯器能夠生成一個單一的 Java 外觀類,它具有指定的名稱且包含來自所有文件中具有該名稱的所有聲明。
要啟用生成這樣的外觀,請在所有相關文件中使用 [`@JvmMultifileClass`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-jvm-multifile-class/index.html) 注解。
```kotlin
// oldutils.kt
@file:JvmName("Utils")
@file:JvmMultifileClass
package org.example
fun getTime() { /*……*/ }
```
```kotlin
// newutils.kt
@file:JvmName("Utils")
@file:JvmMultifileClass
package org.example
fun getDate() { /*……*/ }
```
``` java
// Java
org.example.Utils.getTime();
org.example.Utils.getDate();
```
## 實例字段
如果需要在 Java 中將 Kotlin 屬性作為字段暴露,那就使用 [`@JvmField`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-jvm-field/index.html) 注解對其標注。
該字段將具有與底層屬性相同的可見性。如果一個屬性有幕后字段(backing field)、非私有、沒有 `open`、`override` 或者 `const` 修飾符并且不是被委托的屬性,那么你可以用 `@JvmField` 注解該屬性。
```kotlin
class User(id: String) {
@JvmField val ID = id
}
```
``` java
// Java
class JavaClient {
public String getID(User user) {
return user.ID;
}
}
```
[延遲初始化的](http://www.kotlincn.net/docs/reference/properties.html#%E5%BB%B6%E8%BF%9F%E5%88%9D%E5%A7%8B%E5%8C%96%E5%B1%9E%E6%80%A7%E4%B8%8E%E5%8F%98%E9%87%8F)屬性(在Java中)也會暴露為字段。該字段的可見性與 `lateinit` 屬性的 setter 相同。
## 靜態字段
在命名對象或伴生對象中聲明的 Kotlin 屬性會在該命名對象或包含伴生對象的類中具有靜態幕后字段。
通常這些字段是私有的,但可以通過以下方式之一暴露出來:
- [`@JvmField`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-jvm-field/index.html) 注解;
- `lateinit` 修飾符;
- `const` 修飾符。
使用 `@JvmField` 標注這樣的屬性使其成為與屬性本身具有相同可見性的靜態字段。
```kotlin
class Key(val value: Int) {
companion object {
@JvmField
val COMPARATOR: Comparator<Key> = compareBy<Key> { it.value }
}
}
```
``` java
// Java
Key.COMPARATOR.compare(key1, key2);
// Key 類中的 public static final 字段
```
在命名對象或者伴生對象中的一個[延遲初始化的](http://www.kotlincn.net/docs/reference/properties.html#%E5%BB%B6%E8%BF%9F%E5%88%9D%E5%A7%8B%E5%8C%96%E5%B1%9E%E6%80%A7%E4%B8%8E%E5%8F%98%E9%87%8F)屬性具有與屬性 setter 相同可見性的靜態幕后字段。
```kotlin
object Singleton {
lateinit var provider: Provider
}
```
``` java
// Java
Singleton.provider = new Provider();
// 在 Singleton 類中的 public static 非-final 字段
```
(在類中以及在頂層)以 `const` 聲明的屬性在 Java 中會成為靜態字段:
```kotlin
// 文件 example.kt
object Obj {
const val CONST = 1
}
class C {
companion object {
const val VERSION = 9
}
}
const val MAX = 239
```
在 Java 中:
``` java
int const = Obj.CONST;
int max = ExampleKt.MAX;
int version = C.VERSION;
```
## 靜態方法
如上所述,Kotlin 將包級函數表示為靜態方法。Kotlin 還可以為命名對象或伴生對象中定義的函數生成靜態方法,如果你將這些函數標注為 [`@JvmStatic`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-jvm-static/index.html) 的話。如果你使用該注解,編譯器既會在相應對象的類中生成靜態方法,也會在對象自身中生成實例方法。
例如:
```kotlin
class C {
companion object {
@JvmStatic fun callStatic() {}
fun callNonStatic() {}
}
}
```
現在,`callStatic()` 在 Java 中是靜態的,而 `callNonStatic()` 不是:
``` java
C.callStatic(); // 沒問題
C.callNonStatic(); // 錯誤:不是一個靜態方法
C.Companion.callStatic(); // 保留實例方法
C.Companion.callNonStatic(); // 唯一的工作方式
```
對于命名對象也同樣:
```kotlin
object Obj {
@JvmStatic fun callStatic() {}
fun callNonStatic() {}
}
```
在 Java 中:
``` java
Obj.callStatic(); // 沒問題
Obj.callNonStatic(); // 錯誤
Obj.INSTANCE.callNonStatic(); // 沒問題,通過單例實例調用
Obj.INSTANCE.callStatic(); // 也沒問題
```
Starting from Kotlin 1.3, `@JvmStatic` applies to functions defined in companion objects of interfaces as well.
Such functions compile to static methods in interfaces. Note that static method in interfaces were introduced in Java 1.8,so be sure to use the corresponding targets.
```kotlin
interface ChatBot {
companion object {
@JvmStatic fun greet(username: String) {
println("Hello, $username")
}
}
}
```
`@JvmStatic` 注解也可以應用于對象或伴生對象的屬性,使其 getter 和 setter 方法在該對象或包含該伴生對象的類中是靜態成員。
## Default methods in interfaces
> Default methods are available only for targets JVM 1.8 and above.
> The `@JvmDefault` annotation is experimental in Kotlin 1.3. Its name and behavior may change, leading to future incompatibility.
Starting from JDK 1.8, interfaces in Java can contain [default methods](https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html).
You can declare a non-abstract member of a Kotlin interface as default for the Java classes implementing it.
To make a member default, mark it with the [`@JvmDefault`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-jvm-default/index.html) annotation.
Here is an example of a Kotlin interface with a default method:
```kotlin
interface Robot {
@JvmDefault fun move() { println("~walking~") }
fun speak(): Unit
}
```
The default implementation is available for Java classes implementing the interface.
```java
//Java implementation
public class C3PO implements Robot {
// move() implementation from Robot is available implicitly
@Override
public void speak() {
System.out.println("I beg your pardon, sir");
}
}
```
```java
C3PO c3po = new C3PO();
c3po.move(); // default implementation from the Robot interface
c3po.speak();
```
Implementations of the interface can override default methods.
```java
//Java
public class BB8 implements Robot {
//own implementation of the default method
@Override
public void move() {
System.out.println("~rolling~");
}
@Override
public void speak() {
System.out.println("Beep-beep");
}
}
```
For the `@JvmDefault` annotation to take effect, the interface must be compiled with an `-Xjvm-default` argument.
Depending on the case of adding the annotation, specify one of the argument values:
* `-Xjvm-default=enabled` should be used if you add only new methods with the `@JvmDefault` annotation.
This includes adding the entire interface for your API.
* `-Xjvm-default=compatibility` should be used if you are adding a `@JvmDefault` to the methods that were available in the API before.
This mode helps avoid compatibility breaks: all the interface implementations written for the previous versions will be fully compatible with the new version.
However, the compatibility mode may add some overhead to the resulting bytecode size and affect the performance.
For more details about compatibility issues, see the `@JvmDefault` [reference page](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-jvm-default/index.html).
Note that if an interface with `@JvmDefault` methods is used as a [delegate](/docs/reference/delegation.html),
the default method implementations are called even if the actual delegate type provides its own implementations.
```kotlin
interface Producer {
@JvmDefault fun produce() {
println("interface method")
}
}
class ProducerImpl: Producer {
override fun produce() {
println("class method")
}
}
class DelegatedProducer(val p: Producer): Producer by p {
}
fun main() {
val prod = ProducerImpl()
DelegatedProducer(prod).produce() // prints "interface method"
}
```
For more details about interface delegation in Kotlin, see [Delegation](/docs/reference/delegation.html).
## 可見性
Kotlin 的可見性以下列方式映射到 Java:
* `private` 成員編譯成 `private` 成員;
* `private` 的頂層聲明編譯成包級局部聲明;
* `protected` 保持 `protected`(注意 Java 允許訪問同一個包中其他類的受保護成員,
而 Kotlin 不能,所以 Java 類會訪問更廣泛的代碼);
* `internal` 聲明會成為 Java 中的 `public`。`internal` 類的成員會通過名字修飾,使其更難以在 Java 中意外使用到,并且根據 Kotlin 規則使其允許重載相同簽名的成員而互不可見;
* `public` 保持 `public`。
## KClass
有時你需要調用有 `KClass` 類型參數的 Kotlin 方法。因為沒有從 `Class` 到 `KClass` 的自動轉換,所以你必須通過調用`Class<T>.kotlin` 擴展屬性的等價形式來手動進行轉換:
```kotlin
kotlin.jvm.JvmClassMappingKt.getKotlinClass(MainView.class)
```
## 用 `@JvmName` 解決簽名沖突
有時我們想讓一個 Kotlin 中的命名函數在字節碼中有另外一個 JVM 名稱。最突出的例子是由于*類型擦除*引發的:
```kotlin
fun List<String>.filterValid(): List<String>
fun List<Int>.filterValid(): List<Int>
```
這兩個函數不能同時定義,因為它們的 JVM 簽名是一樣的:`filterValid(Ljava/util/List;)Ljava/util/List;`。
如果我們真的希望它們在 Kotlin 中用相同名稱,我們需要用 [`@JvmName`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-jvm-name/index.html) 去標注其中的一個(或兩個),并指定不同的名稱作為參數:
```kotlin
fun List<String>.filterValid(): List<String>
@JvmName("filterValidInt")
fun List<Int>.filterValid(): List<Int>
```
在 Kotlin 中它們可以用相同的名稱 `filterValid` 來訪問,而在 Java 中,它們分別是 `filterValid` 和 `filterValidInt`。
同樣的技巧也適用于屬性 `x` 和函數 `getX()` 共存:
```kotlin
val x: Int
@JvmName("getX_prop")
get() = 15
fun getX() = 10
```
如需在沒有顯式實現 getter 與 setter 的情況下更改屬性生成的訪問器方法的名稱,可以使用 `@get:JvmName` 與 `@set:JvmName`:
```kotlin
@get:JvmName("x")
@set:JvmName("changeX")
var x: Int = 23
```
## 生成重載
通常,如果你寫一個有默認參數值的 Kotlin 函數,在 Java 中只會有一個所有參數都存在的完整參數簽名的方法可見,如果希望向 Java 調用者暴露多個重載,可以使用[`@JvmOverloads`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-jvm-overloads/index.html) 注解。
該注解也適用于構造函數、靜態方法等。它不能用于抽象方法,包括在接口中定義的方法。
```kotlin
class Circle @JvmOverloads constructor(centerX: Int, centerY: Int, radius: Double = 1.0) {
@JvmOverloads fun draw(label: String, lineWidth: Int = 1, color: String = "red") { /*……*/ }
}
```
對于每一個有默認值的參數,都會生成一個額外的重載,這個重載會把這個參數和它右邊的所有參數都移除掉。在上例中,會生成以下代碼:
``` java
// 構造函數:
Circle(int centerX, int centerY, double radius)
Circle(int centerX, int centerY)
// 方法
void draw(String label, int lineWidth, String color) { }
void draw(String label, int lineWidth) { }
void draw(String label) { }
```
請注意,如[次構造函數](http://www.kotlincn.net/docs/reference/classes.html#%E6%AC%A1%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0)中所述,如果一個類的所有構造函數參數都有默認值,那么會為其生成一個公有的無參構造函數。這就算沒有 `@JvmOverloads` 注解也有效。
## 受檢異常
如上所述,Kotlin 沒有受檢異常。所以,通常 Kotlin 函數的 Java 簽名不會聲明拋出異常。于是如果我們有一個這樣的 Kotlin 函數:
```kotlin
// example.kt
package demo
fun writeToFile() {
/*……*/
throw IOException()
}
```
然后我們想要在 Java 中調用它并捕捉這個異常:
``` java
// Java
try {
demo.Example.writeToFile();
}
catch (IOException e) { // 錯誤:writeToFile() 未在 throws 列表中聲明 IOException
// ……
}
```
因為 `writeToFile()` 沒有聲明 `IOException`,我們從 Java 編譯器得到了一個報錯消息。為了解決這個問題,要在 Kotlin 中使用 [`@Throws`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-throws/index.html) 注解。
```kotlin
@Throws(IOException::class)
fun writeToFile() {
/*……*/
throw IOException()
}
```
## 空安全性
當從 Java 中調用 Kotlin 函數時,沒人阻止我們將 *null* 作為非空參數傳遞。這就是為什么 Kotlin 給所有期望非空參數的公有函數生成運行時檢測。這樣我們就能在 Java 代碼里立即得到 `NullPointerException`。
## 型變的泛型
當 Kotlin 的類使用了[聲明處型變](generics.html#聲明處型變),有兩種選擇可以從 Java 代碼中看到它們的用法。讓我們假設我們有以下類和兩個使用它的函數:
```kotlin
class Box<out T>(val value: T)
interface Base
class Derived : Base
fun boxDerived(value: Derived): Box<Derived> = Box(value)
fun unboxBase(box: Box<Base>): Base = box.value
```
一種看似理所當然地將這倆函數轉換成 Java 代碼的方式可能會是:
``` java
Box<Derived> boxDerived(Derived value) { …… }
Base unboxBase(Box<Base> box) { …… }
```
問題是,在 Kotlin 中我們可以這樣寫 `unboxBase(boxDerived("s"))`,但是在 Java 中是行不通的,因為在 Java 中類 `Box` 在其泛型參數 `T` 上是*不型變的*,于是 `Box<Derived>` 并不是 `Box<Base>` 的子類。要使其在 Java 中工作,我們按以下這樣定義 `unboxBase`:
``` java
Base unboxBase(Box<? extends Base> box) { …… }
```
這里我們使用 Java 的*通配符類型*(`? extends Base`)來通過使用處型變來模擬聲明處型變,因為在 Java 中只能這樣。
當它*作為參數*出現時,為了讓 Kotlin 的 API 在 Java 中工作,對于協變定義的 `Box` 我們生成 `Box<Super>` 作為 `Box<? extends Super>`(或者對于逆變定義的 `Foo` 生成 `Foo<? super Bar>`)。當它是一個返回值時,我們不生成通配符,因為否則 Java 客戶端將必須處理它們(并且它違反常用Java 編碼風格)。因此,我們的示例中的對應函數實際上翻譯如下:
``` java
// 作為返回類型——沒有通配符
Box<Derived> boxDerived(Derived value) { …… }
// 作為參數——有通配符
Base unboxBase(Box<? extends Base> box) { …… }
```
當參數類型是 final 時,生成通配符通常沒有意義,所以無論在什么地方 `Box<String>`始終轉換為 `Box<String>`。
如果我們在默認不生成通配符的地方需要通配符,我們可以使用 `@JvmWildcard` 注解:
```kotlin
fun boxDerived(value: Derived): Box<@JvmWildcard Derived> = Box(value)
// 將被轉換成
// Box<? extends Derived> boxDerived(Derived value) { …… }
```
另一方面,如果我們根本不需要默認的通配符轉換,我們可以使用`@JvmSuppressWildcards`
```kotlin
fun unboxBase(box: Box<@JvmSuppressWildcards Base>): Base = box.value
// 會翻譯成
// Base unboxBase(Box<Base> box) { …… }
```
`@JvmSuppressWildcards` 不僅可用于單個類型參數,還可用于整個聲明(如函數或類),從而抑制其中的所有通配符。
### `Nothing` 類型翻譯
類型 [`Nothing`](exceptions.html#nothing-類型) 是特殊的,因為它在 Java 中沒有自然的對應。確實,每個 Java 引用類型,包括`java.lang.Void` 都可以接受 `null` 值,但是 Nothing 不行。因此,這種類型不能在 Java 世界中準確表示。這就是為什么在使用 `Nothing` 參數的地方 Kotlin 生成一個原始類型:
```kotlin
fun emptyList(): List<Nothing> = listOf()
// 會翻譯成
// List emptyList() { …… }
```
- 前言
- 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