├── Native ├── FAQ.md ├── C-Interop.md ├── Debugging.md ├── Gradle-Plugin.md ├── Immutability.md ├── Kotlin-Libraries.md ├── Platform-Libraries.md ├── CocoaPods-Integration.md ├── Objective-C-and-Swift-Interop.md ├── Symbolication-iOS-Crash-Reports.md ├── README.md └── Concurency.md ├── Collections ├── Grouping.md ├── Ording.md ├── Squences.md ├── Filtering.md ├── Iterators.md ├── OperationsOverview.md ├── Transformations.md ├── AggregateOperations.md ├── CollectionsOverview.md ├── ConstructionCollections.md ├── ListSepcificOperations.md ├── MapSepcificOperations.md ├── PlusandMinusOperators.md ├── RangesandProgressions.md ├── RetrivingSingleElements.md ├── SetSepcificOperations.md ├── CollectionWriteOperations.md ├── RetrievingCollectionParts.md └── README.md ├── CoreLibraries └── README.md ├── Evolution └── README.md ├── Overview ├── Coroutines.md ├── KotlinForAndroid.md ├── KotlinForNative.md ├── WhatNewIn1.1.md ├── WhatNewIn1.2.md ├── WhatNewIn1.3.md ├── KotlinForDateScience.md ├── KotlinForJavaScript.md ├── KotlinForServerSide.md ├── README.md └── Mutiplatform.md ├── .gitignore ├── Reference ├── README.md ├── Grammar.md └── API-Reference.md ├── Interop ├── README.md └── Java-Interop.md ├── JavaScript └── README.md ├── coroutines ├── SelectExpression.md ├── README.md ├── CoroutinesGuide.md ├── CancellationAndTimeouts.md ├── Basics.md ├── SharedMutableStateAndConcurrency.md └── ComposingSuspendingFunctions.md ├── MoreLanguageConstructs ├── Type-SafeBuilders.md ├── ExperimentalAPIMarkers.md ├── README.md ├── Equality.md ├── Dynamic-Type.md ├── This-Expression.md ├── Multi-Declarations.md ├── Exceptions.md ├── Type-Checks-and-Casts.md ├── Null-Safety.md ├── Annotations.md ├── DestructuringDeclarations.md ├── Opetator-overloading.md ├── Reflection.md └── Ranges.md ├── cover.jpg ├── FAQ ├── README.md ├── FAQ ├── Comparison2Scala.md └── Comparison2java.md ├── kotlinLOC.png ├── Tools ├── Documenting-Kotlin-Code.md ├── Using-Griffon.md ├── README.md ├── Using-Gradle.md ├── Using-Maven.md └── Using-Ant.md ├── Basics ├── README.md ├── Packages.md ├── Returns-and-Jumps.md ├── Control-Flow.md └── Basic-Types.md ├── MutiplatformProgramming ├── README.md ├── mpp-one-compilation.png ├── mpp-structure-default-jvm-js.png └── Platform-Specific-Declarations.md ├── GettingStarted ├── README.md ├── Coding-Conventions.md ├── Idioms.md ├── Basic-Syntax.md └── what-new-in-1.1.md ├── FunctionsAndLambdas ├── README.md ├── InlineFunctions.md ├── Higher-OrderFunctionsAndLambdas.md ├── Functions.md └── Cocroutines.md ├── ClassesAndObjects ├── README.md ├── SealedClasses.md ├── EnumClasses.md ├── NestedClasses.md ├── Interfaces.md ├── Data-Classes.md ├── Delegation.md ├── Visibility-Modifiers.md ├── ObjectExpressicAndDeclarations.md ├── Extensions.md ├── Properties-and-Fields.md ├── InlineClasses.md ├── Classes-and-Inheritance.md ├── DelegationProperties.md └── Generics.md ├── SUMMARY.md └── README.md /Native/FAQ.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Collections/Grouping.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Collections/Ording.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Collections/Squences.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CoreLibraries/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Evolution/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Native/C-Interop.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Native/Debugging.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Native/Gradle-Plugin.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Native/Immutability.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Overview/Coroutines.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /Collections/Filtering.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Collections/Iterators.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Native/Kotlin-Libraries.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Native/Platform-Libraries.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Overview/KotlinForAndroid.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Overview/KotlinForNative.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Overview/WhatNewIn1.1.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Overview/WhatNewIn1.2.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Overview/WhatNewIn1.3.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Reference/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Collections/OperationsOverview.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Collections/Transformations.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Interop/README.md: -------------------------------------------------------------------------------- 1 | Interop 2 | -------------------------------------------------------------------------------- /JavaScript/README.md: -------------------------------------------------------------------------------- 1 | waiting morty -------------------------------------------------------------------------------- /Native/CocoaPods-Integration.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Overview/KotlinForDateScience.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Overview/KotlinForJavaScript.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Overview/KotlinForServerSide.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Reference/Grammar.md: -------------------------------------------------------------------------------- 1 | grammer 2 | -------------------------------------------------------------------------------- /coroutines/SelectExpression.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Collections/AggregateOperations.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Collections/CollectionsOverview.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Collections/ConstructionCollections.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Collections/ListSepcificOperations.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Collections/MapSepcificOperations.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Collections/PlusandMinusOperators.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Collections/RangesandProgressions.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Collections/RetrivingSingleElements.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Collections/SetSepcificOperations.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Collections/CollectionWriteOperations.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Collections/RetrievingCollectionParts.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /MoreLanguageConstructs/Type-SafeBuilders.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Native/Objective-C-and-Swift-Interop.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Native/Symbolication-iOS-Crash-Reports.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /MoreLanguageConstructs/ExperimentalAPIMarkers.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huanglizhuo/kotlin-in-chinese/HEAD/cover.jpg -------------------------------------------------------------------------------- /FAQ/README.md: -------------------------------------------------------------------------------- 1 | * [与 java 对比](Comparison2java.md) 2 | * [与 Scala 对比](Comparison2Scala.md) 3 | -------------------------------------------------------------------------------- /kotlinLOC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huanglizhuo/kotlin-in-chinese/HEAD/kotlinLOC.png -------------------------------------------------------------------------------- /Tools/Documenting-Kotlin-Code.md: -------------------------------------------------------------------------------- 1 | [原文](http://kotlinlang.org/docs/reference/kotlin-doc.html) 2 | 3 | 待翻译 请暂时参考原文 -------------------------------------------------------------------------------- /Tools/Using-Griffon.md: -------------------------------------------------------------------------------- 1 | Griffon 支持参看[provided externally](https://github.com/griffon/griffon-kotlin-plugin) 2 | -------------------------------------------------------------------------------- /Basics/README.md: -------------------------------------------------------------------------------- 1 | * [基本类型](Basic-Types.md) 2 | * [包](Packages.md) 3 | * [控制流程](Control-Flow.md) 4 | * [返回与跳转](Returns-and-Jumps.md) 5 | -------------------------------------------------------------------------------- /MutiplatformProgramming/README.md: -------------------------------------------------------------------------------- 1 | * [平台相关声明](./Platform-Specific-Declarations.md) 2 | * [用 gradle 构建多平台工程](./Building-with-Gradle.md) 3 | -------------------------------------------------------------------------------- /GettingStarted/README.md: -------------------------------------------------------------------------------- 1 | * [基本语法](Basic-Syntax.md) 2 | * [常用术语](Idioms.md) 3 | * [编码风格](Coding-Conventions.md) 4 | * [1.1中的新特性](Coding-Conventions.md) 5 | -------------------------------------------------------------------------------- /MutiplatformProgramming/mpp-one-compilation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huanglizhuo/kotlin-in-chinese/HEAD/MutiplatformProgramming/mpp-one-compilation.png -------------------------------------------------------------------------------- /MutiplatformProgramming/mpp-structure-default-jvm-js.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huanglizhuo/kotlin-in-chinese/HEAD/MutiplatformProgramming/mpp-structure-default-jvm-js.png -------------------------------------------------------------------------------- /Tools/README.md: -------------------------------------------------------------------------------- 1 | * [Kotlin代码文档](Documenting-Kotlin-Code.md) 2 | * [使用Maven](Using-Maven.md) 3 | * [使用Ant](Using-Ant.md) 4 | * [使用Griffon](Using-Griffon.md) 5 | * [使用Gradle](Using-Gradle.md) 6 | -------------------------------------------------------------------------------- /FunctionsAndLambdas/README.md: -------------------------------------------------------------------------------- 1 | * [函数和lambda表达式](README.md) 2 | * [函数](Functions.md) 3 | * [高阶函数和lambda表达式](Higher-OrderFunctionsAndLambdas.md) 4 | * [内联函数](InlineFunctions.md) 5 | * [协程](Cocroutines.md) 6 | -------------------------------------------------------------------------------- /Overview/README.md: -------------------------------------------------------------------------------- 1 | * [Kotlin 服务端](KotlinForServerSide.md) 2 | * [Kotlin Android 端](KotlinForAndroid.md) 3 | * [Kotiln ](KotlinForJavaScript.md) 4 | * [Kotlin 原生](KotlinForNative.md) 5 | * [Kotlin 数据科学](KotlinForDateScience.md) 6 | * [协程](Coroutines.md) 7 | * [多平台](Mutiplatform.md) 8 | * [1.1 新特性](WhatNewIn1.1.md) 9 | * [1.2 新特性](WhatNewIn1.2.md) 10 | * [1.3 新特性](WhatNewIn1.3.md) 11 | -------------------------------------------------------------------------------- /MoreLanguageConstructs/README.md: -------------------------------------------------------------------------------- 1 | * [解构声明](DestructuringDeclarations.md) 2 | * [类型检查和自动转换](Type-Checks-and-Casts.md) 3 | * [This表达式](This-Expression.md) 4 | * [等式](Equality.md) 5 | * [运算符重载](Opetator-overloading.md) 6 | * [空安全](Null-Safety.md) 7 | * [异常](Exceptions.md) 8 | * [注解](Annotations.md) 9 | * [反射](Reflection.md) 10 | * [作用域函数](ScopeFunctions.md) 11 | * [类型安全构造器](Type-SafeBuilders.md) 12 | * [试验性 API 标注](ExperimentalAPIMarkers.md) -------------------------------------------------------------------------------- /Native/README.md: -------------------------------------------------------------------------------- 1 | * [并行](./Concurency.md) 2 | * [不变性](./Immutability.md) 3 | * [Kotlin 库](./Kotlin-Libraries.md) 4 | * [平台库](./Platform-Libraries.md) 5 | * [与 C 交互](./C-Interop.md) 6 | * [与 Objective-C Swift 交互](./Objective-C-and-Swift-Interop.md) 7 | * [符号化 iOS 崩溃报告](./Symbolication-iOS-Crash-Reports.md) 8 | * [CocoaPods 集成](./CocoaPods-Integration.md) 9 | * [Gradle 插件](./Gradle-Plugin.md) 10 | * [调试](Debugging.md) 11 | * [FAQ](FAQ.md) 12 | 13 | -------------------------------------------------------------------------------- /coroutines/README.md: -------------------------------------------------------------------------------- 1 | * [协程](README.md) 2 | * [协程指南](CoroutinesGuide.md) 3 | * [基础](Basics.md) 4 | * [取消和超时](CancellationAndTimeouts.md) 5 | * [组合挂起函数](ComposingSuspendingFunctions.md) 6 | * [协程上下文和调度器](CoroutineContextAndDispatchers.md) 7 | * [异步流](AsynchronousFlow.md) 8 | * [频道](Channels.md) 9 | * [异常处理](ExceptionHandling.md) 10 | * [共享可变状态与并发](SharedMutableStateAndConcurrency.md) 11 | * [Select 表达式](SelectExpression.md) 12 | -------------------------------------------------------------------------------- /Interop/Java-Interop.md: -------------------------------------------------------------------------------- 1 | ## 交互 2 | ### java 交互 3 | Kotlin 在设计时就是以与 java 交互为中心的。现存的 java 代码可以在 kotlin 中使用,并且 Kotlin 代码也可以在 java 中流畅运行。这节我们会讨论在 kotlin 中调用 java 代码。 4 | 5 | #### 在 kotlin 中调用 java 代码 6 | 基本所有的 Java 代码都可以运行 7 | 8 | ```kotlin 9 | import java.util.* 10 | fun demo(source: List) { 11 | val list = ArrayList() 12 | for (item in source ) 13 | list.add(item) 14 | for (i in 0..source.size() - 1) 15 | list[i] = source[i] 16 | } 17 | ``` 18 | 19 | **空的返回** 20 | 21 | 如果 Java 方法返回空,则在 Kotlin 调用中返回 `Unit`。如果 22 | -------------------------------------------------------------------------------- /ClassesAndObjects/README.md: -------------------------------------------------------------------------------- 1 | * [类和对象](README.md) 2 | * [类和继承](Classes-and-Inheritance.md) 3 | * [属性和字段](Properties-and-Fields.md) 4 | * [接口](Interfaces.md) 5 | * [可见性修饰词](Visibility-Modifiers.md) 6 | * [扩展](Extensions.md) 7 | * [数据对象](Data-Classes.md) 8 | * [密封类](SealedClasses.md) 9 | * [泛型](Generics.md) 10 | * [嵌套类](NestedClasses.md) 11 | * [枚举类](EnumClasses.md) 12 | * [对象表达式和声明](ObjectExpressicAndDeclarations.md) 13 | * [内联类](InlineClasses).md) 14 | * [委托模式](Delegation.md) 15 | * [委托属性](DelegationProperties.md) 16 | -------------------------------------------------------------------------------- /MoreLanguageConstructs/Equality.md: -------------------------------------------------------------------------------- 1 | ## 相等 2 | 在 kotlin 中有俩中相等: 3 | 4 | >参照相等(指向相同的对象) 5 | >结构相等 6 | 7 | ### 参照相等 8 | 参照相等是通过 `===` 操作符判断的(不等是`!==` ) a===b 只有 a b 指向同一个对象是判别才成立。 9 | 10 | 11 | 另外,你可以使用内联函数 `identityEquals()` 判断参照相等: 12 | 13 | ```kotlin 14 | a.identityEquals(b) 15 | a identityEquals b 16 | ``` 17 | 18 | ### 结构相等 19 | 结构相等是通过 `==` 判断的。像 `a == b` 将会翻译成: 20 | 21 | ```kotlin 22 | a?.equals(b) ?: b === null 23 | ``` 24 | 25 | 如果 a 不是 null 则调用 `equals(Any?)` 函数,否则检查 b 是否参照等于 null 26 | 27 | 注意完全没有必要为优化你的代码而将 `a == null` 写成 `a === null` 编译器会自动帮你做的。 28 | 29 | -------------------------------------------------------------------------------- /Collections/README.md: -------------------------------------------------------------------------------- 1 | * [集合](README.md) 2 | * [集合概览](CollectionsOverview.md) 3 | * [结构化集合](ConstructionCollections.md) 4 | * [迭代器](Iterators.md) 5 | * [范围和进度](RangesandProgressions.md) 6 | * [序列](Squences.md) 7 | * [操作概览](OperationsOverview.md) 8 | * [转化](Transformations.md) 9 | * [过滤](Filtering.md) 10 | * [加减操作符](PlusandMinusOperators.md) 11 | * [分组](Grouping.md) 12 | * [取得部分集合](RetrievingCollectionParts.md) 13 | * [取得单个元素](RetrivingSingleElements.md) 14 | * [排序](Ording.md) 15 | * [聚合操作](AggregateOperations.md) 16 | * [集合写曹锁](CollectionWriteOperations.md) 17 | * [只针对于 list 的操作](ListSepcificOperations.md) 18 | * [只针对于 set 的操作](SetSepcificOperations.md) 19 | * [只针对于 map 的操作](MapSepcificOperations.md) -------------------------------------------------------------------------------- /FAQ/FAQ: -------------------------------------------------------------------------------- 1 | ##FAQ 2 | 3 | ###常见问题 4 | 5 | ####什么是 kotlin 6 | 7 | kotlin 是一门面向 JVM 和 JavaScript 的静态类型的语言。目标是工业使用。 8 | 9 | 由 JetBrains 的团队开发,但它是一门 OSS 语言也有很多外部贡献者。 10 | 11 | ####为什么需要一门新语言 12 | 13 | 在 JetBrains 我们在 Java 平台上开发了很久,我们很了解它。另一方面,我们知道 java 语言有很多限制和问题,但由于落后的兼容性问题使得它们很难修复。我们知道 java 会长久存在下去的,但我们相信社区会从这些新的静态类型,并且面向 JVM 的语言获得益处,比如远离传统问题以及获得那些开发者很渴望的功能。 14 | 15 | 在这个项目背后最主要的设计目标是: 16 | 17 | >创造一门兼容 java 的语言 18 | 19 | >编译速度至少和 java 一样 20 | 21 | >比 java 更安全,例如如静态检查,从而避免诸如空指针这样的常见错误 22 | 23 | >通过支持不同的类型接口,高阶函数(闭包),扩展函数,混入,一级代理等等,使代码比 java 更简洁 24 | 25 | >并且在保持易用性的前提下是它比成熟的 Scala 更简单。 26 | 27 | ####如何获得它的版权? 28 | 29 | Kotlin 是一门 OSS 语言并且是 Apacha 2 OSS 许可下。IntelliJ Plug-in也是 OSS的 30 | 31 | 它在 github 上,欢迎大家贡献代码 32 | 33 | ####它兼容 java 吗? 34 | 35 | 当然。 -------------------------------------------------------------------------------- /ClassesAndObjects/SealedClasses.md: -------------------------------------------------------------------------------- 1 | 当值可以具有受限集中的一种类型但不能具有任何其他类型时,密封类用于表示受限类层次结构。从某种意义上讲,它们是枚举类的扩展:枚举类型的值集也受到限制,但是每个枚举常量仅作为单个实例存在,而密封类的子类可以具有多个实例,这些实例可以包含状态。 2 | 3 | 要声明一个密封类,可以将 `sealed` 修饰符放在该类的名称之前。密封类可以具有子类,但是所有子类必须与密封类本身在同一文件中声明。 (在Kotlin 1.1之前,规则更加严格:类必须嵌套在密封类的声明中)。 4 | 5 | ```Kotlin 6 | sealed class Expr 7 | data class Const(val number: Double) : Expr() 8 | data class Sum(val e1: Expr, val e2: Expr) : Expr() 9 | object NotANumber : Expr() 10 | ``` 11 | 12 | (上面的示例使用了Kotlin 1.1的另一项新特性:数据类可以扩展其他类,包括密封类)。 13 | 14 | 密封类本身是抽象的,不能直接实例化,并且可以具有抽象成员。 15 | 16 | 密封的类不允许具有非私有的构造函数(默认情况下,它们的构造函数是私有的)。 17 | 18 | 请注意,继承密封类的子类的类(间接继承程序)可以放置在任何位置,而不必放在同一文件中。 19 | 20 | 当在when表达式中使用密封类时,使用密封类的主要好处就发挥了作用。如果可以验证该语句是否涵盖所有情况,则无需在该语句中添加 else 子句。但仅当将 when 用作表达式(使用结果)而不用作语句时,此方法才有效。 21 | 22 | ```Kotlin 23 | fun eval(expr: Expr): Double = when(expr) { 24 | is Const -> expr.number 25 | is Sum -> eval(expr.e1) + eval(expr.e2) 26 | NotANumber -> Double.NaN 27 | // the `else` clause is not required because we've covered all the cases 28 | } 29 | ``` -------------------------------------------------------------------------------- /MoreLanguageConstructs/Dynamic-Type.md: -------------------------------------------------------------------------------- 1 | ## 动态类型 2 | 作为静态类型的语言,kotlin任然拥有与无类型或弱类型语言的调用,比如 javaScript。为了方便使用,`dynamic`应运而生: 3 | 4 | ```kotlin 5 | val dyn: dynamic = ... 6 | ``` 7 | 8 | `dynamic` 类型关闭了 kotlin 的类型检查: 9 | 10 | >这样的类型可以分配任意变量或者在任意的地方作为参数传递 11 | >任何值都可以分配为`dynamic` 类型,或者作为参数传递给任何接受 `dynamic` 类型参数的函数 12 | >这样的类型不做 null 检查 13 | 14 | `dynamic` 最奇特的特性就是可以在 `dynamic` 变量上调用任何属性或任何方法: 15 | (The most peculiar feature of dynamic is that we are allowed to call any property or function with any parameters on a dynamic variable:) 16 | ```kotlin 17 | dyn.whatever(1, "foo", dyn) // 'whatever' is not defined anywhere 18 | dyn.whatever(*array(1, 2, 3)) 19 | ``` 20 | 21 | 在 javaScript 平台上这样的代码会按照现有的样子编译:Kotlin 中的 `dyn.whatever(1)` 在生成的代码中变成 JavaScript 语言的 `dyn.whatever(1)`。 22 | 23 | 动态调用可以返回 `dynamic` 作为结果,因此我们可以轻松实现链式调用: 24 | 25 | ```kotlin 26 | dyn.foo().bar.bat(0 27 | ``` 28 | 29 | 当给动态调用传递一个 lambda 表达式时,所有的参数默认都是 `dynamic`: 30 | 31 | ```kotlin 32 | dyn.foo { 33 | x -> x.bar() // x is dynamic 34 | } 35 | ``` 36 | 37 | 更多细节参看[spec document](https://github.com/JetBrains/kotlin/blob/master/spec-docs/dynamic-types.md) 38 | -------------------------------------------------------------------------------- /MoreLanguageConstructs/This-Expression.md: -------------------------------------------------------------------------------- 1 | ## This 表达式 2 | 为了记录下当前接受者,我们使用 this 表达式: 3 | 4 | > 在类的成员中,this 表示当前类的对象 5 | 6 | > 在[扩展函数](http://kotlinlang.org/docs/reference/extensions.html)或[扩展字面函数](http://kotlinlang.org/docs/reference/lambdas.html#function-literals)中,this 表示 . 左边接收者参数 7 | 8 | 如果 this 没有应用者,则指向的是最内层的闭合范围。为了在其它范围中返回 this ,需要使用标签 9 | 10 | ### this使用范围 11 | 为了在范围外部(一个类,或者表达式函数,或者带标签的扩展字面函数)访问 this ,我们需要在使用 `this@lable` 作为 lable 12 | 13 | ```kotlin 14 | class A { // implicit label @A 15 | inner class B { // implicit label @B 16 | fun Int.foo() { // implicit label @foo 17 | val a = this@A // A's this 18 | val b = this@B // B's this 19 | 20 | val c = this // foo()'s receiver, an Int 21 | val c1 = this@foo // foo()'s receiver, an Int 22 | 23 | val funLit = @lambda {String.() -> 24 | val d = this // funLit's receiver 25 | val d1 = this@lambda // funLit's receiver 26 | } 27 | 28 | 29 | val funLit2 = { (s: String) -> 30 | // foo()'s receiver, since enclosing function literal 31 | // doesn't have any receiver 32 | val d1 = this 33 | } 34 | } 35 | } 36 | } 37 | ``` 38 | -------------------------------------------------------------------------------- /ClassesAndObjects/EnumClasses.md: -------------------------------------------------------------------------------- 1 | ## 枚举类 2 | 枚举类最基本的用法就是实现类型安全的枚举 3 | 4 | ```kotlin 5 | enum class Direction { 6 | NORTH,SOUTH,WEST 7 | } 8 | ``` 9 | 10 | 每个自举常量都是一个对象。枚举常量通过逗号分开。 11 | 12 | ### 初始化 13 | 因为每个枚举都是枚举类的一个实例,它们是可以初始化的。 14 | 15 | ```kotlin 16 | enum class Color(val rgb: Int) { 17 | RED(0xFF0000), 18 | GREEN(0x00FF00), 19 | BLUE(0x0000FF) 20 | } 21 | ``` 22 | 23 | ### 匿名类 24 | 枚举实例也可以声明它们自己的匿名类 25 | 26 | ```kotlin 27 | enum class ProtocolState { 28 | WAITING { 29 | override fun signal() = Taking 30 | }, 31 | Taking{ 32 | override fun signal() = WAITING 33 | }; 34 | abstract fun signal(): ProtocolState 35 | } 36 | ``` 37 | 38 | 可以有对应的方法,以及复写基本方法。注意如果枚举定义了任何成员,你需要像在 java 中那样用分号 ; 把枚举常量定义和成员定义分开。 39 | 40 | ### 使用枚举常量 41 | 像 java 一样,Kotlin 中的枚举类有合成方法允许列出枚举常量的定义并且通过名字获得枚举常量。这些方法的签名就在下面列了出来(假设枚举类名字是 EnumClass): 42 | 43 | ```kotlin 44 | EnumClass.valueOf(value: String): EnumClass 45 | EnumClass.values(): Array 46 | ``` 47 | 48 | 如果指定的名字在枚举类中没有任何匹配,那么`valueOf()`方法将会抛出参数异常。 49 | 50 | 每个枚举常量都有获取在枚举类中声明的名字和位置的方法: 51 | 52 | ```kotlin 53 | name(): Sting 54 | ordinal(): Int 55 | ``` 56 | 57 | 枚举类也实现了 [Comparable](http://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-comparable/index.html) 接口,比较时使用的是它们在枚举类定义的自然顺序。 58 | -------------------------------------------------------------------------------- /ClassesAndObjects/NestedClasses.md: -------------------------------------------------------------------------------- 1 | ## 嵌套类 2 | 类可以嵌套在其他类中 3 | 4 | ```kotlin 5 | class Outer { 6 | private val bar: Int = 1 7 | class Nested { 8 | fun foo() = 2 9 | } 10 | } 11 | 12 | val demo = Outer.Nested().foo() //==2 13 | ``` 14 | 15 | ### 内部类 16 | 类可以标记为 inner 这样就可以访问外部类的成员。内部类拥有外部类的一个对象引用: 17 | 18 | ```kotlin 19 | class Outer { 20 | private val bar: Int = 1 21 | inner class Inner { 22 | fun foo() = bar 23 | } 24 | } 25 | 26 | val demo = Outer().Inner().foo() //==1 27 | ``` 28 | 29 | 参看[这里](http://kotlinlang.org/docs/reference/this-expressions.html)了解更多 this 在内部类的用法 30 | 31 | ### 匿名内部类 32 | 匿名内部类的实例是通过 [对象表达式](ClassesAndObjects/ObjectExpressicAndDeclarations.md) 创建的: 33 | 34 | ```kotlin 35 | window.addMouseListener(object: MouseAdapter() { 36 | override fun mouseClicked(e: MouseEvent) { 37 | // ... 38 | } 39 | 40 | override fun mouseEntered(e: MouseEvent) { 41 | // ... 42 | } 43 | }) 44 | ``` 45 | 46 | 如果对象是函数式的 java 接口的实例(比如只有一个抽象方法的 java 接口),你可以用一个带接口类型的 lambda 表达式创建它。 47 | 48 | 49 | 50 | ```kot 51 | val listener = ActionListener { println("clicked") } 52 | ``` 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /FAQ/Comparison2Scala.md: -------------------------------------------------------------------------------- 1 | ## 与 Scala 对比 2 | Kotlin 设计时的俩个主要目标是: 3 | 4 | > 至少和 java 运行速度一样快 5 | 6 | > 在保证语言尽量简单的情况下在易用性上提高 7 | 8 | 考虑到这俩点,如果你喜欢 Scala ,你可能不需要 Kotlin 9 | 10 | ### Scala 有而 Kotlin 没有的 11 | > 隐式转换,隐式参数 12 | --在 Scala 中,在不适用 debugger 的时候很难知道代码发生了什么,因为太多的东西是隐式的 13 | --通过函数增加类型在 kotlin 中需要使用[扩展函数](http://kotlinlang.org/docs/reference/extensions.html) 14 | 15 | > 可重载和类型成员 16 | 17 | > 路径依赖的类型 18 | 19 | > 宏 20 | 21 | > Existential types 22 | --类型推断是很特殊的情形 23 | 24 | > 特征的初始化逻辑很复杂 25 | --参看[类和继承](http://kotlinlang.org/docs/reference/classes.html) 26 | 27 | >自定义象征操作 28 | --参看[操作符重载](http://kotlinlang.org/docs/reference/operator-overloading.html) 29 | 30 | > 内建 xml 31 | --参看[Type-safe Groovy-style builders](http://kotlinlang.org/docs/reference/type-safe-builders.html) 32 | 33 | 以后 kotlin可能会添加的特性: 34 | 35 | > 结构类型 36 | 37 | > 值类型 38 | 39 | > Yield 操作符 40 | 41 | > Actors 42 | 43 | > 并行集合(Parallel collections) 44 | 45 | ### Kotlin 有而 Scala 没有的 46 | >零开销的null安全 47 | - Scala的是Option,是在句法和运行时的包装 48 | 49 | >[ Smart casts](http://kotlinlang.org/docs/reference/typecasts.html) 50 | 51 | >[Kotlin 的内联函数非局部的跳转](http://kotlinlang.org/docs/reference/inline-functions.html#inline-functions) 52 | 53 | > [First-class delegation](http://kotlinlang.org/docs/reference/delegation.html)。也通过第三方插件:Autoproxy实现 54 | -------------------------------------------------------------------------------- /Native/Concurency.md: -------------------------------------------------------------------------------- 1 | Kotlin / Native 运行时不鼓励使用带有互斥代码块和条件变量的经典的面向线程的并发模型,因为该模型容易出错且不可靠。 相反,我们推荐使用一系列替代方案,来允许你使用硬件并发并实现阻塞IO。 这些方法如下,并将在后面的部分中详细说明: 2 | 3 | - 可传递消息的 Worker 4 | - 对象子图所有权转移 5 | - 对象子图冻结 6 | - 对象子图分离 7 | - 使用C全局变量的原始共享内存 8 | - 用于阻止操作的协程(本文档未涵盖) 9 | 10 | ## Works 11 | 12 | Kotlin / Native运行提供 Workers 的概念替换线程:带有请求队列的并发可执行控制流。 Workers 与 Actor 模型中的 Actor 非常相似。 一个 Worker 可以与另一个 Worker 交换 Kotlin 对象,以便在任何时候每个可变对象都由一个 Worker 拥有,但是所有权可以转移。 请参阅[对象传输和冻结部分](https://kotlinlang.org/docs/reference/native/concurrency.html#transfer)。 13 | 14 | 一旦使用 `Worker.start` 函数调用启动 worker,便可以通过唯一整数 ID 进行寻址。 其他 worker 或非 worker 并发原语(例如OS线程)可以通过 `execute` 调用向 worker 发送消息。 15 | 16 | ```kotlin 17 | val future = execute(TransferMode.SAFE, { SomeDataForWorker() }) { 18 | // data returned by the second function argument comes to the 19 | // worker routine as 'input' parameter. 20 | input -> 21 | // Here we create an instance to be returned when someone consumes result future. 22 | WorkerResult(input.stringParam + " result") 23 | } 24 | 25 | future.consume { 26 | // Here we see result returned from routine above. Note that future object or 27 | // id could be transferred to another worker, so we don't have to consume future 28 | // in same execution context it was obtained. 29 | result -> println("result is $result") 30 | } 31 | ``` -------------------------------------------------------------------------------- /coroutines/CoroutinesGuide.md: -------------------------------------------------------------------------------- 1 | Kotlin 作为一门语言,只在标准库中提供了最基本的底层 APIs 以供其它库使用协程。与许多其他具有类似功能的语言不同,`async` 和 `await` 并不是 Kotlin 的关键字,甚至都没有包含在标准库中。此外,Kotlin的挂起函数的概念为异步操作提供了比异步操作 futures 和 promises 更安全且更不容易出错的抽象。 2 | 3 | `kotlinx.coroutines` 是由 Jetbrains 为协程开发的一个功能丰富的库。包含了许多支持协程高阶原函数,包括 `launch` `async`以及其它。这些均将在本册指南中描述。 4 | 5 | 这部 `kotlinx.coroutines` 的指南包含了许多关于关键特性的例子,并被分为不同的主题。 6 | 7 | 为了使用协程以及按照本指南中的例子练习,需要添加 kotlinx-coroutines-core 模块依赖。参考 [README](https://github.com/kotlin/kotlinx.coroutines/blob/master/README.md#using-in-your-projects) 8 | 9 | 内容目录 10 | 11 | * [协程](README.md) 12 | * [协程指南](CoroutinesGuide.md) 13 | * [基础](Basics.md) 14 | * [取消和超时](CancellationAndTimeouts.md) 15 | * [频道](Channels.md) 16 | * [组合挂起函数](ComposingSuspendingFunctions.md) 17 | * [协程上下文和调度器](CoroutineContextAndDispatchers.md) 18 | * [异常处理](ExceptionHandling.md) 19 | * [Select 表达式](SelectExpression.md) 20 | * [共享可变状态与并发](SharedMutableStateAndConcurrency.md) 21 | 22 | 附加参考 23 | 24 | * [Guide to UI programming with coroutines](https://github.com/kotlin/kotlinx.coroutines/blob/master/ui/coroutines-guide-ui.md) 25 | * [Guide to reactive streams with coroutines](https://github.com/kotlin/kotlinx.coroutines/blob/master/reactive/coroutines-guide-reactive.md) 26 | * [Coroutines design document (KEEP)](https://github.com/Kotlin/kotlin-coroutines-examples/blob/master/kotlin-coroutines-informal.md) 27 | * [Full kotlinx.coroutines API reference](https://kotlin.github.io/kotlinx.coroutines/) -------------------------------------------------------------------------------- /ClassesAndObjects/Interfaces.md: -------------------------------------------------------------------------------- 1 | ## 接口 2 | Kotlin 的接口很像 java 8。它们都可以包含抽象方法,以及方法的实现。和抽象类不同的是,接口不能保存状态。可以有属性但必须是抽象的,或者提供访问器的实现。 3 | 4 | 接口用关键字 `interface` 来定义: 5 | 6 | ```kotlin 7 | interface MyInterface { 8 | fun bar() 9 | fun foo() { 10 | //函数体是可选的 11 | } 12 | } 13 | ``` 14 | 15 | ### 实现接口 16 | 一个类或对象可以实现一个或多个接口 17 | 18 | ```kotlin 19 | class Child : MyInterface { 20 | fun bar () { 21 | //函数体 22 | } 23 | } 24 | ``` 25 | 26 | ### 接口中的属性 27 | 可以在接口中申明属性。接口中的属性要么是抽象的,要么提供访问器的实现。接口属性不可以有后备字段。而且访问器不可以引用它们。 28 | 29 | ```kotlin 30 | interface MyInterface { 31 | val property: Int // abstract 32 | 33 | val propertyWithImplementation: String 34 | get() = "foo" 35 | 36 | fun foo() { 37 | print(property) 38 | } 39 | } 40 | 41 | class Child : MyInterface { 42 | override val property: Int = 29 43 | } 44 | ``` 45 | 46 | ### 解决重写冲突 47 | 当我们在父类中声明了许多类型,有可能出现一个方法的多种实现。比如: 48 | 49 | ```kotlin 50 | interface A { 51 | fun foo() { print("A") } 52 | fun bar() 53 | } 54 | 55 | interface B { 56 | fun foo() { print("B") } 57 | fun bar() { print("bar") } 58 | } 59 | 60 | class C : A { 61 | override fun bar() { print("bar") } 62 | } 63 | 64 | class D : A, B { 65 | override fun foo() { 66 | super.foo() 67 | super.foo() 68 | } 69 | 70 | override fun bar() { 71 | super.bar() 72 | } 73 | } 74 | ``` 75 | 76 | A B 接口都有声明了 foo() 和 bar() 函数。它们都实现了 foo() 方法,但只有 B 实现了 bar() ,bar() 在 A 中并没有声明它是抽象的,这是因为在接口中如果函数没有函数体,那么默认是抽像的。 77 | 78 | 不过,如果我们从 A 中派生一个 C 实体类,显然我们需要重写 bar() ,并实现它。而我们从 A 和 B 派生一个 D ,我们不用重写 bar() 方法,因为我们的一个继承中有一个已经实现了它。但我们继承了两个 foo() 的实现,因此编译器不知道应该选哪个,并强制我们重写 foo() 并且明确指出我们想怎么实现。 79 | -------------------------------------------------------------------------------- /MoreLanguageConstructs/Multi-Declarations.md: -------------------------------------------------------------------------------- 1 | ## 多重声明 2 | 有时候可以通过给对象插入多个成员函数做区别是很方便的,比如: 3 | 4 | ```kotlin 5 | val (name, age) = person 6 | ``` 7 | 8 | 这种语法叫多重声明。多重声明一次创建了多个变量。我们声明了俩个新变量:`name` `age` 并且可以独立使用: 9 | 10 | ```kotlin 11 | println(name) 12 | println(age) 13 | ``` 14 | 15 | 多重声明被编译成下面的代码: 16 | 17 | ```kotlin 18 | val name = persion.component1() 19 | val age = persion.component2() 20 | ``` 21 | 22 | `component1()` `component2()`是另一个转换原则的例子。任何类型都可以在多重分配的右边。当然了,也可以有 `component3()` `component4()` 等等 23 | 24 | 多重声明也可以在 for 循环中用 25 | 26 | ```kotlin 27 | for ((a, b) in collection) { ... } 28 | ``` 29 | 30 | 参数 `a` 和 `b` 是 `component1()` `component2()` 的返回值 31 | 32 | ### 例子:一个函数返回俩个值 33 | 要是一个函数想返回俩个值。比如,一个对象结果,一个是排序的状态。在 Kotlin 中的一个紧凑的方案是声明 [data](http://kotlinlang.org/docs/reference/data-classes.html) 类并返回实例: 34 | 35 | ```kotlin 36 | data class Result(val result: Int, val status: Status) 37 | 38 | fun function(...): Result { 39 | //... 40 | return Result(result, status) 41 | } 42 | 43 | val (result, status) = function(...) 44 | ``` 45 | 数据类自动声明 `componentN()` 函数 46 | 47 | 注意:也可以使用标准类 `Pair` 并让函数返回 'Pair',但可读性不是很强 48 | 49 | ### 例子:多重声明和 Map 50 | 转换 map 的最好办法可能是下面这样: 51 | 52 | ```kotlin 53 | for ((key, value) in map) { 54 | 55 | } 56 | ``` 57 | 58 | 为了让这个可以工作,我们需要 59 | 60 | >通过提供 `iterator()` 函数序列化呈现 map 61 | >通过 `component1()`和 `component1()` 函数是把元素成对呈现 62 | 63 | 事实上,标准库提供了这样的扩展: 64 | 65 | ```kotlin 66 | fun Map.iterator(): Iterator> = entrySet().iterator() 67 | fun Map.Entry.component1() = getKey() 68 | fun Map.Entry.component2() = getValue() 69 | ``` 70 | 71 | 因此你可以用 for 循环方便的读取 map (或者其它数据集合) 72 | -------------------------------------------------------------------------------- /GettingStarted/Coding-Conventions.md: -------------------------------------------------------------------------------- 1 | [原文](http://kotlinlang.org/docs/reference/coding-conventions.html) 2 | 3 | ## 编码规范 4 | 5 | 本页包含了当前 kotlin 语言的代码风格。 6 | 7 | ### 命名风格 8 | 如有疑惑,默认为Java编码约定,比如: 9 | 10 | > --使用骆驼命名法(在命名中避免下划线) 11 | 12 | > --类型名称首字母大写 13 | 14 | > --方法和属性首字母小写 15 | 16 | > --缩进用四个空格 17 | 18 | > --public 方法要写说明文档,这样它就可以出现在 Kotlin Doc 中 19 | 20 | ### 冒号 21 | 在冒号区分类型和父类型中要有空格,在实例和类型之间是没有空格的: 22 | 23 | ```kotlin 24 | interface Foo : Bar { 25 | fun foo(a: Int): T 26 | } 27 | ``` 28 | 29 | ### Lambdas 30 | 在 Lambdas 表达式中,大括号与表达式间要有空格,箭头与参数和函数体间要有空格。lambda表达应尽可能不要写在圆括号中 31 | 32 | ```Kotlin 33 | list.filter { it > 10 }.map { element -> element * 2 } 34 | ``` 35 | 36 | 在使用简短而非嵌套的lambda中,建议使用`it`而不是显式地声明参数。在使用参数的嵌套lambda中,参数应该总是显式声明 37 | 38 | ### 类声明格式 39 | 参数比较少的类可以用一行表示: 40 | 41 | ```Kotlin 42 | class Person(id: Int, name: String) 43 | ``` 44 | 45 | 具有较多的参数的类应该格式化成每个构造函数的参数都位于与缩进的单独行中。此外,结束括号应该在新行上。如果我们使用继承,那么超类构造函数调用或实现的接口列表应该位于与括号相同的行中 46 | 47 | ```Kotlin 48 | class Person( 49 | id: Int, 50 | name: String, 51 | surname: String 52 | ) : Human(id, name) { 53 | // ... 54 | } 55 | ``` 56 | 57 | 对于多个接口,应该首先定位超类构造函数调用,然后每个接口应该位于不同的行中 58 | 59 | ```Kotlin 60 | class Person( 61 | id: Int, 62 | name: String, 63 | surname: String 64 | ) : Human(id, name), 65 | KotlinMaker { 66 | // ... 67 | } 68 | ``` 69 | 70 | 构造函数参数可以使用常规缩进或连续缩进(双倍正常缩进)。 71 | 72 | ### Unit 73 | 如果函数返回 Unit ,返回类型应该省略: 74 | 75 | ```kotlin 76 | fun foo() { // ": Unit"被省略了 77 | } 78 | ``` 79 | 80 | ### 函数 vs 属性 81 | 在某些情况下,没有参数的函数可以与只读属性互换。尽管语义是相似的,但是有一些风格上的约定在什么时候更偏向于另一个。 82 | 83 | 在下面的情况下,更偏向于属性而不是一个函数: 84 | > -- 不需要抛出异常 85 | > -- 复杂度为O(1) 86 | > -- 低消耗的计算(或首次运行结果会被缓存) 87 | > -- 返回与调用相同的结果 88 | -------------------------------------------------------------------------------- /MoreLanguageConstructs/Exceptions.md: -------------------------------------------------------------------------------- 1 | ## 异常 2 | ### 异常类 3 | 所有的异常类都是 `Exception` 的子类。每个异常都有一个消息,栈踪迹和可选的原因。 4 | 5 | 使用 throw 表达式,抛出异常 6 | 7 | ```kotlin 8 | throw MyException("Hi There!") 9 | ``` 10 | 使用 try 捕获异常 11 | 12 | ```kotlin 13 | try { 14 | // some code 15 | } 16 | catch (e: SomeException) { 17 | // handler 18 | } 19 | finally { 20 | // optional finally block 21 | } 22 | ``` 23 | 24 | 有可能有不止一个的 catch 块。finally 块可以省略。 25 | 26 | ### try 是一个表达式 27 | try 可以有返回值: 28 | 29 | ```kotlin 30 | val a: Int? = try { parseInt(input) } catch (e: NumberFormatException) { null } 31 | ``` 32 | 33 | try 返回值要么是 try 块的最后一个表达式,要么是 catch 块的最后一个表达式。finally 块的内容不会对表达式有任何影响。 34 | 35 | ### 检查异常 36 | Kotlin 中没有异常检查。这是由多种原因造成的,我们这里举个简单的例子 37 | 38 | 下面是 JDK `StringBuilder` 类实现的一个接口 39 | 40 | ```java 41 | Appendable append(CharSequence csq) throws IOException; 42 | ``` 43 | 44 | 这个签名说了什么? 它说每次我把 string 添加到什么东西(StringBuilder 或者 log console 等等)上时都会捕获 `IOExceptions` 为什么呢?因为可能涉及到 IO 操作(Writer 也实现了 Appendable)... 所以导致所有实现 Appendable 的接口都得捕获异常 45 | 46 | ```java 47 | try { 48 | log.append(message) 49 | } 50 | catch (IOException e) { 51 | // Must be safe 52 | } 53 | ``` 54 | 55 | 这样是不利的,参看[Effective java ](http://www.oracle.com/technetwork/java/effectivejava-136174.html) 56 | 57 | Bruce Eckel 在[java 需要异常检查吗?](http://www.mindview.net/Etc/Discussions/CheckedExceptions)说到: 58 | 59 | > Examination of small programs leads to the conclusion that requiring exception specifications could both enhance developer productivity and enhance code quality, but experience with large software projects suggests a different result – decreased productivity and little or no increase in code quality. 60 | 61 | ### java 互动 62 | 参看 [Java Interoperability section](http://kotlinlang.org/docs/reference/java-interop.html) 63 | -------------------------------------------------------------------------------- /Basics/Packages.md: -------------------------------------------------------------------------------- 1 | ## 包 2 | 代码文件以包声明开始: 3 | 4 | ```kotlin 5 | package foo.bar 6 | 7 | fun bza() {} 8 | 9 | class Goo {} 10 | 11 | //... 12 | ``` 13 | 14 | 代码文件的所有内容(比如类和函数)都被包含在包声明中。因此在上面的例子中, `bza() ` 的全名应该是 `foo.bar.bza` ,`Goo` 的全名是 `foo.bar.Goo`。 15 | 16 | 如果没有指定包名,那这个文件的内容就从属于没有名字的 "default" 包。 17 | 18 | ### 默认导入 19 | 许多包被默认导入到每个Kotlin文件中: 20 | 21 | > -- kotlin.* 22 | > 23 | > -- kotlin.annotation.* 24 | > 25 | > -- kotlin.collections.* 26 | > 27 | > -- kotlin.comparisons.* (since 1.1) 28 | > 29 | > -- kotlin.io.* 30 | > 31 | > -- kotlin.ranges.* 32 | > 33 | > -- kotlin.sequences.* 34 | > 35 | > -- kotlin.text.* 36 | 37 | 一些附加包会根据平台来决定是否默认导入: 38 | 39 | > -- JVM: 40 | > 41 | > ---- java.lang.* 42 | > 43 | > ---- kotlin.jvm.* 44 | 45 | > -- JS: 46 | > 47 | > ---- kotlin.js.* 48 | 49 | ### Imports 50 | 除了模块中默认导入的包,每个文件都可以有导入自己需要的包。导入语法可以在 [grammar](Reference/Grammar.md) 查看。 51 | 52 | 我们可以导入一个单独的名字,比如下面这样: 53 | 54 | ```kotlin 55 | import foo.Bar // Bar 现在可以直接使用了 56 | ``` 57 | 58 | 或者范围内的所有可用的内容 (包,类,对象,等等): 59 | 60 | ```kotlin 61 | import foo.*/ /foo 中的所有都可以使用 62 | ``` 63 | 64 | 如果命名有冲突,我们可以使用 `as` 关键字局部重命名解决冲突 65 | 66 | ```kotlin 67 | import foo.Bar // Bar 可以使用 68 | import bar.Bar as bBar // bBar 代表 'bar.Bar' 69 | ``` 70 | 71 | import关键字不局限于导入类;您也可以使用它来导入其他声明: 72 | 73 | >-- 顶级函数与属性 74 | > 75 | >-- 在[对象声明](http://kotlinlang.org/docs/reference/object-declarations.html#object-declarations)中声明的函数和属性 76 | > 77 | >-- [枚举常量](http://kotlinlang.org/docs/reference/enum-classes.html) 78 | 79 | 与 Java 不同的是,Koting 没有[静态导入](https://docs.oracle.com/javase/8/docs/technotes/guides/language/static-import.html)的语法,所有的导入都是通过`import`关键字声明的。 80 | 81 | ### 顶级声明的可见性 82 | 如果最顶的声明标注为 private , 那么它是声明文件私有的 (参看[ Visibility Modifiers](http://kotlinlang.org/docs/reference/visibility-modifiers.html))。 83 | -------------------------------------------------------------------------------- /ClassesAndObjects/Data-Classes.md: -------------------------------------------------------------------------------- 1 | ## 数据类 2 | 我们经常创建一个只保存数据的类。在这样的类中一些函数只是机械的对它们持有的数据进行一些推导。在 kotlin 中这样的类称之为 data 类,用 `data` 标注: 3 | 4 | ```kotlin 5 | data class User(val name: String, val age: Int) 6 | ``` 7 | 8 | 编译器会自动根据主构造函数中声明的所有属性添加如下方法: 9 | 10 | > `equals()`/`hashCode` 函数 11 | 12 | > `toString` 格式是 "User(name=john, age=42)" 13 | 14 | > [compontN()functions] (http://kotlinlang.org/docs/reference/multi-declarations.html) 对应按声明顺序出现的所有属性 15 | 16 | > `copy()` 函数 17 | 18 | 如果在类中明确声明或从基类继承了这些方法,编译器不会自动生成。 19 | 20 | 为确保这些生成代码的一致性,并实现有意义的行为,数据类要满足下面的要求: 21 | 22 | 注意如果构造函数参数中没有 `val` 或者 `var` ,就不会在这些函数中出现; 23 | 24 | > 主构造函数应该至少有一个参数; 25 | 26 | > 主构造函数的所有参数必须标注为 `val` 或者 `var` ; 27 | 28 | > 数据类不能是 abstract,open,sealed,或者 inner ; 29 | 30 | > 数据类不能继承其它的类(但可以实现接口)。 31 | > 32 | > (在1.1之前)数据类只能实现接口。 33 | 34 | 从1.1开始数据类可以继承其它类(参考 [Sealed classes](http://kotlinlang.org/docs/reference/sealed-classes.html) ) 35 | 36 | 37 | 38 | 在 JVM 中如果构造函数是无参的,则所有的属性必须有默认的值,(参看[Constructors](http://kotlinlang.org/docs/reference/classes.html#constructors)); 39 | 40 | > data class User(val name: String = "", val age: Int = 0) 41 | 42 | ### 复制 43 | 我们经常会对一些属性做修改但想要其他部分不变。这就是 `copy()` 函数的由来。在上面的 User 类中,实现起来应该是这样: 44 | 45 | ```kotlin 46 | fun copy(name: String = this.name, age: Int = this.age) = User(name, age) 47 | ``` 48 | 49 | 有了 copy 我们就可以像下面这样写了: 50 | 51 | ```kotlin 52 | val jack = User(name = "jack", age = 1) 53 | val olderJack = jack.copy(age = 2) 54 | ``` 55 | 56 | ### 数据类和多重声明 57 | 组件函数允许数据类在[多重声明](http://kotlinlang.org/docs/reference/multi-declarations.html)中使用: 58 | 59 | ```kotlin 60 | val jane = User("jane", 35) 61 | val (name, age) = jane 62 | println("$name, $age years of age") //打印出 "Jane, 35 years of age" 63 | ``` 64 | 65 | ### 标准数据类 66 | 标准库提供了 `Pair` 和 `Triple`。在大多数情形中,命名数据类是更好的设计选择,因为这样代码可读性更强而且提供了有意义的名字和属性。 67 | -------------------------------------------------------------------------------- /MoreLanguageConstructs/Type-Checks-and-Casts.md: -------------------------------------------------------------------------------- 1 | ## 类型检查和转换 2 | ## is !is 表达式 3 | 我们可以在运行是通过上面俩个操作符检查一个对象是否是某个特定类: 4 | 5 | ```kotlin 6 | if (obj is String) { 7 | print(obj.length) 8 | } 9 | 10 | if (obj !is String) { // same as !(obj is String) 11 | print("Not a String") 12 | } 13 | else { 14 | print(obj.length) 15 | } 16 | ``` 17 | 18 | ### 智能转换 19 | 在很多情形中,需要使用非明确的类型,因为编译器会跟踪 `is` 检查静态变量,并在需要的时候自动插入安全转换: 20 | 21 | ```kotlin 22 | fun demo(x: Any) { 23 | if (x is String) { 24 | print(x.length) // x is automatically cast to String 25 | } 26 | } 27 | ``` 28 | 29 | 编译器足够智能如何转换是安全的,如果不安全将会返回: 30 | 31 | ```kotlin 32 | if (x !is String) return 33 | print(x.length) //x 自动转换为 String 34 | ``` 35 | 36 | 或者在 `||` `&&` 操作符的右边的值 37 | 38 | ```kotlin 39 | // x is automatically cast to string on the right-hand side of `||` 40 | if (x !is String || x.length == 0) return 41 | 42 | // x is automatically cast to string on the right-hand side of `&&` 43 | if (x is String && x.length > 0) 44 | print(x.length) // x is automatically cast to String 45 | ``` 46 | 47 | 这样的转换在 when 表达式和 whie 循环中也会发生 48 | 49 | ```kotlin 50 | when (x) { 51 | is Int -> print(x + 1) 52 | is String -> print(x.length + 1) 53 | is Array -> print(x.sum()) 54 | } 55 | ``` 56 | 57 | ### “不安全”的转换符和 58 | 如果转换是不被允许的那么转换符就会抛出一个异常。因此我们称之为不安全的。在kotlin 中 我们用前缀 as 操作符 59 | 60 | ```kotlin 61 | val x: String = y as String 62 | ``` 63 | 64 | 注意 null 不能被转换为 `String` 因为它不是 [`nullable`](http://kotlinlang.org/docs/reference/null-safety.html),也就是说如果 `y` 是空的,则上面的代码会抛出空异常。 65 | 66 | 为了 java 的转换语句匹配我们得像下面这样: 67 | 68 | ```kotlin 69 | val x: String?= y as String? 70 | ``` 71 | 72 | ### "安全"转换符 73 | 为了避免抛出异常,可以用 as? 这个安全转换符,这样失败就会返回 null : 74 | 75 | ```kotlin 76 | val x: String ?= y as? String 77 | ``` 78 | 79 | 不管 as? 右边的是不是一个非空 `String` 结果都会转换为可空的。 80 | -------------------------------------------------------------------------------- /Reference/API-Reference.md: -------------------------------------------------------------------------------- 1 | ## Kotlin 标准库 2 | Kotlin 标准库是一系列实现了常用模式的函数和类型,可以用在集合,文字,和文件上。 3 | 4 | ### 包 5 | 包名 | 信息 6 | --- |--- 7 | [kotlin](http://kotlinlang.org/api/latest/jvm/stdlib/kotlin/index.html) | 核心函数和类型,在所有支持平台均可用 8 | [kotlin.browser](http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.browser/index.html) | 访问并操作浏览器 DOM 的 API 9 | [kotlin.concurrent](http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.concurrent/index.html) | 并发编程的函数集合 10 | [kotlin.dom](http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.dom/index.html) | 用在 W3C DOM 的函数 11 | [kotlin.io](http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/index.html) | 用于文件和流的 IO API 12 | [kotlin.jvm](http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/index.html) | 专门用于 java 平台的函数和注解 13 | [kotlin.math](http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.math/index.html) | 数学方面的 API 14 | [kotlin.platform](http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.platform/index.html) | 用于自定义 Kotlin 编译器生成的代码,使其在目标平台上更好的交互 15 | [kotlin.properties](http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.properties/index.html) | 实现泛型和[泛型属性](http://kotlinlang.org/docs/reference/delegated-properties.html)的标准并帮助函数实现自定义泛型 16 | [kotlin.reflect](http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/index.html) | [Kotlin reflection](http://kotlinlang.org/docs/reference/reflection.html) 运行时 API 17 | [kotlin.reflect.jvm](http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect.jvm/index.html) | 用于 [Kotlin reflection](http://kotlinlang.org/docs/reference/reflection.html) 和 java 交互的运行时 API 18 | [kotlin.support](http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.support/index.html) | 19 | [kotlin.test](http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.test/index.html) | 写测试用例的 API 20 | [kotlin.text](http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/index.html) | 用于正则表达式和文字的函数 21 | [kotlin.util](http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.util/index.html) | 实用函数 22 | -------------------------------------------------------------------------------- /FAQ/Comparison2java.md: -------------------------------------------------------------------------------- 1 | ## 与 java 的对比 2 | ### 一些 java 的问题 3 | Kotlin 修复了 java 的一系列问题 4 | 5 | > Null 引用交给了[类型系统](http://kotlinlang.org/docs/reference/null-safety.html)控制 6 | 7 | > 没有 [raw](http://kotlinlang.org/docs/reference/java-interop.html) 类型 8 | 9 | > Arrays 在 kotlin 中是不变的 10 | 11 | > kotlin 有合适的[函数类型](http://kotlinlang.org/docs/reference/lambdas.html#function-types),作为 java SAM 转换的反对。(Kotlin has proper function types, as opposed to Java’s SAM-conversions) 12 | 13 | > [Use-site variance](Use-site variance without wildcards) without wildcards 14 | 15 | > Kotlin 不强制检查[异常](http://kotlinlang.org/docs/reference/exceptions.html) 16 | 17 | ### java 有的而 kotlin 没有 18 | > 异常检查 19 | 20 | > 原始类型不是类 21 | 22 | > 静态成员 23 | 24 | > 非私有成员 25 | 26 | > 通配符类型 27 | 28 | ### kotlin 有的而 java 没有 29 | >[字面函数](http://kotlinlang.org/docs/reference/lambdas.html)+[内联函数](http://kotlinlang.org/docs/reference/inline-functions.html)=高性能自定义控制结构 30 | 31 | >[扩展函数](http://kotlinlang.org/docs/reference/extensions.html) 32 | 33 | >[空安全](http://kotlinlang.org/docs/reference/null-safety.html) 34 | 35 | >[智能转换](http://kotlinlang.org/docs/reference/typecasts.html) 36 | 37 | >[String 模板](http://kotlinlang.org/docs/reference/basic-types.html#strings) 38 | 39 | >[性能](http://kotlinlang.org/docs/reference/properties.html) 40 | 41 | >[一级构造函数](http://kotlinlang.org/docs/reference/classes.html) 42 | 43 | >[First-class delegation](http://kotlinlang.org/docs/reference/delegation.html) 44 | 45 | >[变量和属性类型的类型接口](http://kotlinlang.org/docs/reference/basic-types.html) 46 | 47 | >[单例模式](http://kotlinlang.org/docs/reference/object-declarations.html) 48 | 49 | >[变量推断和类型预测](http://kotlinlang.org/docs/reference/generics.html) 50 | 51 | >[范围表达式](http://kotlinlang.org/docs/reference/ranges.html) 52 | 53 | >[运算符重载](http://kotlinlang.org/docs/reference/operator-overloading.html) 54 | 55 | >[伴随对象](http://kotlinlang.org/docs/reference/classes.html#companion-objects) 56 | 57 | -------------------------------------------------------------------------------- /MutiplatformProgramming/Platform-Specific-Declarations.md: -------------------------------------------------------------------------------- 1 | ## 平台相关声明 2 | 3 | **多平台项目是 Kotlin 1.2 和 1.3 中的实验性特性。本文档中描述的所有语言和工具功能都可能在将来的Kotlin版本中发生变更** 4 | 5 | Kotlin的多平台代码的主要功能之一是使通用代码依赖于特定于平台的声明的方式。 在其他语言中,通常可以通过在通用代码中构建一组接口并在特定于平台的模块中实现这些接口来实现。 但是,如果在其中一个平台上所需功已经有一个库,并且希望直接使用该库的API而无需额外的包装器,则这种方法并不适合。 另外,它要求将公共声明表示为接口,但并不能涵盖所有可能的情况。 6 | 7 | 作为替代,Kotlin提供了预期和实际声明的机制。 通过这种机制,公共模块可以定义期望的声明,而平台模块可以提供与期望的声明相对应的实际声明。 为了了解它是如何工作的,我们首先来看一个示例。 此代码是通用模块的一部分: 8 | 9 | 10 | ```Kotlin 11 | package org.jetbrains.foo 12 | 13 | expect class Foo(bar: String) { 14 | fun frob() 15 | } 16 | 17 | fun main() { 18 | Foo("Hello").frob() 19 | } 20 | ``` 21 | 22 | 这是对应 JVM 的模块: 23 | 24 | ```kotlin 25 | package org.jetbrains.foo 26 | 27 | actual class Foo actual constructor(val bar: String) { 28 | actual fun frob() { 29 | println("Frobbing the $bar") 30 | } 31 | } 32 | ``` 33 | 34 | 这里演示了一个重要的点: 35 | 36 | - 通用模块中的预期声明及其实际对应项始终具有完全相同的限定名称。 37 | 38 | - 预期的声明用 expect 关键字标记; 实际的声明中用 actual 关键字标记。 39 | 40 | - 与预期声明的任何部分匹配的所有实际声明都需要标记为 actual 。 41 | 42 | - 预期的声明绝不包含任何实现代码。 43 | 44 | 请注意,预期的声明不限于接口和接口成员。 在此示例中,期望的类具有构造函数,可以直接从通用代码创建。 还可以将 expect 修饰符应用于其他声明,包括顶级声明和注解: 45 | 46 | ```Kotlin 47 | 48 | // Common 49 | expect fun formatString(source: String, vararg args: Any): String 50 | 51 | expect annotation class Test 52 | 53 | // JVM 54 | actual fun formatString(source: String, vararg args: Any) = 55 | String.format(source, *args) 56 | 57 | actual typealias Test = org.junit.Test 58 | 59 | ``` 60 | 61 | 编译器确保每个期望的声明在实现相应公共模块的所有平台模块中都有实际的声明,并在缺少任何实际的声明时报告错误。 IDE提供了可帮助创建缺少实际声明的工具。 62 | 63 | 如果您要在通用代码中使用特定平台的库,同时为另一个平台提供自己的实现,则可以为现有类提供类型别名作为实际的声明: 64 | 65 | ```Kotlin 66 | expect class AtomicRef(value: V) { 67 | fun get(): V 68 | fun set(value: V) 69 | fun getAndSet(value: V): V 70 | fun compareAndSet(expect: V, update: V): Boolean 71 | } 72 | 73 | actual typealias AtomicRef = java.util.concurrent.atomic.AtomicReference 74 | ``` -------------------------------------------------------------------------------- /ClassesAndObjects/Delegation.md: -------------------------------------------------------------------------------- 1 | ## 委托 2 | 3 | ### 委托属性 4 | 5 | 委托属性在单独页面描述:[委托属性](DelegationProperties.md) 6 | 7 | ### 委托实现 8 | 9 | [委托模式](https://zh.wikipedia.org/wiki/%E5%A7%94%E6%89%98%E6%A8%A1%E5%BC%8F)已被证实是继承的一个很好的替代方案,而且 kotlin 原生支持该模式并不需要任何模板代码。`Derived` 类可以通过将其所有公有成员都委托给指定对象来实现一个接口 `Base`: 10 | 11 | ```kotlin 12 | interface Base { 13 | fun print() 14 | } 15 | 16 | class BaseImpl(val x: Int) : Base { 17 | override fun print() { print(x) } 18 | } 19 | 20 | class Derived(b: Base) : Base by b 21 | 22 | fun main() { 23 | val b = BaseImpl(10) 24 | Derived(b).print() 25 | } 26 | ``` 27 | 28 | `Derived` 的超类列表中的 *by*子句表示 `b` 将会存储在 `Derived` 内部,并且编译器将生成所有委托给 `b` 的 `Base` 的方法。 29 | 30 | ### 覆写由委托实现的接口成员 31 | 32 | 33 | 覆写将和你预期的一样工作:编译器会使用你 `override` 后的实现而不是委托对象中的实现。如果将 `override fun printMessage() { print("abc") }` 添加给 `Derived`,那么当调用 `printMessage` 时程序会输出“abc”而不是“10”: 34 | 35 | ```kotlin 36 | interface Base { 37 | fun printMessage() 38 | fun printMessageLine() 39 | } 40 | 41 | class BaseImpl(val x: Int) : Base { 42 | override fun printMessage() { print(x) } 43 | override fun printMessageLine() { println(x) } 44 | } 45 | 46 | class Derived(b: Base) : Base by b { 47 | override fun printMessage() { print("abc") } 48 | } 49 | 50 | fun main() { 51 | val b = BaseImpl(10) 52 | Derived(b).printMessage() 53 | Derived(b).printMessageLine() 54 | } 55 | ``` 56 | 57 | 但请注意,以这种方式覆写的成员不会从委托对象的成员调用,该成员只能访问其自己的接口成员实现: 58 | 59 | ```kotlin 60 | interface Base { 61 | val message: String 62 | fun print() 63 | } 64 | 65 | class BaseImpl(val x: Int) : Base { 66 | override val message = "BaseImpl: x = $x" 67 | override fun print() { println(message) } 68 | } 69 | 70 | class Derived(b: Base) : Base by b { 71 | // 在 b 的 `print` 实现中不会访问到这个属性 72 | override val message = "Message of Derived" 73 | } 74 | 75 | fun main() { 76 | val b = BaseImpl(10) 77 | val derived = Derived(b) 78 | derived.print() 79 | println(derived.message) 80 | } 81 | ``` -------------------------------------------------------------------------------- /Basics/Returns-and-Jumps.md: -------------------------------------------------------------------------------- 1 | ## 返回与跳转 2 | Kotlin 有三种结构跳转表达式: 3 | 4 | > -- return 默认情况下从最近的闭合函数或者[匿名函数](https://kotlinlang.org/docs/reference/lambdas.html#anonymous-functions)返回。 5 | > -- break 结束最近的闭合循环 6 | > -- continue 跳到最近的闭合循环的下一次循环 7 | 8 | 上述表达式都可以作为更大的表达式的一部分: 9 | 10 | ```kotlin 11 | val s = person.name ?: return 12 | ``` 13 | 14 | 这些表达式的类型是 [Nothing type](http://kotlinlang.org/docs/reference/exceptions.html#the-nothing-type) 15 | 16 | ### break 和 continue 标签 17 | Kotlin 中任意表达式可以添加标签。标签通过 @ 结尾来标识,比如:`abc@`,`fooBar@` 都是有效的(参看[语法](http://kotlinlang.org/docs/reference/grammar.html#label))。使用标签表达式,只需像这样: 18 | 19 | ```kotlin 20 | loop@ for (i in 1..100){ 21 | // ... 22 | } 23 | ``` 24 | 25 | 我们可以用标签实现 break 或者 continue 的快速跳转: 26 | 27 | ```kotlin 28 | loop@ for (i in 1..100) { 29 | for (j in i..100) { 30 | if (...) 31 | break@loop 32 | } 33 | } 34 | ``` 35 | 36 | break 是跳转标签后面的表达式,continue 是跳转到循环的下一次迭代。 37 | 38 | ### 返回到标签 39 | 40 | 41 | 在字面函数,局部函数,以及对象表达式中,函数在Kotlin中是可以嵌套的。合法的return 允许我们返回到外层函数。最重要的使用场景就是从lambda表达式中返回,还记得我们之前的写法吗: 42 | 43 | ```kotlin 44 | fun foo() { 45 | ints.forEach { 46 | if (it == 0) return 47 | print(it) 48 | } 49 | } 50 | ``` 51 | 52 | return 表达式返回到最近的闭合函数,比如 `foo` (注意这样非局部返回仅仅可以在[内联函数](http://kotlinlang.org/docs/reference/inline-functions.html)中使用)。如果我们需要从一个字面函数返回可以使用标签修饰 return : 53 | 54 | ```kotlin 55 | fun foo() { 56 | ints.forEach lit@ { 57 | if (it ==0) return@lit 58 | print(it) 59 | } 60 | } 61 | ``` 62 | 63 | 现在它仅仅从字面函数中返回。经常用一种更方便的含蓄的标签:比如用和传入的 lambda 表达式名字相同的标签。 64 | 65 | ```kotlin 66 | fun foo() { 67 | ints.forEach { 68 | if (it == 0) return@forEach 69 | print(it) 70 | } 71 | } 72 | ``` 73 | 74 | 另外,我们可以用函数表达式替代匿名函数。在函数表达式中使用 return 语句可以从函数表达式中返回。 75 | 76 | ```kotlin 77 | fun foo() { 78 | ints.forEach(fun(value: Int){ 79 | if (value == 0) return 80 | print(value) 81 | }) 82 | } 83 | ``` 84 | 85 | 86 | 当返回一个值时,解析器给了一个参考,比如(原文When returning a value, the parser gives preference to the qualified return, i.e.): 87 | 88 | ```kotlin 89 | return@a 1 90 | ``` 91 | 92 | 表示 “在标签 `@a` 返回 `1` ” 而不是返回一个标签表达式 `(@a 1)` 93 | 94 | 命名函数自动定义标签: 95 | 96 | ```kotlin 97 | foo outer() { 98 | foo inner() { 99 | return@outer 100 | } 101 | } 102 | ``` 103 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | 2 | * [准备开始](GettingStarted/README.md) 完成 3 | * [基本语法](GettingStarted/Basic-Syntax.md) 4 | * [习惯用语](GettingStarted/Idioms.md) 5 | * [编码风格](GettingStarted/Coding-Conventions.md) 6 | 7 | * [基础](Basics/README.md) 完成 8 | * [基本类型](Basics/Basic-Types.md) 9 | * [包](Basics/Packages.md) 10 | * [控制流](Basics/Control-Flow.md) 11 | * [返回与跳转](Basics/Returns-and-Jumps.md) 12 | 13 | * [类和对象](ClassesAndObjects/README.md) 14 | * [类和继承](ClassesAndObjects/Classes-and-Inheritance.md) 完成 15 | * [属性和字段](ClassesAndObjects/Properties-and-Fields.md) 完成 16 | * [接口](ClassesAndObjects/Interfaces.md) 完成 17 | * [可见性修饰词](ClassesAndObjects/Visibility-Modifiers.md) 完成 18 | * [扩展](ClassesAndObjects/Extensions.md) 完成 19 | * [数据对象](ClassesAndObjects/Data-Classes.md) 完成 20 | * [泛型](ClassesAndObjects/Generics.md) 21 | * [嵌套类](ClassesAndObjects/NestedClasses.md) 完成 22 | * [枚举类](ClassesAndObjects/EnumClasses.md) 完成 23 | * [对象表达式和声明](ClassesAndObjects/ObjectExpressicAndDeclarations.md) 完成 24 | * [代理模式](ClassesAndObjects/Delegation.md) 完成 25 | * [代理属性](ClassesAndObjects/DelegationProperties.md) 完成 26 | 27 | * [函数和lambda表达式](FunctionsAndLambdas/README.md) 28 | * [函数](FunctionsAndLambdas/Functions.md) 完成 29 | * [高阶函数和lambda表达式](FunctionsAndLambdas/Higher-OrderFunctionsAndLambdas.md) 30 | * [内联函数](FunctionsAndLambdas/InlineFunctions.md) 31 | * [协程](FunctionsAndLambdas/Cocroutines.md) 32 | 33 | * [其它](Other/README.md) 34 | * [多重申明](Other/Multi-Declarations.md) 35 | * [Ranges](Other/Ranges.md) 36 | * [类型检查和自动转换](Other/Type-Checks-and-Casts.md) 37 | * [This表达式](Other/This-Expression.md) 38 | * [等式](Other/Equality.md) 39 | * [运算符重载](Other/Opetator-overloading.md) 40 | * [空安全](Other/Null-Safety.md) 41 | * [异常](Other/Exceptions.md) 42 | * [注解](Other/Annotations.md) 43 | * [反射](Other/Reflection.md) 44 | * [动态类型](Other/Dynamic-Type.md) 45 | 46 | * [参考](Reference/README.md) 47 | 48 | * [互用性](Interop/README.md) 49 | * [动态类型](Interop/Java-Interop.md) 50 | 51 | * [工具](Tools/README.md) 52 | * [Kotlin代码文档](Tools/Documenting-Kotlin-Code.md) 53 | * [使用Maven](Tools/Using-Maven.md) 54 | * [使用Ant](Tools/Using-Ant.md) 55 | * [使用Griffon](Tools/Using-Griffon.md) 56 | * [使用Gradle](Tools/Using-Gradle.md) 57 | 58 | * [FAQ](FAQ/README.md) 59 | * [与java对比](FAQ/Comparison2java.md) 60 | * [与Scala对比](FAQ/Comparison2Scala.md) 61 | -------------------------------------------------------------------------------- /MoreLanguageConstructs/Null-Safety.md: -------------------------------------------------------------------------------- 1 | ## 空安全 2 | ### 可空类型和非空类型 3 | Kotlin 类型系统致力于消灭空引用。 4 | 5 | 在许多语言中都存在的一个大陷阱包括 java ,就是访问一个空引用的成员,结果会有空引用异常。在 java 中这就是 `NullPointerException` 或者叫 NPE 6 | 7 | Kotlin 类型系统致力与消灭 `NullPointerException` 异常。唯一可能引起 NPE 异常的可能是: 8 | 9 | >明确调用 `throw NullPointerException()` 10 | >外部 java 代码引起 11 | >一些前后矛盾的初始化(在构造函数中没初始化的成员在其它地方使用) 12 | 13 | 在 Kotlin 类型系统中可以为空和不可为空的引用是不同的。比如,普通的 `String` 类型的变量不能为空: 14 | 15 | ```kotlin 16 | var a: String ="abc" 17 | a = null //编译错误  18 | ``` 19 | 20 | 允许为空,我们必须把它声明为可空的变量: 21 | 22 | ```kotlin 23 | var b: String? = "abc" 24 | b = null 25 | ``` 26 | 27 | 现在你可以调用 a 的方法,而不用担心 NPE 异常了: 28 | 29 | ```kotlin 30 | val l = a.length() 31 | ``` 32 | 33 | 但如果你想使用 b 调用同样的方法就有可能报错了: 34 | 35 | ```kotlin 36 | val l = b.length() //错误:b 不可为空 37 | ``` 38 | 39 | 但我们任然想要调用方法,有些办法可以解决。 40 | 41 | ### 在条件中检查 null 42 | 首先,你可以检查 `b` 是否为空,并且分开处理下面选项: 43 | 44 | ```kotlin 45 | val l = if (b != null) b.length() else -1 46 | ``` 47 | 48 | 编译器会跟踪你检查的信息并允许在 if 中调用 length()。更复杂的条件也是可以的: 49 | 50 | ```kotlin 51 | if (b != null && b.length() >0) 52 | print("Stirng of length ${b.length}") 53 | else 54 | print("Empty string") 55 | ``` 56 | 57 | 注意只有在 b 是不可变时才可以 58 | 59 | ### 安全调用 60 | 第二个选择就是使用安全操作符,`?.`: 61 | 62 | ```kotlin 63 | b?.length() 64 | ``` 65 | 66 | 如果 b 不为空则返回长度,否则返回空。这个表达式的的类型是 Int? 67 | 68 | 安全调用在链式调用是是很有用的。比如,如果 Bob 是一个雇员可能分配部门(也可能不分配),如果我们想获取 Bob 的部门名作为名字的前缀,就可以这样做: 69 | 70 | ```kotlin 71 | bob?.department?.head?.name 72 | ``` 73 | 这样的调用链在任何一个属性为空都会返回空。 74 | 75 | ### Elvis 操作符 76 | 当我们有一个 r 的可空引用时,我们可以说如果 `r` 不空则使用它,否则使用使用非空的 x : 77 | 78 | ```kotlin 79 | val l: Int = if (b != null) b.length() else -1 80 | ``` 81 | 尽管使用 if 表达式我们也可以使用 Elvis 操作符,`?:` 82 | 83 | ```kotlin 84 | val l = b.length()?: -1 85 | ``` 86 | 如果 ?: 左边表达式不为空则返回,否则返回右边的表达式。注意右边的表带式只有在左边表达式为空是才会执行 87 | 88 | 注意在 Kotlin 中 throw return 是表达式,所以它们也可以在 Elvis 操作符右边。这是非常有用的,比如检查函数参数是否为空; 89 | 90 | ```kotlin 91 | fun foo(node: Node): String? { 92 | val parent = node.getParent() ?: return null 93 | val name = node.getName() ?: throw IllegalArgumentException("name expected") 94 | 95 | //... 96 | } 97 | ``` 98 | ### !! 操作符 99 | 第三个选择是 NPE-lovers。我们可以用 b!! ,这会返回一个非空的 b 或者抛出一个 b 为空的 NPE 100 | 101 | ```kotlin 102 | val l = b !!.length() 103 | ``` 104 | ### 安全转换 105 | 普通的转换可能产生 `ClassCastException` 异常。另一个选择就是使用安全转换,如果不成功就返回空: 106 | 107 | ```kotlin 108 | val aInt: Int? = a as? Int 109 | ``` 110 | -------------------------------------------------------------------------------- /ClassesAndObjects/Visibility-Modifiers.md: -------------------------------------------------------------------------------- 1 | ## 可见性修饰词 2 | 类,对象,接口,构造函数,属性以及它们的 setter 方法都可以有可见性修饰词。( getter与对应的属性拥有相同的可见性)。在 Kotlin 中有四种修饰词:`private`,`protected`,`internal`,以及 `public` 。默认的修饰符是 `public`。 3 | 下面将解释不同类型的声明作用域。 4 | 5 | ### 包 6 | 函数,属性和类,对象和接口可以在 "top-level" 声明,即可以直接属于包: 7 | 8 | ```kotlin 9 | // 文件名: example.kt 10 | package foo 11 | 12 | fun baz() {} 13 | class bar {} 14 | ``` 15 | 16 | >-- 如果没有指明任何可见性修饰词,默认使用 `public` ,这意味着你的声明在任何地方都可见; 17 | 18 | >-- 如果你声明为 `private` ,则只在包含声明的文件中可见; 19 | 20 | >-- 如果用 `internal` 声明,则在同一[模块](http://kotlinlang.org/docs/reference/visibility-modifiers.html#modules)中的任何地方可见; 21 | 22 | >-- `protected` 在 "top-level" 中不可以使用 23 | 24 | 例子: 25 | 26 | ```kotlin 27 | // 文件名: example.kt 28 | package foo 29 | 30 | private fun foo() {} // 在example.kt可见 31 | 32 | public var bar: Int = 5 // 属性在认可地方都可见 33 | private set // setter仅在example.kt中可见 34 | 35 | internal val baz = 6 // 在同一module中可见 36 | ``` 37 | 38 | ### 类和接口 39 | 当在类中声明成员时: 40 | 41 | > `private` 只在该类(以及它的成员)中可见 42 | 43 | > `protected` 和 `private` 一样但在子类中也可见 44 | 45 | > `internal` 在本模块的所有可以访问到声明区域的均可以访问该类的所有 `internal` 成员 ( internal — any client inside this module who sees the declaring class sees its internal members;) 46 | 47 | > `public` 任何地方可见 (public — any client who sees the declaring class sees its public members.) 48 | 49 | java 使用者注意:外部类不可以访问内部类的 private 成员。 50 | 51 | 如果你复写了一个`protected`成员并且没有指定可见性,那么该复写的成员具有`protected`可见性 52 | 53 | 例子: 54 | 55 | ```kotlin 56 | open class Outer { 57 | private val a = 1 58 | protected open val b = 2 59 | internal val c = 3 60 | val d = 4 // 默认是public 61 | 62 | protected class Nested { 63 | public val e: Int = 5 64 | } 65 | } 66 | 67 | class Subclass : Outer() { 68 | // a不可见 69 | // b,c和d是可见的 70 | // 内部类和e都是可见的 71 | 72 | override val b = 5 // 'b' i具有protected可见性 73 | } 74 | 75 | class Unrelated(o: Outer) { 76 | // o.a, o.b 不可见 77 | // o.c and o.d 可见 (same module) 78 | // Outer.Nested 不可见, and Nested::e 也不可见 79 | } 80 | ``` 81 | 82 | ### 构造函数 83 | 通过下面的语法来指定主构造函数(必须显示的使用 constructor 关键字)的可见性: 84 | 85 | ```kotlin 86 | class C private constructor(a: Int) { ... } 87 | ``` 88 | 89 | 这里构造函数是 private 。所有的构造函数默认是 `public` ,实际上只要类是可见的它们就是可见的 90 | (注意 `internal` 类型的类中的 public 属性只能在同一个模块内才可以访问) 91 | 92 | ### 局部声明 93 | 局部变量,函数和类是不允许使用修饰词的 94 | 95 | ### 模块 96 | `internal` 修饰符是指成员的可见性是只在同一个模块中才可见的。模块在 Kotlin 中就是一系列的 Kotlin 文件编译在一起: 97 | 98 | — an IntelliJ IDEA module; 99 | 100 | — a Maven or Gradle project; 101 | 102 | — a set of files compiled with one invocation of the Ant task. 103 | -------------------------------------------------------------------------------- /MoreLanguageConstructs/Annotations.md: -------------------------------------------------------------------------------- 1 | ## 注解 2 | ### 注解声明 3 | 注解是一种将元数据附加到代码中的方法。声明注解需要在类前面使用 annotation 关键字: 4 | 5 | ```kotlin 6 | annotation class fancy 7 | ``` 8 | 9 | ### 用法 10 | ```kotlin 11 | @fancy class Foo { 12 | @fancy fun baz(@fancy foo: Int): Int { 13 | return (@fancy 1) 14 | } 15 | } 16 | ``` 17 | 18 | 在多数情形中 @ 标识是可选的。只有在注解表达式或本地声明中才必须: 19 | 20 | ```kotlin 21 | fancy class Foo { 22 | fancy fun baz(fancy foo: Int): Int { 23 | @fancy fun bar() { ... } 24 | return (@fancy 1) 25 | } 26 | } 27 | ``` 28 | 29 | 如果要给构造函数注解,就需要在构造函数声明时添加 constructor 关键字,并且需要在前面添加注解: 30 | 31 | ```kotlin 32 | class Foo @inject constructor (dependency: MyDependency) 33 | //... 34 | ``` 35 | 36 | 也可以注解属性访问者: 37 | 38 | ```kotlin 39 | class Foo { 40 | var x: MyDependency?=null 41 | @inject set 42 | } 43 | ``` 44 | 45 | ### 构造函数 46 | 注解可以有带参数的构造函数。 47 | 48 | ```kotlin 49 | annotation class special(val why: String) 50 | special("example") class Foo {} 51 | ``` 52 | 53 | ### Lambdas 54 | 注解也可以用在 Lambda 中。这将会应用到 lambda 生成的 invoke() 方法。这对 [Quasar](http://www.paralleluniverse.co/quasar/)框架很有用,在这个框架中注解被用来并发控制 55 | 56 | ```kotlin 57 | annotation class Suspendable 58 | val f = @Suspendable { Fiber.sleep(10) } 59 | ``` 60 | 61 | ### java 注解 62 | java 注解在 kotlin 中是完全兼容的: 63 | 64 | ```kotlin 65 | import org.junit.Test 66 | import org.junit.Assert.* 67 | 68 | class Tests { 69 | Test fun simple() { 70 | assertEquals(42, getTheAnswer()) 71 | } 72 | } 73 | ``` 74 | 75 | java 注解也可以通过在导入是重命名实现像修改者那样: 76 | 77 | ```kotlin 78 | import org.junit.Test as test 79 | 80 | class Tests { 81 | test fun simple() { 82 | ... 83 | } 84 | } 85 | ``` 86 | 87 | 因为 java 中注解参数顺序是没定义的,你不能通过传入参数的方法调用普通函数。相反,你需要使用命名参数语法: 88 | 89 | ```java 90 | //Java 91 | public @interface Ann { 92 | int intValue(); 93 | String stringValue(0; 94 | } 95 | 96 | //kotlin 97 | Ann(intValue = 1, stringValue = "abc") class C 98 | ``` 99 | 100 | 像 java 中那样,值参数是特殊的情形;它的值可以不用明确的名字。 101 | 102 | ```java 103 | public @interface AnnWithValue { 104 | String value(); 105 | } 106 | 107 | //kotlin 108 | AnnWithValue("abc") class C 109 | ``` 110 | 111 | 如果java 中的 value 参数有数组类型,则在 kotlin 中变成 vararg 参数: 112 | 113 | ```kotlin 114 | // Java 115 | public @interface AnnWithArrayValue { 116 | String[] value(); 117 | } 118 | // Kotlin 119 | AnnWithArrayValue("abc", "foo", "bar") class C 120 | 121 | ``` 122 | 123 | 如果你需要明确一个类作为一个注解参数,使用 Kotlin 类[KClass](http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/-k-class/index.html)。Kotlin 编译器会自动把它转为 java 类,因此 java 代码就可以正常看到注解和参数了。 124 | 125 | ```kotlin 126 | import kotlin.reflect.KClass 127 | 128 | annotation class Ann(val arg1: KClass<*>, val arg2: KClass) 129 | 130 | Ann(String::class, Int::class) class MyClass 131 | ``` 132 | 133 | 注解实例的值在 kotlin 代码中是暴露属性。 134 | 135 | ```kotlin 136 | // Java 137 | public @interface Ann { 138 | int value(); 139 | } 140 | // Kotlin 141 | fun foo(ann: Ann) { 142 | val i = ann.value 143 | } 144 | ``` 145 | -------------------------------------------------------------------------------- /MoreLanguageConstructs/DestructuringDeclarations.md: -------------------------------------------------------------------------------- 1 | ## 解构声明 2 | 3 | 有时将对象解构为多个变量会很方便,例如: 4 | ```Kotlin 5 | val (name, age) = person 6 | ``` 7 | 8 | 此语法称为解构声明。解构声明一次创建多个变量。我们已经声明了两个新变量:name和age,并且可以独立使用它们: 9 | 10 | ```Kotlin 11 | println(name) 12 | println(age) 13 | ``` 14 | 解构声明被编译为以下代码: 15 | 16 | ```Kotlin 17 | val name = person.component1() 18 | val age = person.component2() 19 | ``` 20 | 21 | component1()和component2()函数是Kotlin中广泛使用的约定原理的另一个示例(请参阅+和*等运算符,for循环等)。只要可以在其上调用所需数量的 component 函数,任何内容都可以在解构声明的右侧。当然了可以有component3()和component4()等等。 22 | 23 | 请注意,componentN()函数需要使用`operator`关键字进行标记,以允许在解构声明中使用它们。 24 | 25 | 解构声明也可以在for循环中使用,比如: 26 | 27 | ```Kotlin 28 | for(((a,b)in collection){...} 29 | ``` 30 | 31 | 变量a和b获得在集合的元素上调用的component1()和component2()返回的值。 32 | 33 | ## 示例:函数返回两个值 34 | 35 | 假设我们需要从一个函数返回两个值。例如,结果对象和某种状态。在Kotlin中执行此操作的一种简便方法是声明一个[数据类](https://kotlinlang.org/docs/reference/data-classes.html)并返回其实例: 36 | 37 | ```Kotlin 38 | data class Result(val result: Int, val status: Status) 39 | fun function(...): Result { 40 | // computations 41 | 42 | return Result(result, status) 43 | } 44 | 45 | // Now, to use this function: 46 | val (result, status) = function(...) 47 | ``` 48 | 49 | 由于数据类会自动声明componentN()函数,因此在这里可以执行解构声明。 50 | 51 | 注意:我们也可以使用标准类`Pair`并让 function() 返回Pair ,但是通常最好对数据进行正确命名。 52 | 53 | ## 示例:解构声明和 map 54 | 55 | 遍历 map 最优的方法可能是这样: 56 | 57 | ```Kotlin 58 | for ((key, value) in map) { 59 | // do something with the key and the value 60 | } 61 | ``` 62 | 63 | 为了使其工作,我们应该 64 | 65 | - 提供 iterator()函数,将 map 呈现为值序列; 66 | - 提供函数 component1()和component2()将每个元素成对呈现。 67 | 68 | 实际上,标准库提供了以下扩展: 69 | 70 | ```Kotlin 71 | operator fun Map.iterator(): Iterator> = entrySet().iterator() 72 | operator fun Map.Entry.component1() = getKey() 73 | operator fun Map.Entry.component2() = getValue() 74 | ``` 75 | 76 | 因此,你可以在for循环中对 map 使用解构声明(当然也包括其它集合数据类)。 77 | 78 | ## 下划线表示未使用的变量(从1.1开始) 79 | 80 | 如果在解构声明中不需要某个变量,则可以使用下划线代替其名称: 81 | 82 | ```Kotlin 83 | val (_, status) = getResult() 84 | ``` 85 | 86 | 这种方式跳过的组件将不会调用 `componentN()` 运算符。 87 | 88 | ## Lambda 中的解构(自1.1开始) 89 | 90 | 可以对lambda参数使用解构声明语法。如果lambda具有 `Pair` 类型的参数(或Map.Entry或具有componentN函数的任何其他类型),则可以通过在括号中引入几个新参数来代替它: 91 | 92 | ```Kotlin 93 | map.mapValues { entry -> "${entry.value}!" } 94 | map.mapValues { (key, value) -> "$value!" } 95 | ``` 96 | 97 | 注意声明两个参数和声明一个解构对而不是一个参数之间的区别: 98 | 99 | ```Kotlin 100 | { a -> ... } // one parameter 101 | { a, b -> ... } // two parameters 102 | { (a, b) -> ... } // a destructured pair 103 | { (a, b), c -> ... } // a destructured pair and another parameter 104 | ``` 105 | 106 | 如果未使用已分解结构参数的组件,则可以将其替换为下划线,以避免重新为其取名: 107 | 108 | ```Kotlin 109 | map.mapValues { (_, value) -> "$value!" } 110 | ``` 111 | 112 | 可以为整个已解构参数指定类型或为特定组件指定类型: 113 | 114 | ```Kotlin 115 | map.mapValues { (_, value): Map.Entry -> "$value!" } 116 | 117 | map.mapValues { (_, value: String) -> "$value!" } 118 | ``` 119 | -------------------------------------------------------------------------------- /MoreLanguageConstructs/Opetator-overloading.md: -------------------------------------------------------------------------------- 1 | ## 运算符重载 2 | Kotlin 允许我们实现一些我们自定义类型的运算符实现。这些运算符有固定的表示,和固定的优先级。为实现这样的运算符,我们提供了固定名字的数字函数和扩展函数,比如二元运算符的左值和一元运算符的参数类型。 3 | 4 | ### 转换 5 | 这里我们描述了一些常用运算符的重载 6 | 7 | #### 一元运算符 8 | **表达式**|**转换** 9 | ---|--- 10 | +a|a.plus() 11 | -a|a.minus() 12 | !a|a.not() 13 | 14 | 这张表解释了当编译器运行时,比如,表达式 `+a` ,是这样运行的: 15 | 16 | >决定 a 的类型,假设是 T 17 | >寻找接收者是 T 的无参函数 `plus()` ,比如数字函数或者扩展函数 18 | >如果这样的函数缺失或不明确,则返回错误。 19 | >如果函数是当前函数或返回类型是 `R` 则表达式 `+a` 是 `R` 类型。 20 | 21 | 注意这些操作符和其它的一样,都被优化为基本类型并且不会产生多余的开销。 22 | 23 | **表达式**|**转换** 24 | ---|--- 25 | a++| a.inc() + see below 26 | a--| a.dec() + see below 27 | 28 | 这些操作符允许修改接收者和返回类型。 29 | 30 | ```kotlin 31 | inc()/dec() shouldn’t mutate the receiver object. 32 | By “changing the receiver” we mean the receiver-variable, not the receiver object. 33 | ``` 34 | 35 | 编译器是这样解决有后缀的操作符的比如 `a++` : 36 | 37 | >决定 a 的类型,假设是 T 38 | >寻找无参函数 `inc()` ,作用在接收者T 39 | >如果返回类型是 R ,则必须是 T 的子类 40 | 41 | 计算表达式的效果是: 42 | 43 | >把 a 的初始值存储在 a0 中 44 | >把 a.inc() 的结果作用在 a 上 45 | >把 a0 作为表达式的返回值 46 | 47 | a-- 的步骤也是一样的 48 | 49 | ++a --a 的解决方式也是一样的 50 | 51 | #### 二元操作符 52 | **表达式**|**转换** 53 | ---|--- 54 | a + b | a.plus(b) 55 | a - b | a.minus(b) 56 | a * b | a.times(b) 57 | a / b | a.div(b) 58 | a % b | a.mod(b) 59 | a..b | a.rangeTo(b) 60 | 61 | 编译器只是解决了该表中翻译为列的表达式 62 | 63 | **表达式**|**转换** 64 | ---|--- 65 | a in b | b.contains(a) 66 | a !in b | !b.contains(a) 67 | 68 | in 和 !in 的产生步骤是一样的,但参数顺序是相反的。 69 | 70 | **标志** | **转换** 71 | ---|--- 72 | a[i] | a.get(i) 73 | a[i, j] | a.get(i, j) 74 | a[i_1, ..., i_n] | a.get(i_1, ... , i_n) 75 | a[i] = b | a.set(i, b) 76 | a[i,j] =b | a.set(i, j, b) 77 | a[i_1, ... , i_n] = b | a.set(i_1,... ,o_n,b) 78 | 79 | 方括号被转换为 get set 函数 80 | 81 | **标志** | **转换** 82 | ---|--- 83 | a(i) | a.invoke(i) 84 | a(i, j) | a.invoke(i, j) 85 | a(i_1, ... , i_n) | a.invoke(i_1, ..., i_n) 86 | 87 | 括号被转换为带有正确参数的 invoke 参数 88 | 89 | **表达式** | **转换** 90 | ---|--- 91 | a += b |a.plusAssign(b) 92 | a -= b |a.minusAssign(b) 93 | a *= b |a.timesAssign(b) 94 | a /= b |a.divAssign(b) 95 | a %= b |a.modAssign(b) 96 | 97 | 在分配 a+= b时编译器是下面这样实现的: 98 | 99 | > 右边列的函数是否可用 100 | > 对应的二元函数(比如 plus() )是否也可用,不可用在报告错误 101 | > 确定它的返回值是 `Unit` 否则报告错误 102 | > 生成 `a.plusAssign(b)` 103 | > 否则试着生成 a=a+b 代码 104 | 105 | Note: assignments are NOT expressions in Kotlin. 106 | 107 | **表达式** | **转换** 108 | ---|--- 109 | a == b |a?.equals(b) ?: b.identityEquals(null) 110 | a != b |!(a?.equals(b) ?: b.identityEquals(null)) 111 | 112 | 注意 === !== 是不允许重载的 113 | 114 | == 操作符有俩点特别: 115 | 116 | > 它被翻译成一个复杂的表达式,用于筛选空值,而且 null == null 是真 117 | 118 | > 它需要带有特定签名的函数,而不仅仅是特定名称的函数,下面这样: 119 | 120 | ```kotlin 121 | fun equals(other: Any?): Boolean 122 | ``` 123 | 124 | 或者用相同的参数列表和返回类型的扩展功能 125 | 126 | **标志** | **转换** 127 | ---|--- 128 | 129 | a > b |a.compareTo(b) > 0 130 | a < b |a.compareTo(b) < 0 131 | a >= b |a.compareTo(b) >= 0 132 | a <= b |a.compareTo(b) <= 0 133 | 134 | 所有的比较都转换为 `compareTo` 的调用,这个函数需要返回 `Int` 值 135 | 136 | ### 命名函数的中缀调用 137 | 我们可以通过 [中缀函数的调用](http://kotlinlang.org/docs/reference/functions.html#infix-notation) 来模拟自定义中缀操作符 138 | -------------------------------------------------------------------------------- /Overview/Mutiplatform.md: -------------------------------------------------------------------------------- 1 | ## 多平台编程 2 | 3 | **多平台项目是 Kotlin 1.2 和 1.3 中的实验性特性。本文档中描述的所有语言和工具功能都可能在将来的Kotlin版本中发生变更** 4 | 5 | 使 Kotlin 可以在所有平台上使用是我们的目标,但我们认为这是一个更为重要的目标的前提:在平台之间共享代码。 借助对JVM,Android,JavaScript,iOS,Linux,Windows,Mac甚至STM32等嵌入式系统的支持,Kotlin可以处理现代应用程序的任何和所有组件。这为代码和知识经验重用带来了极大便利, 节省了大量用于多平台而需要两次或多次实现的时间,从而可以把时间用在更具挑战性的任务上。 6 | 7 | ### 它是怎么工作的 8 | 9 | 首先,多平台不是编译代码到所有平台。这种模型有着显而易见的缺点,我们都知道现代应用往往需要调用它们运行所在平台的独有特性。Kotlin不会限制你使用世界上所有API的共同子集。每个组件都可以尽可能的共享代码,同时可以借助语言提供的 [预期/实际(expect/actual) 机制]{https://kotlinlang.org/docs/reference/platform-specific-declarations.html}调用平台相关代码。 10 | 11 | 下面是一段示例代码,一个简单的日志记录框架,可以共享和互相调用公共逻辑和平台逻辑。公共代码如下: 12 | 13 | ```Kotlin 14 | 15 | enum class LogLevel { 16 | DEBUG, WARN, ERROR 17 | } 18 | 19 | // 预期用于平台特有的API 20 | internal expect fun writeLogMessage(message: String, logLevel: LogLevel) 21 | 22 | // 预期通用代码API 23 | fun logDebug(message: String) = writeLogMessage(message, LogLevel.DEBUG) 24 | fun logWarn(message: String) = writeLogMessage(message, LogLevel.WARN) 25 | fun logError(message: String) = writeLogMessage(message, LogLevel.ERROR) 26 | 27 | ``` 28 | 29 | 30 | 它预期目标为 writeLogMessage 提供特定于平台的实现,并且通用代码现在可以使用此声明,而无需考虑如何实现。 31 | 32 | 在JVM上,可以提供一种将日志写入标准输出的实现: 33 | 34 | ```Kotlin 35 | internal actual fun writeLogMessage(message: String, logLevel: LogLevel) { 36 | println("[$logLevel]: $message") 37 | } 38 | ``` 39 | 40 | 在 JavaScript 中,有着完全不同的API集合,所以也可以这样实现: 41 | 42 | ```Kotlin 43 | internal actual fun writeLogMessage(message: String, logLevel: LogLevel) { 44 | when (logLevel) { 45 | LogLevel.DEBUG -> console.log(message) 46 | LogLevel.WARN -> console.warn(message) 47 | LogLevel.ERROR -> console.error(message) 48 | } 49 | } 50 | ``` 51 | 52 | 在 1.3 中,我们重新设计了整个多平台模型。我们用于描述多平台Gradle项目的新DSL更加灵活,并将持续致力于提供简单明了的配置。 53 | 54 | ### 多平台库 55 | 56 | 通用代码可以依靠一组涵盖日常任务(例如HTTP,序列化和管理协程)的库。 此外,所有平台上都提供了广泛的标准库。 57 | 58 | 你也可以编写自己的库,以提供通用的API,并在每个平台上以不同的方式实现它。 59 | 60 | ### 使用场景 61 | 62 | #### Android - iOS 63 | 64 | 在移动平台中共享代码是 Kotlin 多平台的主要使用场景,如今可以在 Android 和 iOS 中共享业务逻辑,网络链接等部分的代码。 65 | 66 | 参看: 67 | 68 | [移动多平台的特性,场景学习以及示例代码](https://www.jetbrains.com/lp/mobilecrossplatform/?_ga=2.87157787.2065084095.1578977891-351818486.1577339287) 69 | 70 | [新建一个移动多平台项目](https://play.kotlinlang.org/hands-on/Targeting%20iOS%20and%20Android%20with%20Kotlin%20Multiplatform/01_Introduction) 71 | 72 | #### Client - Server 73 | 74 | 另一个场景就是在浏览器中共享客户端和服务端共享代码逻辑,Kotlin 多平台也支持这种场景。 75 | 76 | [Ktor框架](https://ktor.io/?_ga=2.20579131.2065084095.1578977891-351818486.1577339287) 适合在连接的系统中构建异步服务器和客户端。 77 | 78 | ### 怎么开始 79 | 80 | 刚开始学 Kotlin ? 可以去[开始](https://kotlinlang.org/docs/reference/basic-syntax.html)页面。 81 | 82 | 建议的文档页面: 83 | 84 | - [设置多平台项目](https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html#setting-up-a-multiplatform-project) 85 | - [平台相关声明](https://kotlinlang.org/docs/reference/platform-specific-declarations.html) 86 | 87 | 推荐教程: 88 | 89 | - [多平台 Kotlin 库](https://kotlinlang.org/docs/tutorials/mpp/multiplatform-library.html) 90 | - [多平台项目:iOS 和 Android](https://kotlinlang.org/docs/tutorials/native/mpp-ios-android.html) 91 | 92 | 示例项目: 93 | 94 | - [KotlinConf app](https://github.com/JetBrains/kotlinconf-app) 95 | - [KotlinConf Spinner app](https://github.com/jetbrains/kotlinconf-spinner) 96 | 97 | 所有例子都可以在 [Github](https://github.com/Kotlin/kotlin-examples) 上找到 -------------------------------------------------------------------------------- /FunctionsAndLambdas/InlineFunctions.md: -------------------------------------------------------------------------------- 1 | ## 内联函数 2 | 使用[高阶函数](http://kotlinlang.org/docs/reference/lambdas.html)带来了相应的运行时麻烦:每个函数都是一个对象,它捕获闭包,即这些变量可以在函数体内被访问。内存的分配,虚拟调用的运行都会带来开销 3 | 4 | 但在大多数这种开销是可以通过内联文本函数避免。下面就是一个很好的例子。`lock()` 函数可以很容易的在内联点调用。思考一下下面的例子: 5 | 6 | ```kotlin 7 | lock(i) { foo() } 8 | ``` 9 | 10 | (Instead of creating a function object for the parameter and generating a call),编译器可以忽略下面的代码: 11 | 12 | ```kotlin 13 | lock.lock() 14 | try { 15 | foo() 16 | } 17 | finally { 18 | lock.lock() 19 | } 20 | ``` 21 | 22 | 这不正是我们最开始想要的吗? 23 | 24 | 为了让编译器这样做,我们需要用 `inline` 标记 `lock()` 函数: 25 | ```kotlin 26 | inline fun lock(lock: Lock,body: ()-> T): T { 27 | //... 28 | } 29 | ``` 30 | 31 | `inline` 标记即影响函数本身也影响传递进来的 lambda 函数:所有的这些都将被关联到调用点。 32 | 33 | 内联可能会引起生成代码增长,但我们可以合理的解决它(不要内联太大的函数) 34 | 35 | ### @noinline 36 | 如果只需要在内联函数中内联部分Lambda表达式,可以使用`@noinline` 注解来标记不需要内联的参数: 37 | 38 | ```kotlin 39 | inline fun foo(inlined: () -> Uint, @noinline notInlined: () -> Unit) { 40 | //... 41 | } 42 | ``` 43 | 44 | 内联的 lambda 只能在内联函数中调用,或者作为内联参数,但 `@noinline` 标记的可以通过任何我们喜欢的方式操控:存储在字段,( passed around etc) 45 | 46 | 注意如果内联函数没有内联的函数参数并且没有具体类型的参数,编译器会报警告,这样内联函数就没有什么优点的(如果你认为内联是必须的你可以忽略警告) 47 | 48 | ### 返回到非局部 49 | 在 kotlin 中,我们可以不加条件的使用 `return` 去退出一个命名函数或表达式函数。这意味这退出一个 lambda 函数,我们不得不使用[标签](http://kotlinlang.org/docs/reference/returns.html#return-at-labels),而且空白的 `return` 在 lambda 函数中是禁止的,因为 lambda 函数不可以造一个闭合函数返回: 50 | 51 | ```kotlin 52 | fun foo() { 53 | ordinaryFunction { 54 | return // 错误 不可以在这返回 55 | } 56 | } 57 | ``` 58 | 59 | 但如果 lambda 函数是内联传递的,则返回也是可以内联的,因此允许下面这样: 60 | 61 | ```kotlin 62 | fun foo() { 63 | inlineFunction { 64 | return // 65 | ] 66 | } 67 | ``` 68 | 69 | 注意有些内联函数可以调用传递进来的 lambda 函数,但不是在函数体,而是在另一个执行的上下文中,比如局部对象或者一个嵌套函数。在这样的情形中,非局部的控制流也不允许在lambda 函数中。为了表明,lambda 参数需要有 `InlineOptions.ONLY_LOCAL_RETURN` 注解: 70 | 71 | ```kotlin 72 | inline fun f(inlineOptions(InlineOption.ONLY_LOCAL_RETURN) body: () -> Unit) { 73 | val f = object: Runnable { 74 | override fun run() = body() 75 | } 76 | // ... 77 | } 78 | ``` 79 | 80 | 内联 lambda 不允许用 break 或 continue ,但在以后的版本可能会支持。 81 | 82 | ### 实例化参数类型 83 | 有时候我们需要访问传递过来的类型作为参数: 84 | 85 | ```kotlin 86 | fun TreeNode.findParentOfType(clazz: Class): T? { 87 | var p = parent 88 | while (p != null && !clazz.isInstance(p)) { 89 | p = p?.parent 90 | } 91 | @suppress("UNCHECKED_CAST") 92 | return p as T 93 | } 94 | ``` 95 | 96 | 现在,我们创立了一颗树,并用反射检查它是否是某个特定类型。一切看起来很好,但调用点就很繁琐了: 97 | 98 | ```kotlin 99 | myTree.findParentOfType(javaClass() ) 100 | ``` 101 | 我们想要的仅仅是给这个函数传递一个类型,即像下面这样: 102 | 103 | ```kotlin 104 | myTree.findParentOfType() 105 | 106 | ``` 107 | 108 | 为了达到这个目的,内联函数支持具体化的类型参数,因此我们可以写成这样: 109 | 110 | 111 | ```kotlin 112 | inline fun TreeNode.findParentOfType(): T? { 113 | var p = parent 114 | while (p != null && p !is T) { 115 | p = p?.parent 116 | } 117 | return p as T 118 | } 119 | ``` 120 | 121 | 我们用 refied 修饰符检查类型参数,既然它可以在函数内部访问了,也就基本上接近普通函数了。因为函数是内联的,所以不许要反射,像 `!is` `as`这样的操作都可以使用。同时,我们也可以像上面那样调用它了 `myTree.findParentOfType()` 122 | 123 | 尽管在很多情况下会使用反射,我们仍然可以使用实例化的类型参数 `javaClass()` 来访问它: 124 | 125 | ```kotlin 126 | inline fun methodsOf() = javaClass().getMethods() 127 | 128 | fun main(s: Array) { 129 | println(methodsOf().joinToString('\n')) 130 | } 131 | ``` 132 | 133 | 普通的函数(没有标记为内联的)不能有实例化参数。 134 | 135 | 更底层的解释请看[spec document](https://github.com/JetBrains/kotlin/blob/master/spec-docs/reified-type-parameters.md) 136 | -------------------------------------------------------------------------------- /MoreLanguageConstructs/Reflection.md: -------------------------------------------------------------------------------- 1 | ## 反射 2 | 反射是一系列语言和库的特性,允许在运行是获取你代码结构。 Kotlin 把函数和属性作为语言的头等类,而且反射它们和使用函数式编程或反应是编程风格很像。 3 | 4 | >On the Java platform, the runtime component required for using the reflection features is distributed as a separate JAR file (kotlin-reflect.jar). This is done to reduce the required size of the runtime library for applications that do not use reflection features. If you do use reflection, please make sure that the .jar file is added to the classpath of your project. 5 | 6 | ### 类引用 7 | 最基本的反射特性就是得到运行时的类引用。要获取引用并使之成为静态类可以使用字面类语法: 8 | 9 | ```kotlin 10 | val c = MyClass::class 11 | ``` 12 | 13 | 引用是一种 [KClass](http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/-k-class/index.html)类型的值。你可以使用 `KClass.properties` 和 `KClass.extensionProperties` 获取类和父类的所有属性引用的列表。 14 | 15 | 注意这与 java 类的引用是不一样的。参看 [java interop section](http://kotlinlang.org/docs/reference/java-interop.html#object-methods) 16 | 17 | 18 | ### 函数引用 19 | 当有一个像下面这样的函数声明时: 20 | 21 | ```kotlin 22 | fun isOdd(x: Int) =x % 2 !=0 23 | ``` 24 | 25 | 我们可以通过 `isOdd(5)` 轻松调用,同样我们也可以把它作为一个值传递给其它函数。我们可以使用 `::` 操作符 26 | 27 | ```kotlin 28 | val numbers = listOf(1, 2, 3) 29 | println(numbers.filter( ::isOdd) ) //prints [1, 3] 30 | ``` 31 | 32 | 这里 `::isOdd` 是是一个函数类型的值 `(Int) -> Boolean` 33 | 34 | 注意现在 `::` 操作符右边不能用语重载函数。将来,我们计划提供一个语法明确参数类型这样就可以使用明确的重载函数了。 35 | 36 | 如果需要使用一系列类,或者扩展函数,必须是需合格的,并且结果是扩展函数类型,比如。`String::toCharArray` 就带来一个 `String: String.() -> CharArray` 类型的扩展函数。 37 | 38 | ### 例子:函数组合 39 | 考虑一下下面的函数: 40 | 41 | ```kotlin 42 | fun compose(f: (B) -> C, g: (A) -> B): (A) -> C { 43 | return {x -> f(g(x))} 44 | } 45 | ``` 46 | 47 | 它返回一个由俩个传递进去的函数的组合。现在你可以把它用在可调用的引用上了: 48 | 49 | ```kotlin 50 | fun length(s: String) = s.size 51 | val oddLength = compose(::isOdd, ::length) 52 | val strings = listOf("a", "ab", "abc") 53 | 54 | println(strings.filter(oddLength)) // Prints "[a, abc]" 55 | ``` 56 | 57 | ### 属性引用 58 | 在 kotlin 中访问顶级类的属性,我们也可以使用 `::` 操作符: 59 | 60 | ```kotlin 61 | var x = 1 62 | fun main(args: Array) { 63 | println(::x.get()) 64 | ::x.set(2) 65 | println(x) 66 | } 67 | ``` 68 | 69 | `::x` 表达式评估为 `KProperty` 类型的属性,它允许我们使用 `get()` 读它的值或者使用名字取回它的属性。更多请参看[docs on the KProperty class](http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/-k-property.html) 70 | 71 | 对于可变的属性比如 `var y =1`,`::y`返回类型为 `[KMutableProperty](http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/-k-mutable-property.html)`,它有 `set()` 方法 72 | 73 | 访问一个类的属性成员,我们这样修饰: 74 | 75 | ```kotlin 76 | class A(val p: Int) 77 | 78 | fun main(args: Array) { 79 | val prop = A::p 80 | println(prop.get(A(1))) // prints "1" 81 | } 82 | ``` 83 | 84 | 对于扩展属性: 85 | 86 | ```kotlin 87 | val String.lastChar: Char 88 | get() = this[size - 1] 89 | 90 | fun main(args: Array) { 91 | println(String::lastChar.get("abc")) // prints "c" 92 | } 93 | ``` 94 | 95 | ### 与 java 反射调用 96 | 在 java 平台上,标准库包括反射类的扩展,提供了到 java 反射对象的映射(参看 kotlin.reflect.jvm 包)。比如,想找到一个备用字段或者 java getter 方法,你可以这样写: 97 | 98 | ```kotlin 99 | import kotlin.reflect.jvm.* 100 | 101 | class A(val p: Int) 102 | 103 | fun main(args: Array) { 104 | println(A::p.javaGetter) // prints "public final int A.getP()" 105 | println(A::p.javaField) // prints "private final int A.p" 106 | } 107 | ``` 108 | 109 | ### 构造函数引用 110 | 构造函数可以像方法或属性那样引用。只需要使用 `::` 操作符并加上类名。下面的函数是一个没有参数并且返回类型是 `Foo`: 111 | 112 | ```kotlin 113 | calss Foo 114 | fun function(factory : () -> Foo) { 115 | val x: Foo = factory() 116 | } 117 | ``` 118 | 119 | 我们可以像下面这样使用: 120 | 121 | ```kotlin 122 | function(:: Foo) 123 | ``` 124 | -------------------------------------------------------------------------------- /Tools/Using-Gradle.md: -------------------------------------------------------------------------------- 1 | ## 使用 Gradle 2 | ### 插件和版本 3 | kotlin-gradle-plugin 可以编译 Kotlin 文件和模块 4 | 5 | > X.Y.SNAPSHOT:对应版本 X.Y 的快照,在 CI 服务器上的每次成功构建的版本。这些版本不是真正的稳定版,只是推荐用来测试新编辑器的功能的。现在所有的构建都是作为 0.1-SNAPSHOT 发表的。你可以参看[configure a snapshot repository in the pom file]() 6 | 7 | >X.Y.X: 对应版本 X.Y.Z 的 release 或 milestone ,自动升级。它们是文件构建。Release 版本发布在 Maven Central 仓库。在 pom 文件里不需要多余的配置。 8 | 9 | milestone 和 版本的对应关系如下: 10 | 11 | **Milestone**|**Version** 12 | ---|--- 13 | M12.1|0.12.613 14 | M12|0.12.200 15 | M11.1|0.11.91.1 16 | M11|0.11.91 17 | M10.1|0.10.195 18 | M10|0.10.4 19 | M9|0.9.66 20 | M8|0.8.11 21 | M7|0.7.270 22 | M6.2|0.6.1673 23 | M6.1|0.6.602 24 | M6|0.6.69 25 | M5.3|0.5.998 26 | 27 | ### 面向 Jvm 28 | 对于 jvm,需要应用 kotlin 插件 29 | 30 | > apply plugin: "kotlin" 31 | 32 | 至于 M11 ,kotlin 文件可以与 java 混用。默认使用不同文件夹: 33 | 34 | ``` 35 | project 36 | - src 37 | - main (root) 38 | - kotlin 39 | - java 40 | ``` 41 | 42 | 如果不使用默认的设置则对应的文件属性要修改: 43 | 44 | ```gradle 45 | sourceSets { 46 | main.kotlin.srcDirs += 'src/main/myKotlin' 47 | main.java.srcDirs += 'src/main/myJava' 48 | } 49 | ``` 50 | 51 | ### 面向JavaScript 52 | 但目标是 JavaScript 时: 53 | 54 | > apply plugin: "kotln2js" 55 | 56 | 这个插件只对 kotlin 文件起作用,因此建议把 kotlin 和 java 文件分开。对于 jvm 如果不用默认的值则需要修改源文件夹: 57 | 58 | ``` 59 | sourceSets { 60 | main.kotlin.srcDirs += 'src/main/myKotlin' 61 | } 62 | ``` 63 | 64 | 如果你想建立一个复用的库,使用 `kotlinOptions.metaInfo` 生成附加的带附加二进制描述的 js 文件 65 | 66 | ``` 67 | compileKotlin2Js { 68 | kotlinOptions.metaInfo = true 69 | } 70 | ``` 71 | 72 | ### 目标是 android 73 | Android Gradle 模块与普通的 Gradle 模块有些不同,所以如果你想建立 kotlin 写的android 项目,则需要下面这样: 74 | 75 | ``` 76 | buildscript { 77 | ... 78 | } 79 | apply plugin: 'com.android.application' 80 | apply plugin: 'kotlin-android' 81 | ``` 82 | 83 | #### Android Studio 84 | 如果使用 Android Studio,需要添加下面的代码: 85 | 86 | ``` 87 | android { 88 | ... 89 | 90 | sourceSets { 91 | main.java.srcDirs += 'src/main/kotlin' 92 | } 93 | } 94 | ``` 95 | 96 | 这是告诉 android studio kotlin 文件的目录位置方便 IDE 识别 97 | 98 | ### 配置依赖 99 | 我们需要添加 kotlin-gradle-plugin 和 kotlin 标准库依赖 100 | 101 | ``` 102 | buildscript { 103 | repositories { 104 | mavenCentral() 105 | } 106 | dependencies { 107 | classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:' 108 | } 109 | } 110 | 111 | apply plugin: "kotlin" // or apply plugin: "kotlin2js" if targeting JavaScript 112 | 113 | repositories { 114 | mavenCentral() 115 | } 116 | 117 | dependencies { 118 | compile 'org.jetbrains.kotlin:kotlin-stdlib:' 119 | } 120 | ``` 121 | 122 | ### 使用快照版本 123 | 如果使用快照版本则如下所示: 124 | 125 | ``` 126 | buildscript { 127 | repositories { 128 | mavenCentral() 129 | maven { 130 | url 'http://oss.sonatype.org/content/repositories/snapshots' 131 | } 132 | } 133 | dependencies { 134 | classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:0.1-SNAPSHOT' 135 | } 136 | } 137 | 138 | apply plugin: "kotlin" // or apply plugin: "kotlin2js" if targeting JavaScript 139 | 140 | repositories { 141 | mavenCentral() 142 | maven { 143 | url 'http://oss.sonatype.org/content/repositories/snapshots' 144 | } 145 | } 146 | 147 | dependencies { 148 | compile 'org.jetbrains.kotlin:kotlin-stdlib:0.1-SNAPSHOT' 149 | } 150 | ``` 151 | 152 | 153 | ### 例子 154 | [Kotlin](https://github.com/jetbrains/kotlin)仓库有如下例子: 155 | 156 | >[Kotlin](https://github.com/JetBrains/kotlin-examples/tree/master/gradle/hello-world) 157 | >[Mixed java and Kotlin](https://github.com/JetBrains/kotlin-examples/tree/master/gradle/mixed-java-kotlin-hello-world) 158 | >[Android](https://github.com/JetBrains/kotlin-examples/tree/master/gradle/android-mixed-java-kotlin-project) 159 | >[javaScript](https://github.com/JetBrains/kotlin/tree/master/libraries/tools/kotlin-gradle-plugin/src/test/resources/testProject/kotlin2JsProject) 160 | -------------------------------------------------------------------------------- /Tools/Using-Maven.md: -------------------------------------------------------------------------------- 1 | ## 使用Maven 2 | ### 插件和版本 3 | Kotlin-maven-plugin 可以编译 Kotlin 资源和模块。现在只有 Maven V3 支持 4 | 5 | 通过 Kotlin.version 定义你想要的 Kotlin 版本。可以有以下的值 6 | 7 | > X.Y.SNAPSHOT:对应版本 X.Y 的快照,在 CI 服务器上的每次成功构建的版本。这些版本不是真正的稳定版,只是推荐用来测试新编辑器的功能的。现在所有的构建都是作为 0.1-SNAPSHOT 发表的。你可以参看[configure a snapshot repository in the pom file]() 8 | 9 | >X.Y.X: 对应版本 X.Y.Z 的 release 或 milestone ,自动升级。它们是文件构建。Release 版本发布在 Maven Central 仓库。在 pom 文件里不需要多余的配置。 10 | 11 | milestone 和 版本的对应关系如下: 12 | 13 | **Milestone**|**Version** 14 | ---|--- 15 | M12.1|0.12.613 16 | M12|0.12.200 17 | M11.1|0.11.91.1 18 | M11|0.11.91 19 | M10.1|0.10.195 20 | M10|0.10.4 21 | M9|0.9.66 22 | M8|0.8.11 23 | M7|0.7.270 24 | M6.2|0.6.1673 25 | M6.1|0.6.602 26 | M6|0.6.69 27 | M5.3|0.5.998 28 | 29 | ### 配置快照仓库 30 | 使用 kotlin 版本的快照,需要在 pom 中这样定义: 31 | 32 | ```pom 33 | 34 | 35 | sonatype.oss.snapshots 36 | Sonatype OSS Snapshot Repository 37 | http://oss.sonatype.org/content/repositories/snapshots 38 | 39 | false 40 | 41 | 42 | true 43 | 44 | 45 | 46 | 47 | 48 | 49 | sonatype.oss.snapshots 50 | Sonatype OSS Snapshot Repository 51 | http://oss.sonatype.org/content/repositories/snapshots 52 | 53 | false 54 | 55 | 56 | true 57 | 58 | 59 | 60 | ``` 61 | 62 | ### 依赖 63 | kotlin 有一些扩展标准库可以使用。在 pom 文件中使用如下的依赖: 64 | 65 | ```pom 66 | 67 | 68 | org.jetbrains.kotlin 69 | kotlin-stdlib 70 | ${kotlin.version} 71 | 72 | 73 | ``` 74 | 75 | ### 只编译 kotlin 源码 76 | 编译源码需要在源码文件夹打一个标签: 77 | 78 | ```xml 79 | ${project.basedir}/src/main/kotlin 80 | ${project.basedir}/src/test/kotlin 81 | ``` 82 | 83 | 在编译资源是需要引用kotlin Maven Plugin: 84 | 85 | ```xml 86 | 87 | kotlin-maven-plugin 88 | org.jetbrains.kotlin 89 | ${kotlin.version} 90 | 91 | 92 | compile 93 | compile 94 | compile 95 | 96 | 97 | test-compile 98 | test-compile 99 | test-compile 100 | 101 | 102 | 103 | ``` 104 | 105 | ### 编译 kotlin 和 java 资源 106 | 为了编译混合代码的应用,Kotlin 编译器应该在 java 编译器之前先工作。在 maven 中意味着 kotlin-maven-plug 应该在 maven-compiler-plugin 之前。 107 | 108 | ```xml 109 | 110 | kotlin-maven-plugin 111 | org.jetbrains.kotlin 112 | 0.1-SNAPSHOT 113 | 114 | 115 | compile 116 | process-sources 117 | compile 118 | 119 | 120 | test-compile 121 | process-test-sources 122 | test-compile 123 | 124 | 125 | 126 | ``` 127 | 128 | ### 使用扩展的注解 129 | kotlin 使用扩展的注解解析 java 库的信息。为了明确这些注解,你需要像下面这样: 130 | 131 | ```xml 132 | 133 | kotlin-maven-plugin 134 | org.jetbrains.kotlin 135 | 0.1-SNAPSHOT 136 | 137 | 138 | path to annotations root 139 | 140 | 141 | ``` 142 | 143 | ### 例子 144 | 你可以在 [Github]() 仓库参考 145 | -------------------------------------------------------------------------------- /Basics/Control-Flow.md: -------------------------------------------------------------------------------- 1 | ## 流程控制: if , when , for , while 2 | ### if 表达式 3 | 在 Kotlin 中,if 是带有返回值的表达式。因此Kotlin没有三元运算符(condition ? then : else),因为 if 语句可以做到同样的事。 4 | 5 | ```kotlin 6 | // 传统用法 7 | var max = a 8 | if (a < b) max = b 9 | 10 | // 带 else 11 | var max: Int 12 | if (a > b) { 13 | max = a 14 | } 15 | else{ 16 | max = b 17 | } 18 | // 作为表达式 19 | val max = if (a > b) a else b 20 | ``` 21 | 22 | if分支可以作为块,最后一个表达式是该块的值: 23 | 24 | ```kotlin 25 | val max = if (a > b){ 26 | print("Choose a") 27 | a 28 | } 29 | else{ 30 | print("Choose b") 31 | b 32 | } 33 | ``` 34 | 35 | 36 | 如果使用If作为表达式而不是声明(例如,返回其值或将其赋值给一个变量),表达式必须带有 else 分支。 37 | 38 | 参看 [if语法](http://kotlinlang.org/docs/reference/grammar.html#if) 39 | 40 | ### When 表达式 41 | when 取代了 C 风格语言的 switch 。最简单的用法像下面这样 42 | 43 | ```kotlin 44 | when (x) { 45 | 1 -> print("x == 1") 46 | 2 -> print("x == 2") 47 | else -> { // Note the block 48 | print("x is neither 1 nor 2") 49 | } 50 | } 51 | ``` 52 | 53 | when会对所有的分支进行检查直到有一个条件满足。when 可以用做表达式或声明。如果用作表达式的话,那么满足条件的分支就是表达式的值jjs。如果用做声明,那么分支的值会被忽略。(与 if 表达式一样,每个分支是一个语句块,而且它的值就是最后一个表达式的值) 54 | 55 | 在其它分支都不匹配的时候默认匹配 else 分支。如果把 when 做为表达式的话 else 分支是强制的,除非编译器可以证明分支条件已经覆盖所有可能性。 56 | 57 | 如果有分支可以用同样的方式处理的话,分支条件可以连在一起: 58 | 59 | ```kotlin 60 | when (x) { 61 | 0,1 -> print("x == 0 or x == 1") 62 | else -> print("otherwise") 63 | } 64 | ``` 65 | 66 | 可以用任意表达式作为分支的条件 67 | 68 | ```kotlin 69 | when (x) { 70 | parseInt(s) -> print("s encode x") 71 | else -> print("s does not encode x") 72 | } 73 | ``` 74 | 75 | 甚至可以用 in 或者 !in 检查值是否值在一个[范围](http://kotlinlang.org/docs/reference/ranges.html)或一个集合中: 76 | 77 | ```kotlin 78 | when (x) { 79 | in 1..10 -> print("x is in the range") 80 | in validNumbers -> print("x is valid") 81 | !in 10..20 -> print("x is outside the range") 82 | else -> print("none of the above") 83 | } 84 | ``` 85 | 86 | 也可以用 is 或者 !is 来判断值是否是某个类型。注意,由于 [smart casts](http://kotlinlang.org/docs/reference/typecasts.html#smart-casts) ,可以不进行类型检查就可以使用相应的属性或方法。 87 | 88 | ```kotlin 89 | val hasPrefix = when (x) { 90 | is String -> x.startsWith("prefix") 91 | else -> false 92 | } 93 | ``` 94 | 95 | when 也可以用来代替 if-else if 。如果没有任何参数提供,那么分支的条件就是简单的布尔表达式,当条件为真时执行相应的分支: 96 | 97 | ```kotlin 98 | when { 99 | x.isOdd() -> print("x is odd") 100 | x.isEven() -> print("x is even") 101 | else -> print("x is funny") 102 | } 103 | ``` 104 | 105 | 参看[when语法](http://kotlinlang.org/docs/reference/grammar.html#when) 106 | 107 | ### for 循环 108 | for 循环可以对所有提供迭代器的变量进行迭代。等同于 C# 等语言中的 `foreach`。语法形式: 109 | 110 | ```kotlin 111 | for (item in collection) 112 | print(item) 113 | ``` 114 | 115 | 内容可以是一个语句块 116 | 117 | ```kotlin 118 | for (item: Int in ints){ 119 | // ... 120 | } 121 | ``` 122 | 123 | 像之前提到的, for 可以对任何提供的迭代器的变量进行迭代,比如: 124 | 125 | > -- 有一个 iterator() 成员函数或扩展函数,其返回类型要 126 | 127 | > -- 有一个 next() 成员函数或扩展函数, 并且 128 | 129 | > -- 有一个返回 Boolean 的 hasNext() 成员函数或扩展函数。 130 | 131 | 这三个函数都需要标记为运算符. 132 | 133 | 对数组的for循环不会创建迭代器对象,而是被编译成一个基于索引的循环. 134 | 135 | 如果你想通过 list 或者 array 的索引进行迭代,你可以这样做: 136 | 137 | ```kotlin 138 | for (i in array.indices) 139 | print(array[i]) 140 | ``` 141 | 142 | 这里需要说明的是 "iteration through a range " 会被自动编译成最优的实现,并不会有附加对象生成。 143 | 144 | 或者,您也可以使用`withIndex`库函数 145 | 146 | ```kotlin 147 | for ((index, value) in array.withIndex()) { 148 | println("the element at $index is $value") 149 | } 150 | ``` 151 | 152 | 参看[for语法](http://kotlinlang.org/docs/reference/grammar.html#for) 153 | 154 | ### while 循环 155 | while 和 do...while 和其它语言没什么区别 156 | 157 | ```kotlin 158 | while (x > 0) { 159 | x-- 160 | } 161 | 162 | do { 163 | val y = retrieveData() 164 | } while (y != null) // y 在这是可见的 165 | ``` 166 | 167 | 参看[while 语法](http://kotlinlang.org/docs/reference/grammar.html#while) 168 | 169 | ### 在循环中使用 break 和 continue 170 | kotlin 支持传统的 break 和 continue 操作符。参看[返回和跳转](http://kotlinlang.org/docs/reference/returns.html) 171 | -------------------------------------------------------------------------------- /Tools/Using-Ant.md: -------------------------------------------------------------------------------- 1 | ## 使用 Ant 2 | ### 获得 Ant 任务 3 | Kotlin 提供了 Ant 三个任务: 4 | 5 | > kotlinc : Kotlin 面向 JVM 的编译器 6 | 7 | > kotlin2js: 面向 javaScript 的编译器 8 | 9 | > withKotlin: 使用标准 javac Ant 任务时编译 Kotlin 文件的任务 10 | 11 | 这些任务定义学在 kotlin-ant.jar 标准库中,位与 [kotlin compiler](https://github.com/JetBrains/kotlin/releases/tag/build-0.12.613) 的 lib 文件夹下 12 | 13 | ### 面向 JVM 的只有 kotlin 源文件任务 14 | 当项目只有 kotlin 源文件时,最简单的方法就是使用 kotlinc 任务: 15 | 16 | ```ant 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | ``` 25 | 26 | ${kotlin.lib} 指向 kotlin 单独编译器解压的文件夹 27 | 28 | ### 面向 JVM 的只有 kotlin 源文件但有多个根的任务 29 | 如果一个项目包含多个根源文件,使用 src 定义路径: 30 | 31 | ```ant 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | ``` 43 | 44 | ### 面向 JVM 的有 kotlin 和 java 源文件 45 | 如果项目包含 java kotlin 代码,使用 kotlinc 是可以的,但建议使用 withKotlin 任务 46 | 47 | ```ant 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | ``` 63 | 64 | ### 面向 JavaScript 的只有一个源文件夹的 65 | ```ant 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | ``` 74 | 75 | ### 面向 JavaScript 有前缀,后缀以及 sourcemap 选项 76 | ```ant 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | ``` 85 | 86 | #### ##面向 JavaScript 只有一个源码文件夹并有元信息的选项 87 | 如果你想要描述 javaScript/Kotlin 库的转换结果,`mateInfo` 选项是很有用的。如果`mateInfo` 设置为 true 则编译附加 javaScript 文件时会创建二进制的元数据。这个文件会与转换结果一起发布 88 | 89 | ```ant 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | ``` 99 | 100 | ## 参考 101 | 下面是所有的元素和属性 102 | 103 | #### #kotlinc 属性**名字**|**描述**|**必须性**|**默认值** 104 | ---|---|---|---| 105 | src|要编译的Kotlin 文件或者文件夹|yes| 106 | output|目标文件夹或 .jar 文件名 |yes| 107 | classpath|类的完整路径|no| 108 | classpathref|类的完整路径参考|no| 109 | stdlib|"Kotlin-runtime.jar" 的完整路径|no|”“ 110 | includeRuntime|如果输出是 .jar 文件,是否 kotlin 运行时库是否包括在 jar 中|no|true 111 | 112 | #### #withKotlin 属性 113 | **名字**|**描述**|**必须性**|**默认值** 114 | ---|---|---|---| 115 | src|要编译的Kotlin 文件或者文件夹|yes| 116 | output|目标文件夹 |yes| 117 | library|库文件(kt,dir,jar) |no| 118 | outputPrefix|生成 javaScript 文件的前缀|no| 119 | outputSufix|生成 javaScript 文件的后缀|no| 120 | sourcemap|是否生成 sourcemap |no| 121 | metaInfo |是否生成二进制元数据文件描述 |no| 122 | main |是否生成调用主函数 |no| 123 | -------------------------------------------------------------------------------- /ClassesAndObjects/ObjectExpressicAndDeclarations.md: -------------------------------------------------------------------------------- 1 | ## 对象表达式和对象声明 2 | 有时候我们想要创建一个对某个类有一点小修改的对象,而不是声明一个新子类。Kotlin 中用*对象表达式*和*对象声明*来实现。 3 | 4 | ### 对象表达式 5 | 以下方式可以创建继承自某种(或某些)匿名类的对象: 6 | 7 | ```kotlin 8 | window.addMouseListener(object : MouseAdapter() { 9 | override fun mouseClicked(e: MouseEvent) { /*...*/ } 10 | 11 | override fun mouseEntered(e: MouseEvent) { /*...*/ } 12 | }) 13 | ``` 14 | 15 | 如果父类有构造函数,则必须传递相应的构造参数。多个父类可以用逗号隔开,跟在冒号后面: 16 | 17 | ```kotlin 18 | open class A(x: Int) { 19 | public open val y: Int = x 20 | } 21 | 22 | interface B { ... } 23 | 24 | val ab = object : A(1), B { 25 | override val y = 14 26 | } 27 | ``` 28 | 29 | 有时候我们只是需要一个的对象,没有任何父类,我们可以这样写: 30 | 31 | ```kotlin 32 | fun foo() { 33 | val adHoc = object { 34 | var x: Int = 0 35 | var y: Int = 0 36 | } 37 | print(adHoc.x + adHoc.y) 38 | } 39 | ``` 40 | 41 | 这里需要注意匿名对象作为类型只能出现在本地或者私有声明中. 如果把匿名对象作为公有函数返回类型或者公有属性时, 真正的类型将会是匿名函数的超类,如果声明没有超类则会使 `Any` .作为成员变量的匿名对象是不可访问的. 42 | 43 | 44 | ```kotlin 45 | class C { 46 | // Private function, so the return type is the anonymous object type 47 | private fun foo() = object { 48 | val x: String = "x" 49 | } 50 | 51 | // Public function, so the return type is Any 52 | fun publicFoo() = object { 53 | val x: String = "x" 54 | } 55 | 56 | fun bar() { 57 | val x1 = foo().x // Works 58 | val x2 = publicFoo().x // ERROR: Unresolved reference 'x' 59 | } 60 | } 61 | ``` 62 | 63 | 在对象表达式中可以访问来自包含它的作用域的变量. 64 | 65 | ```kotlinl 66 | fun countClicks(window: JComponent) { 67 | var clickCount = 0 68 | var enterCount = 0 69 | 70 | window.addMouseListener(object : MouseAdapter() { 71 | override fun mouseClicked(e: MouseEvent) { 72 | clickCount++ 73 | } 74 | 75 | override fun mouseEntered(e: MouseEvent) { 76 | enterCount++ 77 | } 78 | }) 79 | // ... 80 | } 81 | ``` 82 | 83 | ### 对象声明 84 | [单例模式](http://en.wikipedia.org/wiki/Singleton_pattern)在很多情形中很实用,Kotln(在 Scala 之后)大大简化了声明方式: 85 | 86 | ```kotlin 87 | object DataProviderManager { 88 | fun registerDataProvider(provider: DataProvider) { 89 | // ... 90 | } 91 | 92 | val allDataProviders: Collection 93 | get() = // ... 94 | } 95 | ``` 96 | 97 | 这叫做对象声明,跟在 object 关键字后面是对象名。和变量声明一样,对象声明并不是表达式,而且不能作为右值用在赋值语句。 98 | 99 | 对象声明的初始化 100 | 101 | 想要访问这个类,直接通过名字来使用这个类: 102 | 103 | ```kotlin 104 | DataProviderManager.registerDataProvider(...) 105 | ``` 106 | 107 | 这样类型的对象可以有父类型: 108 | 109 | ```kotlin 110 | object DefaultListener : MouseAdapter() { 111 | override fun mouseClicked(e: MouseEvent) { 112 | // ... 113 | } 114 | 115 | override fun mouseEntered(e: MouseEvent) { 116 | // ... 117 | } 118 | } 119 | ``` 120 | 121 | **注意**:对象声明不可以是局部的(比如不可以直接在函数内部声明),但可以在其它对象的声明或非内部类中进行内嵌入 122 | 123 | ### 伴随对象 124 | 在类声明内部可以用 companion 关键字标记对象声明: 125 | 126 | ```kotln 127 | class MyClass { 128 | companion object Factory { 129 | fun create(): MyClass = MyClass() 130 | } 131 | } 132 | ``` 133 | 134 | 伴随对象的成员可以通过类名做限定词直接使用: 135 | 136 | ```kotlin 137 | val instance = MyClass.create() 138 | ``` 139 | 140 | 在使用了 `companion` 关键字时,伴随对象的名字可以省略: 141 | 142 | ```kotlin 143 | class MyClass { 144 | companion object { 145 | 146 | } 147 | } 148 | ``` 149 | 150 | 注意,尽管伴随对象的成员很像其它语言中的静态成员,但在运行时它们任然是真正对象的成员实例,比如可以实现接口: 151 | 152 | ```kotlin 153 | interface Factory { 154 | fun create(): T 155 | } 156 | 157 | class MyClass { 158 | companion object : Factory { 159 | override fun create(): MyClass = MyClass() 160 | } 161 | } 162 | ``` 163 | 164 | 如果你在 JVM 上使用 `@JvmStatic` 注解,你可以有多个伴随对象生成为真实的静态方法和属性。参看 [java interoperabillity](https://kotlinlang.org/docs/reference/java-interop.html#static-methods-and-fields)。 165 | 166 | ### 对象表达式和声明的区别 167 | 他俩之间只有一个特别重要的区别: 168 | 169 | > 对象表达式在我们使用的地方立即初始化并执行的 170 | > 171 | > 对象声明是懒加载的,是在我们第一次访问时初始化的。 172 | > 173 | >​ 伴随对象是在对应的类加载时初始化的,和 Java 的静态初始是对应的。 174 | -------------------------------------------------------------------------------- /ClassesAndObjects/Extensions.md: -------------------------------------------------------------------------------- 1 | ## 扩展 2 | 与 C# 和 Gosu 类似, Kotlin 也提供了一种,可以在不继承父类,也不使用类似装饰器这样的设计模式的情况下对指定类进行扩展。我们可以通过一种叫做扩展的特殊声明来实现他。Kotlin 支持函数扩展和属性扩展。 3 | 4 | ### 函数扩展 5 | 为了声明一个函数扩展,我们需要在函数前加一个接收者类型作为前缀。下面我们会为 `MutableList` 添加一个 `swap` 函数: 6 | 7 | ```kotlin 8 | fun MutableList.swap(x: Int, y: Int) { 9 | val tmp = this[x] // this 对应 list 10 | this[x] = this[y] 11 | this[y] = tmp 12 | } 13 | ``` 14 | 15 | 在扩展函数中的 this 关键字对应接收者对象。现在我们可以在任何 `MutableList` 实例中使用这个函数了: 16 | 17 | ```kotlin 18 | val l = mutableListOf(1, 2, 3) 19 | l.swap(0, 2)// 在 `swap()` 函数中 `this` 持有的值是 `l` 20 | ``` 21 | 22 | 当然,这个函数对任意的 `MutableList` 都是适用的,而且我们可以把它变的通用: 23 | 24 | ```kotlin 25 | fun MutableList.swap(x: Int, y: Int) { 26 | val tmp = this[x] // 'this' corresponds to the list 27 | this[x] = this[y] 28 | this[y] = tmp 29 | } 30 | ``` 31 | 32 | 我们在函数名前声明了通用类型,从而使它可以接受任何参数。参看[泛型函数](http://kotlinlang.org/docs/reference/generics.html)。 33 | 34 | ### 扩展是被**静态**解析的 35 | 扩展实际上并没有修改它所扩展的类。定义一个扩展,你并没有在类中插入一个新的成员,只是让这个类的实例对象能够通过`.`调用新的函数。 36 | 37 | 需要强调的是扩展函数是静态分发的,举个例子,它们并不是接受者类型的虚拟方法。这意味着扩展函数的调用是由发起函数调用的表达式的类型决定的,而不是在运行时动态获得的表达式的类型决定。比如 38 | 39 | ```Kotlin 40 | open class C 41 | 42 | class D: C() 43 | 44 | fun C.foo() = "c" 45 | 46 | fun D.foo() = "d" 47 | 48 | fun printFoo(c: C) { 49 | println(c.foo()) 50 | } 51 | 52 | printFoo(D()) 53 | ``` 54 | 55 | 这个例子会输出 `c`,因为这里扩展函数的调用决定于声明的参数 `c` 的类型,也就是 `C`。 56 | 57 | 如果有同名同参数的成员函数和扩展函数,调用的时候必然会使用成员函数,比如: 58 | 59 | ```kotlin 60 | 61 | class C { 62 | fun foo() { println("member") } 63 | 64 | } 65 | fun C.foo() { println("extension") } 66 | ``` 67 | 68 | 当我们对C的实例c调用`c.foo()`的时候,他会输出"member",而不是"extension" 69 | 70 | 但你可以用不同的函数签名通过扩展函数的方式重载函数的成员函数,比如下面这样: 71 | 72 | ```Kotlin 73 | class C { 74 | fun foo() { println("number") } 75 | } 76 | 77 | fun C.foo(i:Int) { println("extention") } 78 | ``` 79 | 80 | `C().foo(1)` 的调用会打印 “extentions”。 81 | 82 | ### 可空的接收者 83 | 注意扩展可以使用空接收者类型进行定义。这样的扩展使得,即使是一个空对象仍然可以调用该扩展,然后在扩展的内部进行 `this == null` 的判断。这样你就可以在 Kotlin 中任意调用 toString() 方法而不进行空指针检查:空指针检查延后到扩展函数中完成。 84 | 85 | ```kotlin 86 | fun Any?.toString(): String { 87 | if (this == null) return "null" 88 | // 在空检查之后,`this` 被自动转为非空类型,因此 toString() 可以被解析到任何类的成员函数中 89 | return toString() 90 | } 91 | ``` 92 | 93 | ### 属性扩展 94 | 和函数类似, Kotlin 也支持属性扩展: 95 | 96 | ```kotlin 97 | val List.lastIndex: Int 98 | get() = size-1 99 | ``` 100 | 101 | 注意,由于扩展并不会真正给类添加了成员属性,因此也没有办法让扩展属性拥有一个备份字段.这也是为什么**初始化函数不允许有扩展属性**。扩展属性只能够通过明确提供 getter 和 setter方法来进行定义. 102 | 103 | 例子: 104 | ```kotlin 105 | val Foo.bar = 1 //error: initializers are not allowed for extension properties 106 | ``` 107 | 108 | ### 伴随对象扩展 109 | 如果一个对象定义了伴随对象,你也可以给伴随对象添加扩展函数或扩展属性: 110 | 111 | ```kotlin 112 | class MyClass { 113 | companion object {} 114 | } 115 | fun MyClass.Companion.foo(){ 116 | 117 | } 118 | ``` 119 | 120 | 和普通伴随对象的成员一样,它们可以只用类的名字就调用: 121 | 122 | ```kotlin 123 | MyClass.foo() 124 | ``` 125 | 126 | ### 扩展的域 127 | 大多数时候我们在 top level 定义扩展,就在包下面直接定义: 128 | 129 | ```kotlin 130 | package foo.bar 131 | fun Baz.goo() { ... } 132 | ``` 133 | 134 | 为了在除声明的包外使用这个扩展,我们需要在 import 时导入: 135 | 136 | ```kotlin 137 | package com.example.usage 138 | 139 | import foo.bar.goo // 导入所有名字叫 "goo" 的扩展 140 | 141 | // 或者 142 | 143 | import foo.bar.* // 导入foo.bar包下得所有数据 144 | 145 | fun usage(baz: Baz) { 146 | baz.goo() 147 | } 148 | ``` 149 | 150 | ### 动机 151 | 在 java 中,我们通常使用一系列名字为 "*Utils" 的类: `FileUtils`,`StringUtils`等等。很有名的 `java.util.Collections` 也是其中一员的,但我们不得不像下面这样使用他们: 152 | 153 | ```java 154 | //java 155 | Collections.swap(list, Collections.binarySearch(list, Collections.max(otherList)), Collections.max(list)) 156 | ``` 157 | 158 | 由于这些类名总是不变的。我们可以使用静态导入并这样使用: 159 | 160 | ```java 161 | swap(list, binarySearch(list, max(otherList)), max(list)) 162 | ``` 163 | 164 | 这样就好很多了,但这样我们就只能从 IDE 自动完成代码那里获得很少或得不到帮助信息。如果我们可以像下面这样那么就好多了 165 | 166 | ```kotlin 167 | list.swap(list.binarySearch(otherList.max()), list.max()) 168 | ``` 169 | 170 | 但我们又不想在 List 类中实现所有可能的方法。这就是扩展带来的好处。 171 | -------------------------------------------------------------------------------- /ClassesAndObjects/Properties-and-Fields.md: -------------------------------------------------------------------------------- 1 | ## 属性和字段 2 | ### 属性声明 3 | 在 Kotlin 中类可以有属性,我们可以使用 var 关键字声明可变属性,或者用 val 关键字声明只读属性。 4 | 5 | ```kotlin 6 | class Address { 7 | var name: String = ... 8 | var street: String = ... 9 | var city: String = ... 10 | var state: String? = ... 11 | var zip: String = ... 12 | } 13 | ``` 14 | 15 | 我们可以像使用 java 中的字段那样,通过名字直接使用一个属性: 16 | 17 | ```kotlin 18 | fun copyAddress(address: Address) : Address { 19 | val result = Address() // 在 kotlin 中没有 new 关键字 20 | result.name = address.name // accessors are called 21 | result.street = address.street 22 | // ... 23 | return result 24 | } 25 | ``` 26 | 27 | ### Getters 和 Setters 28 | 声明一个属性的完整语法如下: 29 | 30 | ```kotlin 31 | var : [ = ] 32 | 33 | 34 | ``` 35 | 36 | 语法中的初始化语句,getter 和 setter 都是可选的。如果属性类型可以从初始化语句或者类的成员函数中推断出来,那么他的类型也是忽略的。 37 | 38 | 例子: 39 | 40 | ```kotlin 41 | var allByDefault: Int? // 错误: 需要一个初始化语句, 默认实现了 getter 和 setter 方法 42 | var initialized = 1 // 类型为 Int, 默认实现了 getter 和 setter 43 | ``` 44 | 45 | 只读属性的声明语法和可变属性的声明语法相比有两点不同: 它以 `val` 而不是 `var` 开头,不允许 setter 函数: 46 | 47 | ```kotlin 48 | val simple: Int? // 类型为 Int ,默认实现 getter ,但必须在构造函数中初始化 49 | 50 | val inferredType = 1 // 类型为 Int 类型,默认实现 getter 51 | ``` 52 | 53 | 我们可以像写普通函数那样在属性声明中自定义的访问器,下面是一个自定义的 getter 的例子: 54 | 55 | ```kotlin 56 | var isEmpty: Boolean 57 | get() = this.size == 0 58 | ``` 59 | 60 | 下面是一个自定义的setter: 61 | 62 | ```kotlin 63 | var stringRepresentation: String 64 | get() = this.toString() 65 | set (value) { 66 | setDataFormString(value) // 格式化字符串,并且将值重新赋值给其他元素 67 | } 68 | ``` 69 | 70 | 为了方便起见,setter 方法的参数名是`value`,你也可以自己任选一个自己喜欢的名称. 71 | 72 | 从Kotlin 1.1开始,如果可以从getter方法推断出类型则可以省略之: 73 | 74 | ```kotlin 75 | val isEmpty get() = this.size == 0 // 拥有Boolean类型 76 | ``` 77 | 78 | 如果你需要改变一个访问器的可见性或者给它添加注解,但又不想改变默认的实现,那么你可以定义一个不带函数体的访问器: 79 | 80 | ```kotlin 81 | var setterVisibility: String = "abc"// 非空类型必须初始化 82 | private set // setter是私有的并且有默认的实现 83 | var setterWithAnnotation: Any? 84 | @Inject set // 用 Inject 注解 setter 85 | ``` 86 | 87 | ### 备用字段 88 | 在 kotlin 中类不可以有字段。然而当使用自定义的访问器时有时候需要备用字段。出于这些原因 kotlin 使用 `field` 关键词提供了自动备用字段, 89 | 90 | ```kotlin 91 | var counter = 0 // 初始化值会直接写入备用字段 92 | set(value) { 93 | if (value >= 0) 94 | field = value 95 | } 96 | ``` 97 | 98 | `field` 关键词只能用于属性的访问器. 99 | 100 | 编译器会检查访问器的代码,如果使用了备用字段(或者访问器是默认的实现逻辑),就会自动生成备用字段,否则就不会. 101 | 102 | 比如下面的例子中就不会有备用字段: 103 | 104 | ```kotlin 105 | val isEmpty: Boolean 106 | get() = this.size == 0 107 | ``` 108 | 109 | ### 备用属性 110 | 如果你想要做一些事情但不适合这种 "隐含备用字段" 方案,你可以试着用备用属性的方式: 111 | 112 | ```kotlin 113 | private var _table: Map? = null 114 | public val table: Map 115 | get() { 116 | if (_table == null) 117 | _table = HashMap() //参数类型是推导出来的 118 | return _table ?: throw AssertionError("Set to null by another thread") 119 | } 120 | ``` 121 | 122 | 综合来讲,这些和 java 很相似,可以避免函数访问私有属性而破坏它的结构 123 | 124 | ### 编译时常量 125 | 那些在编译时就能知道具体值的属性可以使用 `const` 修饰符标记为 *编译时常量*. 这种属性需要同时满足以下条件: 126 | 127 | * 在top-level声明的 或者 是一个`object`的成员(Top-level or member of an object) 128 | 129 | * 以`String`或基本类型进行初始化 130 | 131 | * 没有自定义getter 132 | 133 | 这种属性可以被当做注解使用: 134 | ```kotlin 135 | const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated" 136 | @Deprected(SUBSYSTEM_DEPRECATED) fun foo() { ... } 137 | ``` 138 | 139 | ### 延迟初始化属性 140 | 通常,那些被定义为拥有非空类型的属性,都需要在构造器中初始化.但有时候这并没有那么方便.例如在单元测试中,属性应该通过依赖注入进行初始化, 141 | 或者通过一个 setup 方法进行初始化.在这种条件下,你不能在构造器中提供一个非空的初始化语句,但是你仍然希望在访问这个属性的时候,避免非空检查. 142 | 143 | 为了处理这种情况,你可以为这个属性加上 `lateinit` 修饰符 144 | 145 | ```kotlin 146 | public class MyTest { 147 | lateinit var subject: TestSubject 148 | 149 | @SetUp fun setup() { 150 | subject = TestSubject() 151 | } 152 | 153 | @Test fun test() { 154 | subject.method() 155 | } 156 | } 157 | ``` 158 | 159 | 这个修饰符只能够被用在类的 `var` 类型的可变属性定义中,不能用在构造方法中.并且属性不能有自定义的 getter 和 setter访问器.这个属性的类型必须是非空的,同样也不能为一个基本类型. 160 | 161 | 在一个`lateinit`的属性初始化前访问他,会导致一个特定异常,告诉你访问的时候值还没有初始化. 162 | 163 | ### 复写属性 164 | 参看[复写成员](http://kotlinlang.org/docs/reference/classes.html#overriding-members) 165 | 166 | ### 代理属性 167 | 最常见的属性就是从备用属性中读(或者写)。另一方面,自定义的 getter 和 setter 可以实现属性的任何操作。有些像懒值( lazy values ),根据给定的关键字从 map 中读出,读取数据库,通知一个监听者等等,像这些操作介于 getter setter 模式之间。 168 | 169 | 像这样常用操作可以通过代理属性作为库来实现。更多请参看[这里](http://kotlinlang.org/docs/reference/delegated-properties.html)。 170 | -------------------------------------------------------------------------------- /ClassesAndObjects/InlineClasses.md: -------------------------------------------------------------------------------- 1 | ## 内联类 2 | 3 | > 内联类在 kotlin1.3 开始支持,并且目前还是*实验性*的。参看下文 4 | 5 | 有时候会为了某些业务逻辑而对某些类型进行包装。然而由于额外的堆分配操作,这会给运行时带来性能损耗。除此之外,如果包装类型是原始类型,性能损耗尤为可怕,因为原始类型通常会经由运行时进行大幅度优化,然而这些包装器并不会享受任何特殊待遇。 6 | 7 | 为了解决这些问题,Kotlin引入了一种称为内联类的特殊类,它通过在类的名称前面放置一个 inline 关键字来声明: 8 | 9 | ```kotlin 10 | inline class Password(val value: String) 11 | ``` 12 | 13 | 内联类必须在主构造函数中初始化唯一属性。在运行时,将使用此单个属性表示内联类的实例(请参阅下面有关运行时表示的详细信息): 14 | 15 | ```kotlin 16 | // 并没有 Password 实例 17 | // 在运行时,'securePassword' 只包含'String' 18 | val securePassword = Password("Don't try this in production") 19 | ``` 20 | 21 | 这是内联类的主要特性。之所以有命名为内联,是因为类的数据内联在它使用处(与内联函数概念类似) 22 | 23 | ###成员 24 | 25 | 内联类也支持一些普通类的功能。尤其是可以声明属性和函数: 26 | 27 | ```kotlin 28 | inline class Name(val s: String) { 29 | val length: Int 30 | get() = s.length 31 | 32 | fun greet() { 33 | println("Hello, $s") 34 | } 35 | } 36 | 37 | fun main() { 38 | val name = Name("Kotlin") 39 | name.greet() // method `greet` is called as a static method 40 | println(name.length) // property getter is called as a static method 41 | } 42 | ``` 43 | 44 | 当然内联类成员也有些其它限制: 45 | 46 | * 内联类不能有 init 块 47 | * 内联类不能有 inner 类 48 | * 内联类属性不能有备用字段(backing fields) 49 | * 内联类只允许有简单的可计算属性(不能含有延迟初始化/代理属性) 50 | 51 | ### 继承 52 | 53 | 内联类允许继承接口: 54 | 55 | ```kotlin 56 | interface Printable { 57 | fun prettyPrint(): String 58 | } 59 | 60 | inline class Name(val s: String) : Printable { 61 | override fun prettyPrint(): String = "Let's $s!" 62 | } 63 | 64 | fun main() { 65 | val name = Name("Kotlin") 66 | println(name.prettyPrint()) // Still called as a static method 67 | } 68 | ``` 69 | 70 | 禁止内联类进行类关系继承。这意味着内联函数不能继承其它类而且必须是 final 71 | 72 | ### 表示 73 | 74 | 在生成的代码中,kotlin 编译器会为每个内联类保留一个包装器。内联类实例在运行时既可以表示为包装器也可以表示为基础类型。这和 `Int` 既可以表示为基础类型`int`也可以表示为包装器 `Integer`。 75 | 76 | kotlin 编译器会更加倾向于使用基础类型而不是包装器,这样可以提高性能并优化代码。然而有时保留包装器也是很有必要的。一般来说,只要将内联类用作另一种类型,它们就会被装箱。 77 | 78 | ```kotlin 79 | interface I 80 | 81 | inline class Foo(val i: Int) : I 82 | 83 | fun asInline(f: Foo) {} 84 | fun asGeneric(x: T) {} 85 | fun asInterface(i: I) {} 86 | fun asNullable(i: Foo?) {} 87 | 88 | fun id(x: T): T = x 89 | 90 | fun main() { 91 | val f = Foo(42) 92 | 93 | asInline(f) // unboxed: used as Foo itself 94 | asGeneric(f) // boxed: used as generic type T 95 | asInterface(f) // boxed: used as type I 96 | asNullable(f) // boxed: used as Foo?, which is different from Foo 97 | 98 | // below, 'f' first is boxed (while being passed to 'id') and then unboxed (when returned from 'id') 99 | // In the end, 'c' contains unboxed representation (just '42'), as 'f' 100 | val c = id(f) 101 | } 102 | ``` 103 | 104 | 因为内联类既可以表示为基础类型有可以表示为包装器,引用相等对于内联类而言毫无意义,因而也禁止此项操作。 105 | 106 | ### 类名重排 107 | 108 | 由于内联类被编译为其基础类型,因此可能会带来各种模糊的错误,例如意想不到的平台签名冲突: 109 | 110 | ```kotlin 111 | inline class UInt(val x: Int) 112 | 113 | // Represented as 'public final void compute(int x)' on the JVM 114 | fun compute(x: Int) { } 115 | 116 | // Also represented as 'public final void compute(int x)' on the JVM! 117 | fun compute(x: UInt) { } 118 | ``` 119 | 120 | 为了缓解这种问题,一般会通过在函数名拼接一段哈希值重命名函数。 `fun compute(x: UInt)` 将会被表示为 `public final void compute-(int x)`,以此来解决冲突的问题。 121 | 122 | > 请注意在 Java 中 `-` 是一个 *无效的* 符号,也就是说在 Java 中不能调用使用内联类作为形参的函数。 123 | 124 | ### 内联类 vs 类型别名 125 | 126 | 乍一看,内联类似乎与类型别名非常相似,两者似乎都引入了一种新的类型,并且都在运行时表示为基础类型。 127 | 128 | 然而,关键的区别在于类型别名与其基础类型(以及具有相同基础类型的其他类型别名)是 *赋值兼容* 的,而内联类却不是这样。 129 | 130 | 换句话说,内联类真正引入了新类型,而类型别名仅仅是为现有的类型取了个新的替代名称(别名): 131 | 132 | ```kotlin 133 | typealias NameTypeAlias = String 134 | inline class NameInlineClass(val s: String) 135 | 136 | fun acceptString(s: String) {} 137 | fun acceptNameTypeAlias(n: NameTypeAlias) {} 138 | fun acceptNameInlineClass(p: NameInlineClass) {} 139 | 140 | fun main() { 141 | val nameAlias: NameTypeAlias = "" 142 | val nameInlineClass: NameInlineClass = NameInlineClass("") 143 | val string: String = "" 144 | 145 | acceptString(nameAlias) // 正确: 传递别名类型的实参替代函数中基础类型的形参 146 | acceptString(nameInlineClass) // 错误: 不能传递内联类的实参替代函数中基础类型的形参 147 | 148 | // And vice versa: 149 | acceptNameTypeAlias("") // 正确: 传递基础类型的实参替代函数中别名类型的形参 150 | acceptNameInlineClass("") // 错误: 不能传递基础类型的实参替代函数中内联类类型的形参 151 | } 152 | ``` 153 | 154 | 155 | 156 | 157 | ### 内联类的实验性状态 158 | 159 | 内联类的设计目前是实验性的,这就是说此特性是正在 *快速变化*的,并且不保证其兼容性。在 Kotlin 1.3+ 中使用内联类时,将会得到一个警告,来表明此特性还是实验性的。 160 | 161 | 要想移除警告,可以对 `kotlinc` 指定 `-XXLanguage:+InlineClasses`参数来选择使用该实验性的特性。 162 | 163 | ### 在 Gradle 中启用内联类: 164 | 165 | ``` groovy 166 | compileKotlin { 167 | kotlinOptions.freeCompilerArgs += ["-XXLanguage:+InlineClasses"] 168 | } 169 | ``` 170 | 171 | ### 在 Maven 中启用内联类 172 | 173 | ```xml 174 | 175 | 176 | -XXLanguage:+InlineClasses 177 | 178 | 179 | ``` 180 | 181 | ## 进一步讨论 182 | 183 | 关于其他技术详细信息和讨论,请参见[内联类的语言提议](https://github.com/Kotlin/KEEP/blob/master/proposals/inline-classes.md) -------------------------------------------------------------------------------- /FunctionsAndLambdas/Higher-OrderFunctionsAndLambdas.md: -------------------------------------------------------------------------------- 1 | ## 高阶函数与 lambda 表达式 2 | ### 高阶函数 3 | 高阶函数就是可以接受函数作为参数或者返回一个函数的函数。比如 `lock()` 就是一个很好的例子,它接收一个 lock 对象和一个函数,运行函数并释放 lock; 4 | 5 | ```kotlin 6 | fun lock(lock: Lock, body: () -> T ) : T { 7 | lock.lock() 8 | try { 9 | return body() 10 | } 11 | finally { 12 | lock.unlock() 13 | } 14 | } 15 | ``` 16 | 17 | 现在解释一下上面的代码吧:`body` 有一个函数类型 `() -> T`,把它设想为没有参数并返回 T 类型的函数。它引发了内部的 try 函数块,并被 `lock` 保护,结果是通过 `lock()` 函数返回的。 18 | 19 | 如果我们想调用 `lock()` ,函数,我们可以传给它另一个函数做参数,参看[函数引用](http://kotlinlang.org/docs/reference/reflection.html#function-references): 20 | 21 | ```kotlin 22 | fun toBeSynchroized() = sharedResource.operation() 23 | 24 | val result = lock(lock, ::toBeSynchroized) 25 | ``` 26 | 27 | 其实最方便的办法是传递一个字面函数(通常是 lambda 表达式): 28 | 29 | ```kotlin 30 | val result = lock(lock, { 31 | sharedResource.operation() }) 32 | ``` 33 | 34 | 字面函数经常描述有更多[细节](http://kotlinlang.org/docs/reference/lambdas.html#function-literals-and-function-expressions),但为了继续本节,我们看一下更简单的预览吧: 35 | 36 | > 字面函数被包在大括号里 37 | 38 | > 参数在 `->` 前面声明(参数类型可以省略) 39 | 40 | > 函数体在 `->` 之后 41 | 42 | 在 kotlin 中有一个约定,如果某一个函数的最后一个参数是函数,并且你向那个位置传递了一个 lambda 表达式,那么,你可以在括号外面定义这个 lambda 表达式: 43 | 44 | ```kotlin 45 | lock (lock) { 46 | sharedResource.operation() 47 | } 48 | ``` 49 | 50 | 最后一个高阶函数的例子是 `map()` (of MapReduce): 51 | 52 | ```kotlin 53 | fun List.map(transform: (T) -> R): 54 | List { 55 | val result = arrayListOf() 56 | for (item in this) 57 | result.add(transform(item)) 58 | return result 59 | } 60 | ``` 61 | 62 | 函数可以通过下面的方式调用 63 | 64 | ```kotlin 65 | val doubled = ints.map {it -> it * 2} 66 | ``` 67 | 68 | 如果字面函数只有一个参数,则声明可以省略,名字就是 `it` : 69 | 70 | ```kotlin 71 | ints.map {it * 2} 72 | ``` 73 | 74 | 这样就可以写[LINQ-风格](http://msdn.microsoft.com/en-us/library/bb308959.aspx)的代码了: 75 | 76 | ```kotlin 77 | strings.filter{ it.length == 5 }.sortedBy{ it }.map{ it.toUpperCase() } 78 | ``` 79 | 80 | ### 内联函数 81 | 有些时候可以用 [内联函数](https://github.com/huanglizhuo/kotlin-in-chinese/blob/master/FunctionsAndLambdas/InlineFunctions.md) 提高高阶函数的性能。 82 | 83 | ### 字面函数和函数表达式 84 | 字面函数或函数表达式就是一个 "匿名函数",也就是没有声明的函数,但立即作为表达式传递下去。想想下面的例子: 85 | 86 | ```kotlin 87 | max(strings, {a, b -> a.length < b.length }) 88 | ``` 89 | `max` 函数就是一个高阶函数,它接受函数作为第二个参数。第二个参数是一个表达式所以本生就是一个函数,即字面函数。作为一个函数,相当于: 90 | 91 | ```kotlin 92 | fun compare(a: String, b: String) : Boolean = a.length < b.length 93 | ``` 94 | 95 | ### 函数类型 96 | 一个函数要接受另一个函数作为参数,我们得给它指定一个类型。比如上面的 `max` 定义是这样的: 97 | 98 | ```kotlin 99 | fun max(collection: Collection, less: (T, T) -> Boolean): T? { 100 | var max: T? = null 101 | for (it in collection) 102 | if (max == null || less(max!!, it)) 103 | max = it 104 | return max 105 | } 106 | ``` 107 | 108 | 参数 `less` 是 `(T, T) -> Boolean`类型,也就是接受俩个 `T` 类型参数返回一个 `Boolean`:如果第一个参数小于第二个则返回真。 109 | 110 | 在函数体第四行, `less` 是用作函数 111 | 112 | 一个函数类型可以像上面那样写,也可有命名参数,更多参看[命名参数](http://kotlinlang.org/docs/reference/functions.html#named-arguments) 113 | 114 | ```kotlin 115 | val compare: (x: T,y: T) -> Int = ... 116 | ``` 117 | 118 | ### 函数文本语法 119 | 函数文本的完全写法是下面这样的: 120 | 121 | ```kotlin 122 | val sum = {x: Int,y: Int -> x + y} 123 | ``` 124 | 125 | 函数文本总是在大括号里包裹着,在完全语法中参数声明是在括号内,类型注解是可选的,函数体是在 `->` 之后,像下面这样: 126 | 127 | ```kotlin 128 | val sum: (Int, Int) -> Int = {x, y -> x+y } 129 | ``` 130 | 131 | 函数文本有时只有一个参数。如果 kotlin 可以从它本生计算出签名,那么可以省略这个唯一的参数,并会通过 `it` 隐式的声明它: 132 | 133 | ```kotlin 134 | ints.filter {it > 0}//这是 (it: Int) -> Boolean 的字面意思 135 | ``` 136 | 137 | 注意如果一个函数接受另一个函数做为最后一个参数,该函数文本参数可以在括号内的参数列表外的传递。参看 [callSuffix](http://kotlinlang.org/docs/reference/grammar.html#call-suffix) 138 | 139 | ### 函数表达式 140 | 上面没有讲到可以指定返回值的函数。在大多数情形中,这是不必要的,因为返回值是可以自动推断的。然而,如果你需要自己指定,可以用函数表达式来做: 141 | 142 | ```kotlin 143 | fun(x: Int, y: Int ): Int = x + y 144 | ``` 145 | 146 | 函数表达式很像普通的函数声明,除了省略了函数名。它的函数体可以是一个表达式(像上面那样)或者是一个块: 147 | 148 | ```kotlin 149 | fun(x: Int, y: Int): Int { 150 | return x + y 151 | } 152 | ``` 153 | 154 | 参数以及返回值和普通函数是一样的,如果它们可以从上下文推断出参数类型,则参数可以省略: 155 | 156 | ```kotlin 157 | ints.filter(fun(item) = item > 0) 158 | ``` 159 | 160 | 返回值类型的推导和普通函数一样:函数返回值是通过表达式自动推断并被明确声明 161 | 162 | 注意函数表达式的参数总是在括号里传递的。 The shorthand syntax allowing to leave the function outside the parentheses works only for function literals. 163 | 164 | 字面函数和表达式函数的另一个区别是没有本地返回。没有 lable 的返回总是返回到 fun 关键字所声明的地方。这意味着字面函数内的返回会返回到一个闭合函数,而表达式函数会返回到函数表达式自身。 165 | 166 | ### 闭包 167 | 一个字面函数或者表达式函数可以访问闭包,即访问自身范围外的声明的变量。不像 java 那样在闭包中的变量可以被捕获修改: 168 | 169 | ```kotlin 170 | var sum = 0 171 | 172 | ints.filter{it > 0}.forEach { 173 | sum += it 174 | } 175 | print(sum) 176 | ``` 177 | 178 | ### 函数表达式扩展 179 | 除了普通的功能,kotlin 支持扩展函数。这种方式对于字面函数和表达式函数都是适用的。它们最重要的使用是在 [Type-safe Groovy-style builders](http://kotlinlang.org/docs/reference/type-safe-builders.html)。 180 | 181 | 表达式函数的扩展和普通的区别是它有接收类型的规范。 182 | 183 | ```kotlin 184 | val sum = fun Int.(other: Int): Int = this + other 185 | ``` 186 | 187 | 接收类型必须在表达式函数中明确指定,但字面函数不用。字面函数可以作为扩展函数表达式,但只有接收类型可以通过上下文推断出来。 188 | 189 | 表达式函数的扩展类型是一个带接收者的函数: 190 | 191 | ```kotlin 192 | sum : Int.(other: Int) -> Int 193 | ``` 194 | 可以用 . 来使用这样的函数: 195 | 196 | ```kotlin 197 | 1.sum(2) 198 | ``` 199 | -------------------------------------------------------------------------------- /GettingStarted/Idioms.md: -------------------------------------------------------------------------------- 1 | [原文](http://kotlinlang.org/docs/reference/idioms.html) 2 | 3 | ## 习语 4 | 5 | 这里是一些在 Kotlin 中经常使用的习语。如果你有特别喜欢的习语想要贡献出来,赶快发起 pull request 吧。 6 | 7 | ### 创建DTOs(POJOs/POCOs) 数据类 8 | 9 | ```kotlin 10 | data class Customer(val name: String, val email: String) 11 | ``` 12 | 13 | 创建 Customer 类并带有如下方法: 14 | 15 | > --为所有属性添加 getters ,如果为 var 类型同时添加 setters 16 | > --`equals()` 17 | > --`hashCode()` 18 | > --`toString()` 19 | > --`copy()` 20 | > --`component1()` , `component1()` , ... 参看[数据类](../ClassesAndObjects/Data-Classes.md) 21 | 22 | ### 函数默认参数 23 | 24 | ```kotlin 25 | fun foo(a: Int = 0, b: String = "") { ... } 26 | ``` 27 | 28 | ### 过滤 list 29 | ```kotlin 30 | val positives = list.filter { x -> x > 0 } 31 | ``` 32 | 或者更短: 33 | 34 | ```kotlin 35 | val positives = list.filter { it > 0 } 36 | ``` 37 | 38 | ### 字符串插值 39 | ```kotlin 40 | println("Name $name") 41 | ``` 42 | 43 | ### 实例检查 44 | ```kotlin 45 | when (x) { 46 | is Foo -> ... 47 | is Bar -> ... 48 | else -> ... 49 | } 50 | ``` 51 | 52 | ### 遍历 map/list 键值对 53 | ```kotlin 54 | for ((k, v) in map) { 55 | print("$k -> $v") 56 | } 57 | ``` 58 | k,v 可以随便命名 59 | 60 | ### 使用 ranges 61 | ```kotlin 62 | for (i in 1..100) { ... } // 闭区间: 包括100 63 | for (i in 1 until 100) { ... } // 半开区间: 不包括100 64 | for (x in 2..10 step 2) { ... } 65 | for (x in 10 downTo 1) { ... } 66 | if (x in 1..10) { ... } 67 | ``` 68 | 69 | ### 只读 list 70 | ```kotlin 71 | val list = listOf("a", "b", "c") 72 | ``` 73 | 74 | ### 只读map 75 | ```kotlin 76 | val map = mapOf("a" to 1, "b" to 2, "c" to 3) 77 | ``` 78 | 79 | ### 访问 map 80 | ```kotlin 81 | println(map["key"]) 82 | map["key"] = value 83 | ``` 84 | 85 | ### 懒属性(延迟加载) 86 | ```kotlin 87 | val p: String by lazy { 88 | // compute the string 89 | } 90 | ``` 91 | 92 | ### 扩展函数 93 | ```kotlin 94 | fun String.spcaceToCamelCase() { ... } 95 | "Convert this to camelcase".spcaceToCamelCase() 96 | ``` 97 | 98 | ### 创建单例模式 99 | ```kotlin 100 | object Resource { 101 | val name = "Name" 102 | } 103 | ``` 104 | 105 | ### 如果不为空则... 的简写 106 | ```kotlin 107 | val files = File("Test").listFiles() 108 | println(files?.size) 109 | ``` 110 | 111 | ### 如果不为空...否则... 的简写 112 | ```kotlin 113 | val files = File("test").listFiles() 114 | println(files?.size ?: "empty") 115 | ``` 116 | 117 | ### 如果声明为空执行某操作 118 | ```kotlin 119 | val data = ... 120 | val email = data["email"] ?: throw IllegalStateException("Email is missing!") 121 | ``` 122 | 123 | ### 如果不为空执行某操作 124 | ```kotlin 125 | val date = ... 126 | data?.let{ 127 | ...//如果不为空执行该语句块 128 | } 129 | ``` 130 | 131 | ### 如果不空则映射(Map nullable value if not null) 132 | 133 | ```kotlin 134 | val data = ... 135 | 136 | val mapped = data?.let { transformData(it) } ?: defaultValueIfDataIsNull 137 | ``` 138 | 139 | 140 | 141 | ### 返回 when 判断 142 | 143 | ```kotlin 144 | fun transform(color: String): Int { 145 | return when (color) { 146 | "Red" -> 0 147 | "Green" -> 1 148 | "Blue" -> 2 149 | else -> throw IllegalArgumentException("Invalid color param value") 150 | } 151 | } 152 | ``` 153 | 154 | ### try-catch 表达式 155 | ```kotlin 156 | fun test() { 157 | val result = try { 158 | count() 159 | } catch (e: ArithmeticException) { 160 | throw IllegalStateException(e) 161 | } 162 | 163 | // Working with result 164 | } 165 | ``` 166 | ### if 表达式 167 | ```kotlin 168 | fun foo(param: Int) { 169 | val result = if (param == 1) { 170 | "one" 171 | } else if (param == 2) { 172 | "two" 173 | } else { 174 | "three" 175 | } 176 | } 177 | ``` 178 | 179 | ### 使用生成器模式返回 Unit 180 | ```kotlin 181 | fun arrayOfMinusOnes(size: Int): IntArray { 182 | return IntArray(size).apply { fill(-1) } 183 | } 184 | ``` 185 | 186 | ### 单表达式函数 187 | ```kotlin 188 | fun theAnswer() = 42 189 | ``` 190 | 与下面的语句是等效的 191 | 192 | ```kotlin 193 | fun theAnswer(): Int { 194 | return 42 195 | } 196 | ``` 197 | 可以和其它习语组合成高效简洁的代码。譬如说 when 表达式: 198 | 199 | ```kotlin 200 | fun transform(color: String): Int = when (color) { 201 | "Red" -> 0 202 | "Green" -> 1 203 | "Blue" -> 2 204 | else -> throw IllegalArgumentException("Invalid color param value") 205 | } 206 | ``` 207 | ### 利用 with 调用一个对象实例的多个方法 208 | ```kotlin 209 | class Turtle { 210 | fun penDown() 211 | fun penUp() 212 | fun turn(degrees: Double) 213 | fun forward(pixels: Double) 214 | } 215 | 216 | val myTurtle = Turtle() 217 | with(myTurtle) { //draw a 100 pix square 218 | penDown() 219 | for(i in 1..4) { 220 | forward(100.0) 221 | turn(90.0) 222 | } 223 | penUp() 224 | } 225 | ``` 226 | 227 | ### Java 7’s try with resources 228 | ```kotlin 229 | val stream = Files.newInputStream(Paths.get("/some/file.txt")) 230 | stream.buffered().reader().use { reader -> 231 | println(reader.readText()) 232 | } 233 | ``` 234 | 235 | ### 需要泛型类型信息的泛型函数的简便形式 236 | ```kotlin 237 | // public final class Gson { 238 | // ... 239 | // public T fromJson(JsonElement json, Class classOfT) throws JsonSyntaxException { 240 | // ... 241 | 242 | inline fun Gson.fromJson(json): T = this.fromJson(json, T::class.java) 243 | ``` 244 | 245 | ### 消费一个可能为空的布尔值 246 | ```kotlin 247 | val b: Boolean? = ... 248 | if (b == true) { 249 | ... 250 | } else { 251 | // `b` is false or null 252 | } 253 | ``` 254 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Analytics](https://ga-beacon.appspot.com/UA-80536214-1/readme)](https://github.com/huanglizhuo/kotlin-in-chinese) 2 | 3 | * [准备开始](GettingStarted/README.md) 4 | * [基本语法](GettingStarted/Basic-Syntax.md) 5 | * [习惯用语](GettingStarted/Idioms.md) 6 | * [编码风格](GettingStarted/Coding-Conventions.md) 7 | 8 | * [基础](Basics/README.md) 9 | * [基本类型](Basics/Basic-Types.md) 10 | * [包](Basics/Packages.md) 11 | * [控制流](Basics/Control-Flow.md) 12 | * [返回与跳转](Basics/Returns-and-Jumps.md) 13 | 14 | * [类和对象](ClassesAndObjects/README.md) 15 | * [类和继承](ClassesAndObjects/Classes-and-Inheritance.md)  16 | * [属性和字段](ClassesAndObjects/Properties-and-Fields.md)  17 | * [接口](ClassesAndObjects/Interfaces.md) 18 | * [可见性修饰词](ClassesAndObjects/Visibility-Modifiers.md) 19 | * [扩展](ClassesAndObjects/Extensions.md) 20 | * [数据对象](ClassesAndObjects/Data-Classes.md) 21 | * [密封类](ClassesAndObjects/SealedClasses.md) 22 | * [泛型](ClassesAndObjects/Generics.md) 23 | * [嵌套类](ClassesAndObjects/NestedClasses.md) 24 | * [枚举类](ClassesAndObjects/EnumClasses.md) 25 | * [对象表达式和声明](ClassesAndObjects/ObjectExpressicAndDeclarations.md) 26 | * [代理模式](ClassesAndObjects/Delegation.md) 27 | * [代理属性](ClassesAndObjects/DelegationProperties.md) 28 | 29 | * [函数和lambda表达式](FunctionsAndLambdas/README.md) 30 | * [函数](FunctionsAndLambdas/Functions.md) 31 | * [高阶函数和lambda表达式](FunctionsAndLambdas/Higher-OrderFunctionsAndLambdas.md) 32 | * [内联函数](FunctionsAndLambdas/InlineFunctions.md) 33 | 34 | * [集合](Collections/README.md) 35 | * [集合概览](Collections/CollectionsOverview.md) 36 | * [结构化集合](Collections/ConstructionCollections.md) 37 | * [迭代器](Collections/Iterators.md) 38 | * [范围和进度](Collections/RangesandProgressions.md) 39 | * [序列](Collections/Squences.md) 40 | * [操作概览](Collections/OperationsOverview.md) 41 | * [转化](Collections/Transformations.md) 42 | * [过滤](Collections/Filtering.md) 43 | * [加减操作符](Collections/PlusandMinusOperators.md) 44 | * [分组](Collections/Grouping.md) 45 | * [取得部分集合](Collections/RetrievingCollectionParts.md) 46 | * [取得单个元素](Collections/RetrivingSingleElements.md) 47 | * [排序](Collections/Ording.md) 48 | * [聚合操作](Collections/AggregateOperations.md) 49 | * [集合写曹锁](Collections/CollectionWriteOperations.md) 50 | * [只针对于 list 的操作](Collections/ListSepcificOperations.md) 51 | * [只针对于 set 的操作](Collections/SetSepcificOperations.md) 52 | * [只针对于 map 的操作](Collections/MapSepcificOperations.md) 53 | 54 | * [协程](Coroutines/README.md) 55 | * [协程指南](Coroutines/CoroutinesGuide.md) 56 | * [基础](Coroutines/Basics.md) 57 | * [取消和超时](Coroutines/CancellationAndTimeouts.md) 58 | * [组合挂起函数](Coroutines/ComposingSuspendingFunctions.md) 59 | * [协程上下文和调度器](Coroutines/CoroutineContextAndDispatchers.md) 60 | * [异步流](Coroutines/AsynchronousFlow.md) 61 | * [频道](Coroutines/Channels.md) 62 | * [异常处理](Coroutines/ExceptionHandling.md) 63 | * [共享可变状态与并发](Coroutines/SharedMutableStateAndConcurrency.md) 64 | * [Select 表达式](Coroutines/SelectExpression.md) 65 | 66 | 67 | 68 | * [更多语言结构](MoreLanguageConstructs/README.md) 69 | * [解构声明](MoreLanguageConstructs/DestructuringDeclarations.md) 70 | * [类型检查和自动转换](MoreLanguageConstructs/Type-Checks-and-Casts.md) 71 | * [This表达式](MoreLanguageConstructs/This-Expression.md) 72 | * [等式](MoreLanguageConstructs/Equality.md) 73 | * [运算符重载](MoreLanguageConstructs/Opetator-overloading.md) 74 | * [空安全](MoreLanguageConstructs/Null-Safety.md) 75 | * [异常](MoreLanguageConstructs/Exceptions.md) 76 | * [注解](MoreLanguageConstructs/Annotations.md) 77 | * [反射](MoreLanguageConstructs/Reflection.md) 78 | * [作用域函数](MoreLanguageConstructs/ScopeFunctions.md) 79 | * [类型安全构造器](MoreLanguageConstructs/Type-SafeBuilders.md) 80 | * [试验性 API 标注](MoreLanguageConstructs/ExperimentalAPIMarkers.md) 81 | 82 | 83 | 84 | * [参考](Reference/README.md) 85 | * [API](Reference/API-Reference.md) 86 | * [语法](Reference/Grammar.md) 87 | * [互用性](Interop/README.md) 88 | * [与 java 交互](Interop/Java-Interop.md) 89 | 90 | * [工具](Tools/README.md) 91 | * [Kotlin代码文档](Tools/Documenting-Kotlin-Code.md) 92 | * [使用Maven](Tools/Using-Maven.md) 93 | * [使用Ant](Tools/Using-Ant.md) 94 | * [使用Griffon](Tools/Using-Griffon.md) 95 | * [使用Gradle](Tools/Using-Gradle.md)  96 | 97 | * [FAQ](FAQ/README.md) 98 | * [与java对比](FAQ/Comparison2java.md) 99 | * [与Scala对比](FAQ/Comparison2Scala.md) 100 | 101 | 102 | #### Change log 103 | 104 | 2020.1.30 105 | 106 | 开始同步 Kotlin 1.3.61 文档 107 | 108 | 2017.5.17 109 | 110 | Android Announces Support for Kotlin 111 | 112 | By Mike Cleron, Director, Android Platform 113 | 114 | Google 宣布官方支持 Kotlin [android-developers.googleblog](https://android-developers.googleblog.com/2017/05/android-announces-support-for-kotlin.html) 115 | 116 | Android Studio 3.0 将默认集成 Kotlin plug-in ,博客中还说到 Kotlin 有着出色的设计,并相信 Kotlin 会帮助开发者更快更好的开发 Android 应用 117 | 118 | Expedia, Flipboard, Pinterest, Square 等公司都有在自家的项目中使用 Kotlin 119 | 120 | 2017.3.8 121 | 122 | Kotlin 1.1 正式发布,这次最令人振奋的莫过于协程的发布,有了协程就可以更优雅的完成异步编程了 123 | 124 | 更多新特性请参看[what's new in kotlin 1.1](https://kotlinlang.org/docs/reference/whatsnew11.html) 125 | 126 | [pdf下载](https://www.gitbook.com/download/pdf/book/huanglizhuo/kotlin-in-chinese) [ePub下载](https://www.gitbook.com/download/epub/book/huanglizhuo/kotlin-in-chinese) 127 | 128 | 记得要点 star star star 129 | 130 | 发现有翻译的不好的或者错误欢迎到 github 提 [issue](https://github.com/huanglizhuo/kotlin-in-chinese/issues/new) 131 | 132 | ## 号外 号外 Kotlin 1.0 正式发布 133 | Android 世界的 Swift 终于发布1.0版本 134 | 135 | Kotlin 是一个实用性很强的语言,专注于互通,安全,简洁,工具健全... 136 | 137 | 无缝支持 Java+Kotlin 项目,可以更少的使用样版代码,确保类型安全。 138 | 139 | [Kotlin 1.0 更新日志](http://blog.jetbrains.com/kotlin/2016/02/kotlin-1-0-released-pragmatic-language-for-jvm-and-android/) 140 | 141 | 还换了logo :) 142 | 143 | Kotlin LOC (软件规模代码行) 如下图 144 | 145 | ![kotlin](./kotlinLOC.png) -------------------------------------------------------------------------------- /FunctionsAndLambdas/Functions.md: -------------------------------------------------------------------------------- 1 | ## 函数 2 | ### 函数声明 3 | 在 kotlin 中用关键字 `fun` 声明函数: 4 | 5 | ```kotlin 6 | fun double(x: Int): Int { 7 | 8 | } 9 | ``` 10 | 11 | ### 函数用法 12 | 通过传统的方法调用函数 13 | 14 | ```kotlin 15 | val result = double(2) 16 | ``` 17 | 18 | 通过`.`调用成员函数 19 | 20 | ```kotlin 21 | Sample().foo() // 创建Sample类的实例,调用foo方法 22 | ``` 23 | 24 | ### 中缀符号 25 | 在满足以下条件时,函数也可以通过中缀符号进行调用: 26 | 27 | > 它们是成员函数或者是[扩展函数](http://kotlinlang.org/docs/reference/extensions.html) 28 | > 只有一个参数 29 | > 使用`infix`关键词进行标记 30 | 31 | ```kotlin 32 | //给 Int 定义一个扩展方法 33 | infix fun Int.shl(x: Int): Int { 34 | ... 35 | } 36 | 37 | 1 shl 2 //用中缀注解调用扩展函数 38 | 39 | 1.shl(2) 40 | ``` 41 | 42 | ### 参数 43 | 函数参数是用 Pascal 符号定义的 name:type。参数之间用逗号隔开,每个参数必须指明类型。 44 | 45 | ```kotlin 46 | fun powerOf(number: Int, exponent: Int) { 47 | ... 48 | } 49 | ``` 50 | 51 | ### 默认参数 52 | 函数参数可以设置默认值,当参数被忽略时会使用默认值。这样相比其他语言可以减少重载。 53 | 54 | ```kotlin 55 | fun read(b: Array, off: Int = 0, len: Int = b.size ) { 56 | ... 57 | } 58 | ``` 59 | 60 | 默认值可以通过在type类型后使用`=`号进行赋值 61 | 62 | ### 命名参数 63 | 在调用函数时可以参数可以命名。这对于那种有大量参数的函数是很方便的. 64 | 65 | 下面是一个例子: 66 | 67 | ```kotlin 68 | fun reformat(str: String, normalizeCase: Boolean = true,upperCaseFirstLetter: Boolean = true, 69 | divideByCamelHumps: Boolean = false, 70 | wordSeparator: Char = ' ') { 71 | ... 72 | } 73 | ``` 74 | 75 | 我们可以使用默认参数 76 | 77 | >reformat(str) 78 | 79 | 然而当调用非默认参数是就需要像下面这样: 80 | 81 | ```kotlin 82 | reformat(str, true, true, false, '_') 83 | ``` 84 | 85 | 使用命名参数我们可以让代码可读性更强: 86 | 87 | ```kotlin 88 | reformat(str, 89 | normalizeCase = true, 90 | uppercaseFirstLetter = true, 91 | divideByCamelHumps = false, 92 | wordSeparator = '_' 93 | ) 94 | ``` 95 | 96 | 如果不需要全部参数的话可以这样: 97 | 98 | ```kotlin 99 | reformat(str, wordSeparator = '_') 100 | ``` 101 | 102 | 注意,命名参数语法不能够被用于调用Java函数中,因为Java的字节码不能确保方法参数命名的不变性 103 | 104 | ### 不带返回值的参数 105 | 如果函数不会返回任何有用值,那么他的返回类型就是 `Unit` .`Unit` 是一个只有唯一值`Unit`的类型.这个值并不需要被直接返回: 106 | 107 | ```kotlin 108 | fun printHello(name: String?): Unit { 109 | if (name != null) 110 | println("Hello ${name}") 111 | else 112 | println("Hi there!") 113 | // `return Unit` or `return` is optional 114 | } 115 | ``` 116 | 117 | `Unit` 返回值也可以省略,比如下面这样: 118 | 119 | ```kotlin 120 | fun printHello(name: String?) { 121 | ... 122 | } 123 | ``` 124 | ### 单表达式函数 125 | 当函数只返回单个表达式时,大括号可以省略并在 = 后面定义函数体 126 | 127 | ```kotlin 128 | fun double(x: Int): Int = x*2 129 | ``` 130 | 在编译器可以推断出返回值类型的时候,返回值的类型可以省略: 131 | 132 | ```kotlin 133 | fun double(x: Int) = x * 2 134 | 135 | ``` 136 | 137 | ### 明确返回类型 138 | 下面的例子中必须有明确返回类型,除非他是返回 `Unit`类型的值,Kotlin 并不会对函数体重的返回类型进行推断,因为函数体中可能有复杂的控制流,他的返回类型未必对读者可见(甚至对编译器而言也有可能是不可见的): 139 | 140 | ### 变长参数 141 | 函数的参数(通常是最后一个参数)可以用 vararg 修饰符进行标记: 142 | 143 | ```kotlin 144 | fun asList(vararg ts: T): List { 145 | val result = ArrayList() 146 | for (t in ts) 147 | result.add(t) 148 | return result 149 | } 150 | ``` 151 | 152 | 标记后,允许给函数传递可变长度的参数: 153 | 154 | ```kotlin 155 | val list = asList(1, 2, 3) 156 | ``` 157 | 158 | 只有一个参数可以被标注为 `vararg` 。加入`vararg`并不是列表中的最后一个参数,那么后面的参数需要通过命名参数语法进行传值,再或者如果这个参数是函数类型,就需要通过lambda法则. 159 | 160 | 当调用变长参数的函数时,我们可以一个一个的传递参数,比如 `asList(1, 2, 3)`,或者我们要传递一个 array 的内容给函数,我们就可以使用 * 前缀操作符: 161 | 162 | ```kotlin 163 | val a = array(1, 2, 3) 164 | val list = asList(-1, 0, *a, 4) 165 | ``` 166 | 167 | ### 函数范围 168 | Kotlin 中可以在文件顶级声明函数,这就意味者你不用像在Java,C#或是Scala一样创建一个类来持有函数。除了顶级函数,Kotlin 函数可以声明为局部的,作为成员函数或扩展函数。 169 | 170 | #### 局部函数 171 | Kotlin 支持局部函数,比如在一个函数包含另一函数。 172 | 173 | ```kotlin 174 | fun dfs(graph: Graph) { 175 | fun dfs(current: Vertex, visited: Set) { 176 | if (!visited.add(current)) return 177 | for (v in current.neighbors) 178 | dfs(v, visited) 179 | } 180 | 181 | dfs(graph.vertices[0], HashSet()) 182 | } 183 | ``` 184 | 185 | 局部函数可以访问外部函数的局部变量(比如闭包) 186 | 187 | ```kotlin 188 | fun dfs(graph: Graph) { 189 | val visited = HashSet() 190 | fun dfs(current: Vertex) { 191 | if (!visited.add(current)) return 192 | for (v in current.neighbors) 193 | dfs(v) 194 | } 195 | dfs(graph.vertices[0]) 196 | } 197 | ``` 198 | 199 | 局部函数甚至可以返回到外部函数 [qualified return expressions](http://kotlinlang.org/docs/reference/returns.html) 200 | 201 | ```kotlin 202 | fun reachable(from: Vertex, to: Vertex): Boolean { 203 | val visited = HashSet() 204 | fun dfs(current: Vertex) { 205 | if (current == to) return@reachable true 206 | if (!visited.add(current)) return 207 | for (v in current.neighbors) 208 | dfs(v) 209 | } 210 | dfs(from) 211 | return false 212 | } 213 | ``` 214 | 215 | ### 成员函数 216 | 成员函数是定义在一个类或对象里边的 217 | 218 | ```kotlin 219 | class Sample() { 220 | fun foo() { print("Foo") } 221 | } 222 | ``` 223 | 224 | 成员函数可以用 . 的方式调用 225 | 226 | ```kotlin 227 | Sample.foo() 228 | ``` 229 | 230 | 更多请参看[类](http://kotlinlang.org/docs/reference/classes.html)和[继承](http://kotlinlang.org/docs/reference/classes.html#inheritance) 231 | 232 | ### 泛型函数 233 | 函数可以有泛型参数,样式是在函数名前加上尖括号。 234 | 235 | ```kotlin 236 | fun sigletonArray(item: T): Array { 237 | return Array(1, {item}) 238 | } 239 | ``` 240 | 241 | 更多请参看[泛型](http://kotlinlang.org/docs/reference/generics.html) 242 | 243 | ### 内联函数 244 | 参看[这里](http://kotlinlang.org/docs/reference/inline-functions.html) 245 | 246 | ### 扩展函数 247 | 参看[这里](http://kotlinlang.org/docs/reference/extensions.html) 248 | 249 | ### 高阶函数和 lambda 表达式 250 | 参看[这里](http://kotlinlang.org/docs/reference/lambdas.html) 251 | 252 | ### 尾递归函数 253 | Kotlin 支持函数式编程的尾递归。这个允许一些算法可以通过循环而不是递归解决问题,从而避免了栈溢出。当函数被标记为 `tailrec` 时,编译器会优化递归,并用高效迅速的循环代替它。 254 | 255 | ```kotlin 256 | tailrec fun findFixPoint(x: Double = 1.0): Double 257 | = if (x == Math.cos(x)) x else findFixPoint(Math.cos(x)) 258 | ``` 259 | 260 | 这段代码计算的是数学上的余弦不动点。Math.cos 从 1.0 开始不断重复,直到值不变为止,结果是 0.7390851332151607 261 | 这段代码和下面的是等效的: 262 | 263 | ```kotlin 264 | private fun findFixPoint(): Double { 265 | var x = 1.0 266 | while (true) { 267 | val y = Math.cos(x) 268 | if ( x == y ) return y 269 | x = y 270 | } 271 | } 272 | ``` 273 | 274 | 使用 `tailrec` 修饰符必须在最后一个操作中调用自己。在递归调用代码后面是不允许有其它代码的,并且也不可以在 try/catch/finall 块中进行使用。当前的尾递归只在 JVM 的后端中可以用 275 | -------------------------------------------------------------------------------- /MoreLanguageConstructs/Ranges.md: -------------------------------------------------------------------------------- 1 | ## Ranges 2 | range 表达式是通过 rangeTo 函数形成的。rangeTo 函数拥有形如 .. 的操作符,该操作符是用 in 和 !in 实现的。 Range 可以对任何可比较的类型做操作,但对整数基本类型是优化过的。下面是些例子: 3 | 4 | ```kotlin 5 | if (i in 1..10) { 6 | println(i) 7 | } 8 | 9 | if (x !in 1.0..3.0) println(x) 10 | 11 | if (str in "island".."isle") println(str) 12 | ``` 13 | 14 | 数字的范围有个附加的特性:它们可以迭代。编译器会把它转成类似于 java 的 for 循环的形式,且不用担心越界: 15 | 16 | ```kotlin 17 | for (i in 1..4) print(i) // prints "1234" 18 | 19 | for (i in 4..1) print(i) // prints nothing 20 | 21 | for (x in 1.0..2.0) print("$x ") // prints "1.0 2.0 " 22 | ``` 23 | 24 | 如果你想迭代数字并想反过来,这个相当简单,你可以使用 `downTo()` 函数 25 | 26 | ```kotlin 27 | for (i in 4 downTo 1) print(i) 28 | ``` 29 | 30 | 也可以使用指定步数的迭代,这个用到 `step()` 31 | 32 | ```kotlin 33 | for (i in 1..4 step 2) print(i) // prints "13" 34 | 35 | for (i in 4 downTo 1 step 2) print(i) // prints "42" 36 | 37 | for (i in 1.0..2.0 step 0.3) print("$i ") // prints "1.0 1.3 1.6 1.9 " 38 | ``` 39 | 40 | ### 工作原理 41 | 在标准库中有俩种接口:Range 和 Progression 42 | 43 | Range 表示数学范围上的一个间隔。它有俩个端点:start 和 end 。主要的操作符是 contains 通常在 in/!in 操作符内: 44 | 45 | Progression 表示一个算数级数。它有一个 start 和 end 以及一个非零 increment 。Progression 是Iterable 的一个子类,因此可以使用在 for 循环中,或者 map filter 等等。第一个元素是 start 下一个元素都是前一个元素的 increment 。`Progression` 的迭代与 java/javaScript 的 for 循环相同: 46 | 47 | ```kotlin 48 | // if increment > 0 49 | for (int i = start; i <= end; i += increment) { 50 | // ... 51 | } 52 | // if increment < 0 53 | for (int i = start; i >= end; i += increment) { 54 | // ... 55 | } 56 | ``` 57 | 58 | ### 范围指标 59 | 使用例子: 60 | 61 | ```kotlin 62 | // Checking if value of comparable is in range. Optimized for number primitives. 63 | if (i in 1..10) println(i) 64 | 65 | if (x in 1.0..3.0) println(x) 66 | 67 | if (str in "island".."isle") println(str) 68 | 69 | // Iterating over arithmetical progression of numbers. Optimized for number primitives (as indexed for-loop in Java). 70 | for (i in 1..4) print(i) // prints "1234" 71 | 72 | for (i in 4..1) print(i) // prints nothing 73 | 74 | for (i in 4 downTo 1) print(i) // prints "4321" 75 | 76 | for (i in 1..4 step 2) print(i) // prints "13" 77 | 78 | for (i in (1..4).reversed()) print(i) // prints "4321" 79 | 80 | for (i in (1..4).reversed() step 2) print(i) // prints "42" 81 | 82 | for (i in 4 downTo 1 step 2) print(i) // prints "42" 83 | 84 | for (x in 1.0..2.0) print("$x ") // prints "1.0 2.0 " 85 | 86 | for (x in 1.0..2.0 step 0.3) print("$x ") // prints "1.0 1.3 1.6 1.9 " 87 | 88 | for (x in 2.0 downTo 1.0 step 0.3) print("$x ") // prints "2.0 1.7 1.4 1.1 " 89 | 90 | for (str in "island".."isle") println(str) // error: string range cannot be iterated over 91 | ``` 92 | 93 | ### 常见的接口的定义 94 | 有俩种基本接口:`Range` `Progression` 95 | 96 | `Range` 接口定义了一个范围,或者是数学意义上的一个间隔。 97 | 98 | ```kotlin 99 | interface Range> { 100 | val start: T 101 | val end: T 102 | fun contains(Element : T): Boolean 103 | } 104 | ``` 105 | 106 | `Progression` 定义了数学上的级数。包括 start end increment 端点。最大的特点就是它可以迭代,因此它是 Iterable 的子类。end 不是必须的。 107 | 108 | ```kotlin 109 | interface Progression : Iterable { 110 | val start : N 111 | val end : N 112 | val increment : Number 113 | } 114 | ``` 115 | 与 java 的 for 循环类似: 116 | 117 | ```kotlin 118 | // if increment > 0 119 | for (int i = start; i <= end; i += increment) { 120 | // ... 121 | } 122 | 123 | // if increment < 0 124 | for (int i = start; i >= end; i += increment) { 125 | // ... 126 | } 127 | ``` 128 | 129 | ### 类的实现 130 | 为避免不需要的重复,让我们先考虑一个数字类型 `Int` 。其它的数字类型也一样。注意这些类的实例需要用相应的构造函数来创建,使用 rangeTo() downTo() reversed() stop() 实用函数。 131 | 132 | IntProgression 类很直接也很简单: 133 | 134 | ```kotlin 135 | class IntProgression(override val start: Int, override val end: Int, override val increment: Int ): Progression { 136 | override fun iterator(): Iterator = IntProgressionIteratorImpl(start, end, increment) 137 | } 138 | ``` 139 | 140 | `IntRange` 有些狡猾:它实现了 `Progression` `Range` 接口,因为它天生以通过 range 迭代(默认增加值是 1 ): 141 | 142 | ```kotlin 143 | class IntRange(override val start: Int, override val end: Int): Range, Progression { 144 | override val increment: Int 145 | get() = 1 146 | override fun contains(element: Int): Boolean = start <= element && element <= end 147 | override fun iterator(): Iterator = IntProgressionIteratorImpl(start, end, increment) 148 | } 149 | ``` 150 | 151 | `ComparableRange` 也很简单: 152 | 153 | ```kotlin 154 | class ComparableRange>(override val start: T, override val end: T): Range { 155 | override fun contains(element: T): Boolean = start <= element && element <= end 156 | } 157 | ``` 158 | 159 | ### 一些实用的函数 160 | **rangeTo()** 161 | 162 | `rangeTo()` 函数仅仅是调用 *Range 的构造函数,比如: 163 | 164 | ```kotlin 165 | class Int { 166 | fun rangeTo(other: Byte): IntRange = IntRange(this, Other) 167 | fun rangeTo(other: Int): IntRange = IntRange(this, other) 168 | } 169 | ``` 170 | 171 | **downTo()** 172 | 173 | `downTo()` 扩展函数可以为任何数字类型定义,这里有俩个例子: 174 | 175 | ```kotlin 176 | fun Long.downTo(other: Double): DoubleProgression { 177 | return DoubleProgression(this, other, -1.0) 178 | } 179 | 180 | fun Byte.downTo(other: Int): IntProgression { 181 | return IntProgression(this, other, -1) 182 | } 183 | ``` 184 | 185 | **reversed()** 186 | 187 | `reversed()` 扩展函数是给所有的 `*Range`和`*Progression` 类定义的,并且它们都返回反向的级数。 188 | 189 | ```kotlin 190 | fun IntProgression.reversed(): IntProgression { 191 | return IntProgression(end, start, -increment) 192 | } 193 | 194 | fun IntRange.reversed(): IntProgression { 195 | return IntProgression(end, start, -1) 196 | } 197 | ``` 198 | **step()** 199 | 200 | `step()` 扩展函数是给所有的 `*Range`和`*Progression` 类定义的,所有的返回级数都修改了 step 值。注意 step 值总是正的,否则函数不会改变迭代的方向。 201 | 202 | ```kotlin 203 | fun IntProgression.step(step: Int): IntProgression { 204 | if (step <= 0) throw IllegalArgumentException("Step must be positive, was: $step") 205 | return IntProgression(start, end, if (increment > 0) step else -step) 206 | } 207 | 208 | fun IntRange.step(step: Int): IntProgression { 209 | if (step <= 0) throw IllegalArgumentException("Step must be positive, was: $step") 210 | return IntProgression(start, end, step) 211 | } 212 | ``` 213 | -------------------------------------------------------------------------------- /Basics/Basic-Types.md: -------------------------------------------------------------------------------- 1 | ## 基本类型 2 | 在 Kotlin 中,所有的东西都是对象,这就意味着我们可以调用任何变量的成员函数和属性。一些类型是内建的,它们的实现是优化过的,但对用户来说它们就像普通的类一样。在这节中,我们将会讲到大多数的类型:数值,字符,布尔,以及数组。 3 | 4 | ### 数值 5 | Kotlin 处理数值的方法和 java 很相似,但不是完全一样。比如,不存在隐式转换数值的精度,并且在字面上有一些小小的不同。 6 | 7 | Kotlin 提供了如下内建数值类型(和 java 很相似): 8 | 9 | 10 | | **类型** | **位宽** | 11 | | ------ | :----: | 12 | | Double | 64 | 13 | | Float | 32 | 14 | | Long | 64 | 15 | | Int | 32 | 16 | | Short | 16 | 17 | | Byte | 8 | 18 | 19 | 注意字符在 Kotlin 中不是数值类型 20 | 21 | ### 字面值常量 22 | 主要是以下几种字面值常量: 23 | 24 | > --十进制数值: `123` 25 | > --长整型要加大写 `L` : `123L` 26 | > --16进制:`0x0f` 27 | > --二进制:`0b00001011` 28 | 29 | 注意不支持8进制 30 | 31 | Kotlin 也支持传统的浮点数表示: 32 | 33 | > -- 默认双精度浮点数(Double) : `123.5` , `123.5e10` 34 | > -- 单精度浮点数(Float)要添加 `f` 或 `F` :123.5f 35 | 36 | ### 数值常量中可以添加下划线分割(1.1版本新特性) 37 | 您可以使用下划线增加数值常量的可读性: 38 | 39 | ```kotlin 40 | val oneMillion = 1_000_000 41 | val creditCardNumber = 1234_5678_9012_3456L 42 | val socialSecurityNumber = 999_99_9999L 43 | val hexBytes = 0xFF_EC_DE_5E 44 | val bytes = 0b11010010_01101001_10010100_10010010 45 | ``` 46 | 47 | ### 表示 48 | 在 java 平台上,数值被 JVM 虚拟机以字节码的方式物理存储的,除非我们需要做可空标识(比如说 Int?) 或者涉及泛型。在后者中数值是装箱过的。 49 | 50 | 注意装箱过的数值是不保留特征的: 51 | 52 | ```kotlin 53 | val a: Int = 10000 54 | print (a === a ) // 打印 'true' 55 | val boxedA: Int? =a 56 | val anotherBoxedA: Int? = a 57 | print (boxedA === anotherBoxedA ) // 注意这里打印的是 'false' 58 | ``` 59 | 60 | 然而,它们是值相等的: 61 | 62 | ```kotlin 63 | val a: Int = 10000 64 | print(a == a) // 打印 'true' 65 | val boxedA: Int? = a 66 | val anotherBoxedA: Int? = a 67 | print(boxedA == anotherBoxedA) // 打印 'true' 68 | ``` 69 | 70 | ### 显式转换 71 | 由于不同的表示,低精度类型不是高精度类型的子类型。如果是的话我们就会碰到下面这样的麻烦了 72 | 73 | ```kotlin 74 | // 这是些伪代码,不能编译的 75 | val a: Int? =1 // 一个装箱过的 Int (java.lang.Integer) 76 | val b: Long? = a // 一个隐式装箱的 Long (java.lang.Long) 77 | print( a == b )// 很惊讶吧 这次打印出的是 'false' 这是由于 Long 类型的 equals() 只有和 Long 比较才会相同 78 | ``` 79 | 80 | 因此不止是特征会丢失,有时候连值相等都会悄悄失效。 81 | 82 | 所以,低精度类型是不会隐式转换为高精度类型的。这意味着我们必须显式转换才能把 `Byte` 赋值给 `Int` 83 | 84 | ```kotlin 85 | val b: Byte = 1 // OK, 字面值常量会被静态检查 86 | val i: Int = b // ERROR 87 | ``` 88 | 89 | 我们可以通过显式转换把数值类型提升 90 | 91 | ```kotlin 92 | val i: Int = b.toInt() // 显式转换 93 | ``` 94 | 95 | 每个数值类型都支持下面的转换: 96 | 97 | > ` toByte(): Byte` 98 | 99 | > `toShort(): Short` 100 | 101 | > ` toInt(): Int` 102 | 103 | > ` toLong(): Long` 104 | 105 | > ` toFloat(): Float` 106 | 107 | > ` toDouble(): Double` 108 | 109 | > ` toChar(): Char` 110 | 111 | 隐式转换一般情况下是不容易被发觉的,因为我们使用了上下文推断出类型,并且算术运算会为合适的转换进行重载,比如 112 | 113 | ```kotlin 114 | val l = 1.toLong + 1 // Long + Int => Long 115 | ``` 116 | 117 | ### 运算符 118 | Kotlin支持标准的算术运算表达式,这些运算符被声明为相应类的成员(但是编译器将调用优化到相应的指令)。参看[运算符重载](http://kotlinlang.org/docs/reference/operator-overloading.html)。 119 | 120 | 至于位运算,Kotlin 并没有提供特殊的操作符,只是提供了命名函数,可以采用中缀形式调用,比如: 121 | 122 | ```kotlin 123 | val x = (1 shl 2) and 0x000FF000 124 | ``` 125 | 126 | 下面是全部的位运算操作符(只可以用在 `Int` 和 `Long` 类型): 127 | 128 | > `shl(bits)` – 有符号左移 (相当于 Java’s `<<`) 129 | > `shr(bits)` – 有符号右移 (相当于 Java’s `>>`) 130 | > `ushr(bits)` – 无符号右移 (相当于 Java’s `>>>`) 131 | > `and(bits)` – 按位与 132 | > `or(bits)` – 按位或 133 | > `xor(bits)` – 按位异或 134 | > `inv(bits)` – 按位翻转 135 | 136 | ### 字符 137 | 字符类型用 `Char` 表示。不能直接当做数值来使用 138 | 139 | ```Kotlin 140 | fun check(c: Char) { 141 | if (c == 1) { // ERROR: 类型不匹配 142 | // ... 143 | } 144 | } 145 | ``` 146 | 字符是由单引号包裹的:'1',特殊的字符通过反斜杠\\转义,下面的字符序列支持转义:`\t`,`\b`,`\n`,`\r`,`\'`,`\"`,`\\`和`\$`。编码任何其他字符,使用 Unicode 转义语法:`\uFF00`。 147 | 148 | 我们可以将字符显示的转义为Int数字: 149 | 150 | ```kotlin 151 | fun decimalDigitValue(c: Char): Int { 152 | if (c !in '0'..'9') 153 | throw IllegalArgumentException("Out of range") 154 | return c.toInt() - '0'.toInt() //显示转换为数值类型 155 | } 156 | ``` 157 | 和数值类型一样,需要一个可空引用时,字符会被装箱。特性不会被装箱保留。 158 | 159 | ### 布尔值 160 | 布尔值只有 true 或者 false 161 | 162 | 如果需要一个可空引用,将会对布尔值装箱 163 | 164 | 布尔值的内建操作包括 165 | 166 | > `||` – 短路或 167 | > 168 | > `&&` – 短路与 169 | > 170 | > `!` - 取反 171 | 172 | ### 数组 173 | 数组在 Kotlin 中由 `Array` 类表示,有 `get` 和 `set` (通过运算符重载为`[]` )方法,和 `size` 属性,以及一些常用的函数: 174 | 175 | ```kotlin 176 | class Array private constructor() { 177 | val size: Int 178 | operator fun get(index: Int): T 179 | operator fun set(index: Int, value: T): Unit 180 | 181 | operator fun iterator(): Iterator 182 | // ... 183 | } 184 | ``` 185 | 186 | 给库函数 `arrayOf()` 传递每一项的值来创建Array,`arrayOf(1, 2, 3)` 创建了一个[1, 2, 3] 这样的数组。也可以使用库函数 `arrayOfNulls()` 创建一个指定大小的空Array。 187 | 188 | 另一种方式就是使用工厂函数,接受一个数组大小参数以及一个可以根据给定索引创建初始值的函数: 189 | 190 | ```kotlin 191 | // 创建一个 Array 内容为 ["0", "1", "4", "9", "16"] 192 | val asc = Array(5, {i -> (i * i).toString() }) 193 | ``` 194 | 195 | 像我们上面提到的,`[]` 操作符表示调用 `get()` `set()` 函数 196 | 197 | 注意:和 java 不一样,arrays 在 kotlin 中是不可变的。这意味这 kotlin 不允许我们把 `Array` 转为 `Array` ,这样就阻止了可能的运行时错误(但你可以使用 `Array` , 参看 [Type Projections](http://kotlinlang.org/docs/reference/generics.html#type-projections)) 198 | 199 | Kotlin 有专门的类来表示原始类型从而避免过度装箱: ByteArray, ShortArray, IntArray 等等。这些类与 Array 没有继承关系,但它们有一样的方法与属性。每个都有对应的库函数: 200 | 201 | ```kotlin 202 | val x: IntArray = intArrayOf(1, 2, 3) 203 | x[0] = x[1] + x[2] 204 | ``` 205 | 206 | ### 字符串 207 | 字符串是由 `String` 表示的。字符串是不变的。字符串的元素可以通过索引操作读取: `s[i]` 。字符串可以用 for 循环迭代: 208 | 209 | ```kotlin 210 | for (c in str) { 211 | println(c) 212 | } 213 | ``` 214 | 215 | #### 字符串字面值 216 | Kotlin 有两种类型的字符串字面值:一种是可以带转义符的,一种是可以包含新行以及任意文本的。带转义符的 string 很像 java 的 string: 217 | 218 | ```kotlin 219 | val s = "Hello World!\n" 220 | ``` 221 | 222 | 转义是使用传统的反斜线的方式。参见[Characters](#characters),查看支持的转义序列。 223 | 224 | 整行String 是由三个引号包裹的(`"""`),不可以包含转义符但可 以包含其它字符: 225 | 226 | ```kotlin 227 | val text = """ 228 | for (c in "foo") 229 | print(c) 230 | """ 231 | ``` 232 | 233 | 你可以通过 [trim-margin()](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/trim-margin.html) 函数移除空格: 234 | 235 | ```kotlin 236 | val text = """ 237 | |Tell me and I forget. 238 | |Teach me and I remember. 239 | |Involve me and I learn. 240 | |(Benjamin Franklin) 241 | """.trimMargin() 242 | ``` 243 | 244 | 默认采用`|`标注起始前缀,也可以传递其它的字符做起始前缀,比如`trimMargin(">")` 245 | 246 | #### 字符串模板 247 | 248 | 字符串可以包含模板表达式,即可求值的代码片段,并将其结果连接到字符串中。模板表达式由 $ 开始并包含另一个简单的名称: 249 | 250 | ```kotlin 251 | val i = 10 252 | val s = "i = $i" // 求值为 "i = 10" 253 | ``` 254 | 255 | 或者是一个带大括号的表达式: 256 | 257 | ```kotlin 258 | val s = "abc" 259 | val str = "$s.length is ${s.length}" // 结果为 "abc.length is 3" 260 | ``` 261 | 262 | 模板既可以原始字符串中使用,也可以在转义字符串中使用。如果需要在原始字符串(不支持反斜杠转义)中表示一个文字$字符,那么可以使用以下语法: 263 | 264 | ```kotlin 265 | val price = """ 266 | ${'$'}9.99 267 | """ 268 | ``` 269 | -------------------------------------------------------------------------------- /GettingStarted/Basic-Syntax.md: -------------------------------------------------------------------------------- 1 | [原文](http://kotlinlang.org/docs/reference/basic-syntax.html) 2 | 3 | ## 基本语法 4 | 5 | ### 包定义和引入 6 | 7 | 在源文件的开头定义包: 8 | 9 | ```kotlin 10 | package my.demo 11 | 12 | import kotlin.text.* 13 | 14 | // ... 15 | ``` 16 | 17 | 包名不必和文件夹路径一致:源文件可以放在任意位置。 18 | 19 | 更多请参看 [包(package)](../Basics/Packages.md) 20 | 21 | ### 程序入口 22 | 23 | Kotlin 应用的入口是 `main` 函数. 24 | 25 | ```kotlin 26 | fun main() { 27 | println("Hello world!") 28 | } 29 | ``` 30 | 31 | ### 函数 32 | 33 | 下面的函数接受两个 `Int` 型参数,返回值为 `Int` : 34 | 35 | ```kotlin 36 | fun sum(a: Int, b: Int): Int { 37 | return a + b 38 | } 39 | ``` 40 | 41 | 具有表达式主体和推断的返回类型的函数: 42 | 43 | ```kotlin 44 | fun sum(a: Int, b: Int) = a + b 45 | ``` 46 | 47 | 返回无意义的值: 48 | 49 | ```kotlin 50 | fun printSum(a: Int, b: Int): Unit { 51 | println("sum of $a and $b is ${a + b}") 52 | } 53 | ``` 54 | 55 | `Unit` 的返回类型可以省略: 56 | 57 | ```kotlin 58 | fun printSum(a: Int, b: Int) { 59 | println("sum of $a and $b is ${a + b}") 60 | } 61 | ``` 62 | 63 | 更多请参看[函数](../FunctionsAndLambdas/Functions.md) 64 | 65 | ### 变量 66 | 67 | 只读的本地变量通过`val`关键字定义.该类变量只能赋值一次: 68 | 69 | ```kotlin 70 | val a: Int = 1 // 立刻赋值 71 | val b = 2 // `Int` 类型是自推导的 72 | val c: Int // 没有初始化器时要指定类型 73 | c = 3 // 推断型赋值 74 | ``` 75 | 76 | 被关键字`var`修饰的变量可以重新赋值: 77 | 78 | ```kotlin 79 | var x = 5 // `Int` type is inferred 80 | x += 1 81 | ``` 82 | 83 | 顶级变量: 84 | 85 | ```kotlin 86 | val PI = 3.14 87 | var x = 0 88 | 89 | fun incrementX() { 90 | x += 1 91 | } 92 | ``` 93 | 94 | 更多请参看[属性和字段](../ClassesAndObjects/Properties-and-Fields.md) 95 | 96 | ### 注释 97 | 与多数现代语言一样,Kotlin 支持单行注释(行尾注释)和多行注释(块注释)。 98 | 99 | ```kotlin 100 | // 行尾注释 101 | 102 | /* 这是块注释 103 | 可以在多行注释 */ 104 | ``` 105 | 106 | Kotlin 块注释可以嵌套. 107 | 108 | ```kotlin 109 | /* The comment starts here 110 | /* contains a nested comment */ 111 | and ends here. */ 112 | ``` 113 | 114 | 参看[文档化 Kotlin 代码](../Tools/Documenting-Kotlin-Code.md)更多关于文档化注释的语法。 115 | 116 | ### 字符串模板 117 | 118 | ```kotlin 119 | var a = 1 120 | // simple name in template: 121 | val s1 = "a is $a" 122 | 123 | a = 2 124 | // arbitrary expression in template: 125 | val s2 = "${s1.replace("is", "was")}, but now is $a" 126 | ``` 127 | 128 | 更多请参看[字符串模板](../Basics/Basic-Types.md) 129 | 130 | ### 条件表达式 131 | 132 | ```kotlin 133 | fun maxOf(a: Int, b: Int): Int { 134 | if (a > b) { 135 | return a 136 | } else { 137 | return b 138 | } 139 | } 140 | ``` 141 | 142 | kotin 中可以使用 if 作为表达式: 143 | 144 | ```kotlin 145 | fun maxOf(a: Int, b: Int) = if (a > b) a else b 146 | ``` 147 | 148 | 更多请参看 [if 表达式](../Basics/Control-Flow.md) 149 | 150 | ### 可空变量以及空值检查 151 | 152 | 当空值可能出现时必须明确标注该引用可空。 153 | 154 | 当 str 中不包含整数时返回空: 155 | 156 | ```kotlin 157 | fun parseInt(str: String): Int? { 158 | // ... 159 | } 160 | ``` 161 | 162 | 使用函数返回空值: 163 | 164 | ```kotlin 165 | fun printProduct(arg1: String, arg2: String) { 166 | val x = parseInt(arg1) 167 | val y = parseInt(arg2) 168 | 169 | // Using `x * y` yields error because they may hold nulls. 170 | if (x != null && y != null) { 171 | // x and y are automatically cast to non-nullable after null check 172 | println(x * y) 173 | } 174 | else { 175 | println("'$arg1' or '$arg2' is not a number") 176 | } 177 | } 178 | ``` 179 | 180 | 或者 181 | 182 | ```kotlin 183 | if (x == null) { 184 | println("Wrong number format in arg1: '$arg1'") 185 | return 186 | } 187 | if (y == null) { 188 | println("Wrong number format in arg2: '$arg2'") 189 | return 190 | } 191 | 192 | // x and y are automatically cast to non-nullable after null check 193 | println(x * y) 194 | ``` 195 | 196 | 更多请参看[空安全](../Other/Null-Safety.md) 197 | 198 | ### 类型检查以及自动转换 199 | 200 | `is` 操作符可以检查表达式是否是是某个类型的实例。如果不可变的局部变量或属性进行过了类型检查,就没有必要显示转换: 201 | 202 | ```kotlin 203 | fun getStringLength(obj: Any): Int? { 204 | if (obj is String) { 205 | // obj 将会在这个分支中自动转换为 `String` 类型 206 | return obj.length 207 | } 208 | 209 | // obj 在类型检查分支外仍然是 Any 类型 210 | return null 211 | } 212 | ``` 213 | 214 | 或者 215 | 216 | ```kotlin 217 | fun getStringLength(obj: Any): Int? { 218 | if (obj !is String) return null 219 | 220 | // obj 将会在这个分支中自动转换为 `String` 类型 221 | return obj.length 222 | } 223 | ``` 224 | 225 | 甚至可以这样 226 | 227 | ```kotlin 228 | fun getStringLength(obj: Any): Int? { 229 | // obj 将会在&&右边自动转换为 String 类型 230 | if (obj is String && obj.length > 0) { 231 | return obj.length 232 | } 233 | 234 | return null 235 | } 236 | ``` 237 | 238 | 更多请参看 [类](../ClassesAndObjects/Classes-and-Inheritance.md#3) 和 [类型转换](../Other/Type-Checks-and-Casts.md) 239 | 240 | ### for 循环 241 | ```kotlin 242 | val items = listOf("apple", "banana", "kiwifruit") 243 | for (item in items) { 244 | println(item) 245 | } 246 | ``` 247 | 248 | 或者 249 | 250 | ```kotlin 251 | val items = listOf("apple", "banana", "kiwifruit") 252 | for (index in items.indices) { 253 | println("item at $index is ${items[index]}") 254 | } 255 | ``` 256 | 257 | 参看[for循环](../Basics/Control-Flow.md) 258 | 259 | ### while 循环 260 | ```kotlin 261 | val items = listOf("apple", "banana", "kiwifruit") 262 | var index = 0 263 | while (index < items.size) { 264 | println("item at $index is ${items[index]}") 265 | index++ 266 | } 267 | ``` 268 | 269 | 参看[while循环](../Basics/Control-Flow.md) 270 | 271 | ### when 表达式 272 | ```kotlin 273 | fun describe(obj: Any): String = 274 | when (obj) { 275 | 1 -> "One" 276 | "Hello" -> "Greeting" 277 | is Long -> "Long" 278 | !is String -> "Not a string" 279 | else -> "Unknown" 280 | } 281 | ``` 282 | 283 | 参看[when表达式](../Basics/Control-Flow.md) 284 | 285 | ### ranges 286 | 287 | 使用 `in` 操作符判断数值是否在某个范围内: 288 | 289 | ```kotlin 290 | val x = 10 291 | val y = 9 292 | if (x in 1..y+1) { 293 | println("fits in range") 294 | } 295 | ``` 296 | 297 | 检查数值是否在范围外: 298 | 299 | ```kotlin 300 | val list = listOf("a", "b", "c") 301 | 302 | if (-1 !in 0..list.lastIndex) { 303 | println("-1 is out of range") 304 | } 305 | if (list.size !in list.indices) { 306 | println("list size is out of valid list indices range, too") 307 | } 308 | ``` 309 | 310 | 参看[Ranges](../Other/Ranges.md) 311 | 312 | ### 集合 313 | 314 | 遍历集合: 315 | 316 | ```kotlin 317 | for (item in items) { 318 | println(item) 319 | } 320 | ``` 321 | 322 | 使用 `in` 操作符检查集合中是否包含某个对象: 323 | 324 | ```kotlin 325 | when { 326 | "orange" in items -> println("juicy") 327 | "apple" in items -> println("apple is fine too") 328 | } 329 | ``` 330 | 331 | 使用lambda表达式过滤和映射集合: 332 | 333 | ```kotlin 334 | val fruits = listOf("banana", "avocado", "apple", "kiwifruit") 335 | fruits 336 | .filter { it.startsWith("a") } 337 | .sortedBy { it } 338 | .map { it.toUpperCase() } 339 | .forEach { println(it) } 340 | ``` 341 | 342 | 参看[集合概述](../Collections/CollectionsOverview.md) 343 | 344 | ### 创建基本类以及实例: 345 | 346 | ```kotlin 347 | val rectangle = Rectangle(5.0, 2.0) 348 | val triangle = Triangle(3.0, 4.0, 5.0) 349 | ``` 350 | 351 | 参看[类](../ClassesAndObjects/Classes-and-Inheritance.md)和[对象及实例](../ClassesAndObjects/ObjectExpressicAndDeclarations.md) -------------------------------------------------------------------------------- /coroutines/CancellationAndTimeouts.md: -------------------------------------------------------------------------------- 1 | **目录** 2 | 3 | * [取消与超时](#取消与超时) 4 | * [取消协程的执行](#取消协程的执行) 5 | * [取消是协作的](#取消是协作的) 6 | * [使计算代码可取消](#使计算代码可取消) 7 | * [在 finally 中释放资源](#在-finally-中释放资源) 8 | * [运行不可取消的代码块](#运行不可取消的代码块) 9 | * [超时](#超时) 10 | 11 | ## 取消与超时 12 | 13 | 这一部分包含了协程的取消与超时。 14 | 15 | ### 取消协程的执行 16 | 17 | 在长时间运行的应用程序中,你可能需要对后台协程进行细粒度控制。 例如,用户可能已关闭启动协程的页面,现在不再需要其结果,并且该操作是可取消的。`launch` 函数会返回一个可用于取消运行协程的 `job`: 18 | 19 | ```kotlin 20 | import kotlinx.coroutines.* 21 | 22 | fun main() = runBlocking { 23 | val job = launch { 24 | repeat(1000) { i -> 25 | println("I'm sleeping $i ...") 26 | delay(500L) 27 | } 28 | } 29 | delay(1300L) // delay a bit 30 | println("main: I'm tired of waiting!") 31 | job.cancel() // cancels the job 32 | job.join() // waits for job's completion 33 | println("main: Now I can quit.") 34 | } 35 | ``` 36 | 37 | 运行结果如下: 38 | 39 | ```kotlin 40 | I'm sleeping 0 ... 41 | I'm sleeping 1 ... 42 | I'm sleeping 2 ... 43 | main: I'm tired of waiting! 44 | main: Now I can quit. 45 | ``` 46 | 47 | job.cancel 调用后,其它协程将不能从它获取任何结果,因为该协程已经取消.还有一个Job扩展函数[cancelAndJoin](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/cancel-and-join.html),它结合cancel和join调用。 48 | 49 | ### 取消是协作的 50 | 51 | 协程的取消是协作的,协程代码必须合作才能取消. `kotlinx.coroutines`中所有的挂起函数都是可取消的.它们会检查协程是否可取消,若不可取消则抛出 `[CancellationException](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-cancellation-exception/index.html)` 异常. 然而如果协程正在进行计算并且没有检查可取消性, 那么它是不可取消的,比如下面的例子: 52 | 53 | ```kotlin 54 | import kotlinx.coroutines.* 55 | 56 | fun main() = runBlocking { 57 | val startTime = System.currentTimeMillis() 58 | val job = launch(Dispatchers.Default) { 59 | var nextPrintTime = startTime 60 | var i = 0 61 | while (i < 5) { // computation loop, just wastes CPU 62 | // print a message twice a second 63 | if (System.currentTimeMillis() >= nextPrintTime) { 64 | println("I'm sleeping ${i++} ...") 65 | nextPrintTime += 500L 66 | } 67 | } 68 | } 69 | delay(1300L) // delay a bit 70 | println("main: I'm tired of waiting!") 71 | job.cancelAndJoin() // cancels the job and waits for its completion 72 | println("main: Now I can quit.") 73 | } 74 | ``` 75 | 76 | 可以试试它是否会在取消后继续打印“I'm sleeping”,直到作业在五次迭代后自行完成。 77 | 78 | ### 使计算代码可取消 79 | 80 | 有两种方法可以使计算代码可以取消。 一种是定期调用检查取消的挂起功能。 [yield](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/yield.html)函数是一个很好的选择。 另一种是明确检查取消状态。 本例使用后一种方法: 81 | 82 | 把 `while (i < 5)` 改为 `while (isActive)` 并运行 83 | 84 | ```kotlin 85 | import kotlinx.coroutines.* 86 | 87 | fun main() = runBlocking { 88 | val startTime = System.currentTimeMillis() 89 | val job = launch(Dispatchers.Default) { 90 | var nextPrintTime = startTime 91 | var i = 0 92 | while (isActive) { // cancellable computation loop 93 | // print a message twice a second 94 | if (System.currentTimeMillis() >= nextPrintTime) { 95 | println("I'm sleeping ${i++} ...") 96 | nextPrintTime += 500L 97 | } 98 | } 99 | } 100 | delay(1300L) // delay a bit 101 | println("main: I'm tired of waiting!") 102 | job.cancelAndJoin() // cancels the job and waits for its completion 103 | println("main: Now I can quit.") 104 | } 105 | ``` 106 | 107 | 正如你所看到的,现在循环可以取消. `[isActive](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/is-active.html)` 是一个扩展属性,可通过CoroutineScope对象在coroutine代码中使用。 108 | 109 | ### 在 finally 中释放资源 110 | 111 | 可取消的挂起函数会在取消时抛出CancellationException,这可以通过常规方式处理。 例如,try{...} finally {...} 表达式或者Kotlin `use` 函数在取消协程时正常执行其终止操作: 112 | 113 | ```kotlin 114 | val job = launch { 115 | try { 116 | repeat(1000) { i -> 117 | println("I'm sleeping $i ...") 118 | delay(500L) 119 | } 120 | } finally { 121 | println("I'm running finally") 122 | } 123 | } 124 | delay(1300L) // delay a bit 125 | println("main: I'm tired of waiting!") 126 | job.cancelAndJoin() // cancels the job and waits for its completion 127 | println("main: Now I can quit.") 128 | ``` 129 | 130 | join和cancelAndJoin都等待所有终结操作完成,因此上面的示例生成以下输出: 131 | 132 | ``` 133 | I'm sleeping 0 ... 134 | I'm sleeping 1 ... 135 | I'm sleeping 2 ... 136 | main: I'm tired of waiting! 137 | I'm running finally 138 | main: Now I can quit. 139 | ``` 140 | 141 | ### 运行不可取消的代码块 142 | 143 | 在前一个示例的finally块中使用挂起函数的任何尝试都会导致CancellationException,因为取消了运行此代码的协程。 通常,这没关系,因为所有表现良好的关闭操作(关闭文件,取消作业或关闭任何类型的通信通道)通常都是非阻塞的,并且不涉及任何挂起函数。 但是,在极少数情况下,当您需要在取消的协同程序中挂起时,可以使用[withContext](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-context.html)函数和[NonCancellable](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-non-cancellable.html)上下文将相应的代码包装在withContext(NonCancellable){...}中,如下例所示: 144 | 145 | ```kotlin 146 | val job = launch { 147 | try { 148 | repeat(1000) { i -> 149 | println("I'm sleeping $i ...") 150 | delay(500L) 151 | } 152 | } finally { 153 | withContext(NonCancellable) { 154 | println("I'm running finally") 155 | delay(1000L) 156 | println("And I've just delayed for 1 sec because I'm non-cancellable") 157 | } 158 | } 159 | } 160 | delay(1300L) // delay a bit 161 | println("main: I'm tired of waiting!") 162 | job.cancelAndJoin() // cancels the job and waits for its completion 163 | println("main: Now I can quit.") 164 | ``` 165 | 166 | ### 超时 167 | 168 | 在实践中取消协程执行的最主要的原因是因为它的执行时间超过了限制。 虽然可以手动跟踪对相应作业的引用并启动单独的协程以在延迟后取消跟踪的协程,但[withTimeout](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/with-timeout.html)函数是一个开箱即用的操作。 请看以下示例: 169 | 170 | ```kotlin 171 | withTimeout(1300L) { 172 | repeat(1000) { i -> 173 | println("I'm sleeping $i ...") 174 | delay(500L) 175 | } 176 | } 177 | ``` 178 | 179 | 输出结果如下: 180 | 181 | ``` 182 | I'm sleeping 0 ... 183 | I'm sleeping 1 ... 184 | I'm sleeping 2 ... 185 | Exception in thread "main" kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 1300 ms 186 | ``` 187 | 188 | withTimeout抛出的TimeoutCancellationException是CancellationException的子类。 我们之前没有看到它的堆栈跟踪打印在控制台上。 这是因为在取消的协程中,CancellationException被认为是协程完成的正常原因。 但是,在这个例子中,我们在main函数中使用了withTimeout。 189 | 190 | 因为取消只是一个异常,所有资源都以通常的方式关闭。 如果你可以在任何类型的超时上做一些额外的操作或者使用类似于withTimeout的withTimeoutOrNull函数,你可以在try {...} catch(e:TimeoutCancellationException){...}块中用超时包装代码, 这样在超时时将返回null而不是抛出异常: 191 | 192 | 193 | ```kotlin 194 | val result = withTimeoutOrNull(1300L) { 195 | repeat(1000) { i -> 196 | println("I'm sleeping $i ...") 197 | delay(500L) 198 | } 199 | "Done" // will get cancelled before it produces this result 200 | } 201 | println("Result is $result") 202 | ``` 203 | 204 | 这样运行以上代码就不会抛出异常了: 205 | 206 | ``` 207 | I'm sleeping 0 ... 208 | I'm sleeping 1 ... 209 | I'm sleeping 2 ... 210 | Result is null 211 | ``` -------------------------------------------------------------------------------- /coroutines/Basics.md: -------------------------------------------------------------------------------- 1 | **内容目录** 2 | 3 | * [协程基础](#协程基础) 4 | * [你的第一个协程](#你的第一个协程) 5 | * [桥接阻塞与非阻塞的世界](#桥接阻塞与非阻塞的世界) 6 | * [等待任务](#等待任务) 7 | * [结构化并发](#结构化并发) 8 | * [作用域构建器](#作用域构建器) 9 | * [提取函数重构](#提取函数重构) 10 | * [协程是轻量级的](#协程是轻量级的) 11 | * [全局协程就像一个守护线程](#全局协程就像一个守护线程) 12 | 13 | 14 | ## 协程基础 15 | 16 | 这部分将包含协程的基础概念。 17 | 18 | ### 你的第一个协程 19 | 20 | 运行下面的代码: 21 | 22 | ```kotlin 23 | import kotlinx.coroutines.* 24 | 25 | fun main() { 26 | GlobalScope.launch { // launch a new coroutine in background and continue 27 | delay(1000L) // non-blocking delay for 1 second (default time unit is ms) 28 | println("World!") // print after delay 29 | } 30 | println("Hello,") // main thread continues while coroutine is delayed 31 | Thread.sleep(2000L) // block main thread for 2 seconds to keep JVM alive 32 | } 33 | ``` 34 | 35 | 你会得到如下结果: 36 | 37 | ```text 38 | Hello, 39 | World! 40 | ``` 41 | 本质上讲,协程是轻量级的线程。它们由 `launch` 协程构建器在对应的 `[CoroutineScope](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html)` 上下文中启动。在本例中是 `[GlobalScope](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-global-scope/index.html)` , 意味着新协程的生命周期受限于整个应用生命周期。 42 | 43 | 你可以用 44 | `GlobalScope.launch { …… }` 替换为 `thread { …… }`,将 `delay(……)` 替换为 `Thread.sleep(……)` 达到同样目的。 45 | 46 | 如果你用 `GlobalScope.launch` 替换为 `thread`,编译器会报以下错误: 47 | 48 | ``` 49 | Error: Kotlin: Suspend functions are only allowed to be called from a coroutine or another suspend function 50 | ``` 51 | 52 | 这是因为 [delay] 是一个特殊的 _挂起函数_ ,它不会造成线程阻塞,但是会 _挂起_ 协程,并且只能在协程中使用。 53 | 54 | ### 桥接阻塞与非阻塞的世界 55 | 56 | 第一个例子混合了非阻塞 `delay(...)` 和阻塞 `Thread.sleep(...)` 。这会让人搞混哪个是阻塞哪个是非阻塞。下面用 `[runBlocking](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html)` 协程构建器来说明什么是阻塞: 57 | 58 | ```kotlin 59 | import kotlinx.coroutines.* 60 | 61 | fun main() { 62 | GlobalScope.launch { // launch a new coroutine in background and continue 63 | delay(1000L) 64 | println("World!") 65 | } 66 | println("Hello,") // main thread continues here immediately 67 | runBlocking { // but this expression blocks the main thread 68 | delay(2000L) // ... while we delay for 2 seconds to keep JVM alive 69 | } 70 | } 71 | ``` 72 | 73 | 结果是一样的,但是代码只用了非阻塞的函数 delay。调用了 `runBlocking` 的主线程会一直阻塞直到 `runBlocking` 内部的协程执行完毕。 74 | 75 | 这个例子还可以改写为更加惯用的方式。使用 `runBlocking` 包装主函数的执行: 76 | 77 | ```kotlin 78 | import kotlinx.coroutines.* 79 | 80 | fun main() = runBlocking { // start main coroutine 81 | GlobalScope.launch { // launch a new coroutine in background and continue 82 | delay(1000L) 83 | println("World!") 84 | } 85 | println("Hello,") // main coroutine continues here immediately 86 | delay(2000L) // delaying for 2 seconds to keep JVM alive 87 | } 88 | ``` 89 | 90 | 这里的 `runBlocking {...}` 作为用来启动顶级主协程的适配器。显示声明了返回值是 `Unit`,因为 Kotlin 的 main 函数返回值是 Unit 91 | 92 | 也可以这样给挂起函数写单元测试: 93 | 94 | ```kotlin 95 | class MyTest { 96 | @Test 97 | fun testMySuspendingFunction() = runBlocking { 98 | // here we can use suspending functions using any assertion style that we like 99 | } 100 | } 101 | ``` 102 | 103 | ### 等待一个任务 104 | 105 | 指定延迟时间去等待另一个协程结束并不是一个好办法。我们可以显式的等待一个`[Job](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html)` 是否完成: 106 | 107 | ```kotlin 108 | val job = GlobalScope.launch { // launch a new coroutine and keep a reference to its Job 109 | delay(1000L) 110 | println("World!") 111 | } 112 | ``` 113 | 114 | 结果仍然是一致的,但主协程与后台任务的执行时间解绑了,代码更加整洁。 115 | 116 | ### 结构化并发 117 | 118 | 在使用协程时还需要些东西.当用 `GlobalScope.launch` 会创建一个顶级协程.尽管协程是轻量级的,运行时仍然会带来内存资源的消耗.如果我们忘记对新启动协程的引用,他将一直运行下去. 如果协程中的代码挂起了(比如我们delay 时间过长),万一启动过多协程而内存不够了怎么办? 手动保持所有已启动的协程的引用,以及调用`join`来避免出错. 119 | 120 | 这里有更好的解决方案.可以借助结构化并发来解决。我们可以在执行操作所在的指定作用域内启动协程,而不是像使用线程(线程总是全局的)那样在 [GlobalScope] 中启动. 121 | 122 | 在下面的例子中,主函数被`runBlocking`协程构建器转换为协程.每个协程构建器,包括`runBlocking`都会添加一个 `CoroutineScope` 到自己的代码块中.我们可以在这个范围内启动协程而不用显示调用`join`,因为外部协程(本例中的`runBlocking`) 直到它启动的所有协程均执行结束才会结束.因此,我们可以把代码写的更加简洁: 123 | 124 | ```kotlin 125 | import kotlinx.coroutines.* 126 | 127 | fun main() = runBlocking { // this: CoroutineScope 128 | launch { // launch a new coroutine in the scope of runBlocking 129 | delay(1000L) 130 | println("World!") 131 | } 132 | println("Hello,") 133 | } 134 | ``` 135 | 136 | ### 作用域构建器 137 | 138 | 除了由不同构建器提供的协程作用域之外,还可以使用coroutineScope构建器声明自己的作用域。 它会创建新的协程范围,并且在所有已启动的子项完成之前不会完成。 139 | 140 | runBlocking和coroutineScope 看起来很相似,他们都会等自身作用域内代码和子协程完成后结束。他们之间的主要区别在 runBlocking 会这阻塞当前线程并等待,而 coroutineScope 只会挂起,并释放当前线程给其它人用。由于这个不同,runBlocking 是一个普通函数,而 coroutineScope 是一个挂起函数。 141 | 142 | 下面等例子演示了区别。 143 | 144 | ```kotlin 145 | import kotlinx.coroutines.* 146 | 147 | fun main() = runBlocking { // this: CoroutineScope 148 | launch { 149 | delay(200L) 150 | println("Task from runBlocking") 151 | } 152 | 153 | coroutineScope { // Creates a coroutine scope 154 | launch { 155 | delay(500L) 156 | println("Task from nested launch") 157 | } 158 | 159 | delay(100L) 160 | println("Task from coroutine scope") // launch 块内结束前就打印 161 | } 162 | 163 | println("Coroutine scope is over") // 直到 launch 块内完成才会打印 164 | } 165 | ``` 166 | 167 | 注意这里 "Task from coroutine scope" 消息打印后,在等待内嵌 launch 时 “Task from runBlocking” 将会执行并且打印,尽管 coroutineScope 还没有结束。 168 | 169 | 170 | ### 提取函数重构 171 | 172 | 让我们将launch {...}中的代码块提取到一个单独的函数中。 当对此代码执行“Extract function”重构时,需要创建一个带有suspend修饰符的新函数。 这是你的第一个挂起函数。 挂起函数可以在协同程序内部使用,就像普通函数一样,但它们的附加功能是它们可以使用其他挂起函数(例如本例中的延迟)来挂起协程的执行。 173 | 174 | ```kotlin 175 | import kotlinx.coroutines.* 176 | 177 | fun main() = runBlocking { 178 | launch { doWorld() } 179 | println("Hello,") 180 | } 181 | 182 | // this is your first suspending function 183 | suspend fun doWorld() { 184 | delay(1000L) 185 | println("World!") 186 | } 187 | ``` 188 | 189 | 但如果提取的函数包含在当前作用域上调用的协程构建器,该怎么办? 这种情况下,只有suspend修饰符是不够的。 在CoroutineScope上添加 doWorld 扩展方法是其中一个解决方案,但这并不是一个好的方式,因为它不会使API更清晰。 惯用解决方案是将显式CoroutineScope作为包含目标函数的类中的字段,或者在外部类实现CoroutineScope达到隐式实现。 作为最后的手段,可以使用 [CoroutineScope(coroutineContext)](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope.html),但是这种方法在结构上是不安全的,这样你将不再能够控制此方法的执行范围。 只有私有API才能使用此构建器。 190 | 191 | ### 协程是轻量级的 192 | 193 | 运行下面的代码: 194 | 195 | ```kotlin 196 | import kotlinx.coroutines.* 197 | 198 | fun main() = runBlocking { 199 | repeat(100_000) { // launch a lot of coroutines 200 | launch { 201 | delay(1000L) 202 | print(".") 203 | } 204 | } 205 | } 206 | ``` 207 | 208 | 它会启动100K协程,每个协程在一秒后打印 ".". 如果换成线程去实现,会发生什么呢? (最可能得是出现内存不足的错误) 209 | 210 | ### 全局协程就像一个守护线程 211 | 212 | 下面的代码在GlobalScope中启动一个长时间运行的协程,它会每秒打印“I'm sleeping”两次,然后在一段时延后返回: 213 | 214 | ```kotlin 215 | GlobalScope.launch { 216 | repeat(1000) { i -> 217 | println("I'm sleeping $i ...") 218 | delay(500L) 219 | } 220 | } 221 | delay(1300L) // just quit after delay 222 | ``` 223 | 224 | 试着运行下代码他会打印三行然后结束: 225 | 226 | ``` 227 | I'm sleeping 0 ... 228 | I'm sleeping 1 ... 229 | I'm sleeping 2 ... 230 | ``` 231 | 232 | 在GlobalScope中启动的协程不会使进程保持活动状态。它们就像守护线程(当非守护线程结束后守护线程就会结束)。 -------------------------------------------------------------------------------- /coroutines/SharedMutableStateAndConcurrency.md: -------------------------------------------------------------------------------- 1 | - [共享的可变状态和并发](#共享的可变状态和并发) 2 | - [问题](#问题) 3 | - [Volatiles不起作用](#Volatiles不起作用) 4 | - [线程安全的数据结构](#线程安全的数据结构) 5 | - [细粒度线程限制](#细粒度线程限制) 6 | - [粗粒度线程限制](#粗粒度线程限制) 7 | - [互斥](#互斥) 8 | - [Actors](#Actors) 9 | 10 | ## 共享的可变状态和并发 11 | 12 | 协程可以使用诸如Dispatchers.Default之类的多线程调度程序并发执行。 它代表了所有常见的并发问题。 主要问题是同步访问共享可变状态。 协程领域中针对此问题的某些解决方案与多线程世界中的解决方案相似,但其他解决方案却是独一无二的。 13 | 14 | ### 问题 15 | 16 | 让我们启动一百个协程,它们都执行相同的动作数千次。 我们还将测量它们的完成时间以进行进一步的比较: 17 | 18 | ```Kotlin 19 | suspend fun massiveRun(action: suspend () -> Unit) { 20 | val n = 100 // number of coroutines to launch 21 | val k = 1000 // times an action is repeated by each coroutine 22 | val time = measureTimeMillis { 23 | coroutineScope { // scope for coroutines 24 | repeat(n) { 25 | launch { 26 | repeat(k) { action() } 27 | } 28 | } 29 | } 30 | } 31 | println("Completed ${n * k} actions in $time ms") 32 | } 33 | ``` 34 | 35 | 我们从一个非常简单的操作开始,该操作使用多线程Dispatchers.Default增加共享的可变变量。 36 | 37 | 38 | ```Kotlin 39 | var counter = 0 40 | 41 | fun main() = runBlocking { 42 | withContext(Dispatchers.Default) { 43 | massiveRun { 44 | counter++ 45 | } 46 | } 47 | println("Counter = $counter") 48 | } 49 | ``` 50 | 51 | 最后他会输出什么呢? 很大可能上不是 "Counter - 100000", 因为上百个协程会在多线中非同步的对 `counter` 进行并发操作. 52 | 53 | ### Volatiles不起作用 54 | 55 | 常见的误解是,标记变量为 `volatile` 可解决并发问题。 让我们尝试一下: 56 | 57 | ```Kotlin 58 | @Volatile // in Kotlin `volatile` is an annotation 59 | var counter = 0 60 | 61 | fun main() = runBlocking { 62 | withContext(Dispatchers.Default) { 63 | massiveRun { 64 | counter++ 65 | } 66 | } 67 | println("Counter = $counter") 68 | } 69 | ``` 70 | 71 | 该代码的运行速度较慢,但是最后我们仍然无法获得“ Counter = 100000”,因为 volatile 变量可确保线性化( linearizable 这是“原子”的技术术语)可读写相应的变量,但不提供原子性较大的动作(在我们的情况下为增加)。 72 | 73 | ### 线程安全的数据结构 74 | 75 | 适用于线程和协程的通用解决方案是使用线程安全(aka同步,线性化或原子)数据结构,该结构为需要在共享状态下执行的相应操作提供所有必需的同步。 对于简单的计数器,我们可以使用 `AtomicInteger` 类,该类具有原子级 `incrementAndGet` 操作: 76 | 77 | ```Kotlin 78 | var counter = AtomicInteger() 79 | 80 | fun main() = runBlocking { 81 | withContext(Dispatchers.Default) { 82 | massiveRun { 83 | counter.incrementAndGet() 84 | } 85 | } 86 | println("Counter = $counter") 87 | } 88 | ``` 89 | 90 | 这是针对此特定问题的最快解决方案。 它适用于简单计数器,集合,队列和其他标准数据结构以及对其的基本操作。 但是,它不容易扩展到复杂状态或者复杂操作,它们并没有一个可以立即使用的线程安全的实现. 91 | 92 | ### 细粒度线程限制 93 | 94 | 线程限制是一种解决共享可变状态的方法,其中对特定共享状态的所有访问都限于一个线程。它通常用于UI应用程序中,其中所有UI状态都限制在单个事件调度/应用程序线程中。通过使用 95 | 单线程上下文。 96 | 97 | ```Kotlin 98 | val counterContext = newSingleThreadContext("CounterContext") 99 | var counter = 0 100 | 101 | fun main() = runBlocking { 102 | withContext(Dispatchers.Default) { 103 | massiveRun { 104 | // confine each increment to a single-threaded context 105 | withContext(counterContext) { 106 | counter++ 107 | } 108 | } 109 | } 110 | println("Counter = $counter") 111 | } 112 | ``` 113 | 114 | 这段代码非常慢,因为它可以进行细粒度的线程约束。每个单独的增量都使用withContext(counterContext)块从多线程 Dispatchers.Default 上下文切换到单线程上下文。 115 | 116 | ### 粗粒度线程限制 117 | 118 | 实际上,线程限制是大块执行的,例如状态更新业务逻辑的大部分都局限于单个线程中。下面的示例就是这样做的,首先在单线程上下文中运行每个协程。 119 | 120 | ```Kotlin 121 | val counterContext = newSingleThreadContext("CounterContext") 122 | var counter = 0 123 | 124 | fun main() = runBlocking { 125 | // confine everything to a single-threaded context 126 | withContext(counterContext) { 127 | massiveRun { 128 | counter++ 129 | } 130 | } 131 | println("Counter = $counter") 132 | } 133 | ``` 134 | 135 | 现在这可以更快地工作并产生正确的结果。 136 | 137 | ### 互斥 138 | 139 | 解决该问题的互斥解决方案是使用永远不会并行的临界区来保护共享状态的所有修改。在阻塞的世界中,通常会为此使用 `synchronized` 或 `ReentrantLock` 。协程的替代品称为 [Mutex](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.sync/-mutex/index.html) 。它具有 [lock](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.sync/-mutex/lock.html) 和 [unlock](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.sync/-mutex/unlock.html) 函数来界定临界区。关键区别在于Mutex.lock()是一个可挂起函数。它不会阻塞线程。 140 | 141 | 还有一个 [withLock](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.sync/with-lock.html) 扩展函数,可以方便地表示 `mutex.lock(); try { ... } finally { mutex.unlock() }` 模式: 142 | 143 | ```Kotlin 144 | val mutex = Mutex() 145 | var counter = 0 146 | 147 | fun main() = runBlocking { 148 | withContext(Dispatchers.Default) { 149 | massiveRun { 150 | // protect each increment with lock 151 | mutex.withLock { 152 | counter++ 153 | } 154 | } 155 | } 156 | println("Counter = $counter") 157 | } 158 | ``` 159 | 160 | 此示例中的锁定是细粒度的,因此要付出代价。 但是,在某些情况下,须定期修改某些共享状态,但是没有限制该状态的自然线程,这是一个不错的选择。 161 | 162 | ### Actors 163 | 164 | [actor](https://en.wikipedia.org/wiki/Actor_model) 是一个结合协程创建的实体,该协程限制并封装的状态以及与其他协程通信的通道组成的实体。可以将简单的actor编写为函数,但是状态复杂的actor更适合用类表示。 165 | 166 | [actor](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/actor.html) 协程生成器,可以方便地将actor的邮箱通道合并到其作用域中,以从中接收消息,并将send通道合并到结果 job 对象中,actor的单个引用作为其句柄。 167 | 168 | 使用actor的第一步是定义actor将要处理的消息类。 Kotlin的 [sealed 类](https://kotlinlang.org/docs/reference/sealed-classes.html) 非常适合该目的。我们定义 CounterMsg 密封类,IncCounter 消息以增加计数器,GetCounter 消息获取其值。后者需要发送响应。为此,此处使用了 [CompletableDeferred](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-completable-deferred/index.html) 通信原语,该原语表示将来将要知道(传递)的单个值。 169 | 170 | ```Kotlin 171 | // Message types for counterActor 172 | sealed class CounterMsg 173 | object IncCounter : CounterMsg() // one-way message to increment counter 174 | class GetCounter(val response: CompletableDeferred) : CounterMsg() // a request with reply 175 | ``` 176 | 177 | 然后,我们定义一个使用actor协程生成器启动actor的函数: 178 | 179 | ```Kotln 180 | // This function launches a new counter actor 181 | fun CoroutineScope.counterActor() = actor { 182 | var counter = 0 // actor state 183 | for (msg in channel) { // iterate over incoming messages 184 | when (msg) { 185 | is IncCounter -> counter++ 186 | is GetCounter -> msg.response.complete(counter) 187 | } 188 | } 189 | } 190 | ``` 191 | 192 | 主要代码很简单: 193 | 194 | ```Kotlin 195 | fun main() = runBlocking { 196 | val counter = counterActor() // create the actor 197 | withContext(Dispatchers.Default) { 198 | massiveRun { 199 | counter.send(IncCounter) 200 | } 201 | } 202 | // send a message to get a counter value from an actor 203 | val response = CompletableDeferred() 204 | counter.send(GetCounter(response)) 205 | println("Counter = ${response.await()}") 206 | counter.close() // shutdown the actor 207 | } 208 | ``` 209 | 210 | 正确执行 actor 本身在什么上下文中都没有关系(正确性)。actor 是一个协程,而协程是顺序执行的,因此将状态限制为特定协程可以解决共享可变状态的问题。实际上,参与者可以修改自己的私有状态,但只能通过消息相互影响(避免使用任何锁)。 211 | 212 | Actor比在负载锁定更有效,因为在这种情况下,Actor总是有工作要做,并且根本不必切换到其他上下文。 213 | 214 | 请注意, [actor](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/actor.html) 协程构建器是 [produce](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/produce.html) 协程构建器的对偶。actor 有一个接收消息的通道相关联,而生产者与其发送元素的通道相关联。 -------------------------------------------------------------------------------- /ClassesAndObjects/Classes-and-Inheritance.md: -------------------------------------------------------------------------------- 1 | ## 类和继承 2 | ### 类 3 | 4 | 在 Kotlin 中类用 `class` 时: 5 | 6 | ```kotlin 7 | class Invoice { 8 | } 9 | ``` 10 | 11 | 类的声明包含类名,类头(指定类型参数,主构造函数等等),以及类主体,用大括号包裹。类头和类体是可选的;如果没有类体可以省略大括号。 12 | 13 | ```kotlin 14 | class Empty 15 | ``` 16 | 17 | ### 构造函数 18 | 在 Kotlin 中类可以有一个主构造函数以及多个二级构造函数。主构造函数是类头的一部分:跟在类名后面(可以有可选的类型参数)。 19 | 20 | ```kotlin 21 | class Person constructor(firstName: String) { 22 | } 23 | ``` 24 | 25 | 如果主构造函数没有注解或可见性说明,则 `constructor` 关键字是可以省略: 26 | 27 | ```korlin 28 | class Person(firstName: String){ 29 | } 30 | ``` 31 | 32 | 主构造函数不能包含任意代码。初始化代码可以放在以 `init` 做前缀的初始化块内 33 | 34 | ```kotlin 35 | class Customer(name: String) { 36 | init { 37 | logger,info("Customer initialized with value ${name}") 38 | } 39 | } 40 | ``` 41 | 42 | 注意主构造函数的参数可以用在初始化块内,也可以用在类的属性初始化声明处: 43 | 44 | ```kotlin 45 | class Customer(name: String) { 46 | val customerKry = name.toUpperCase() 47 | } 48 | ``` 49 | 50 | 事实上,声明属性并在主构造函数中初始化,在 Kotlin 中有更简单的语法: 51 | 52 | ```kotlin 53 | class Person(val firstName: String, val lastName: String, var age: Int) { 54 | } 55 | ``` 56 | 57 | 就像普通的属性,在主构造函数中的属性可以是可变的(`var`)或只读的(`val`)。 58 | 59 | 如果构造函数有注解或可见性声明,则 `constructor` 关键字是不可少的,并且可见性应该在前: 60 | 61 | ```kotlin 62 | class Customer public @inject constructor (name: String) {...} 63 | ``` 64 | 65 | 参看[可见性](http://kotlinlang.org/docs/reference/visibility-modifiers.html#constructors) 66 | 67 | ### 二级构造函数 68 | 类也可以有二级构造函数,需要加前缀 `constructor`: 69 | 70 | ```kotlin 71 | class Person { 72 | constructor(parent: Person) { 73 | parent.children.add(this) 74 | } 75 | } 76 | ``` 77 | 78 | 如果类有主构造函数,每个二级构造函数都要,或直接或间接通过另一个二级构造函数代理主构造函数。在同一个类中代理另一个构造函数使用 `this` 关键字: 79 | 80 | ```kotlin 81 | class Person(val name: String) { 82 | constructor (name: String, paret: Person) : this(name) { 83 | parent.children.add(this) 84 | } 85 | } 86 | ``` 87 | 88 | 如果一个非抽象类没有声明构造函数(主构造函数或二级构造函数),它会产生一个没有参数的构造函数。该构造函数的可见性是 public 。如果你不想你的类有公共的构造函数,你就得声明一个拥有非默认可见性的空主构造函数: 89 | 90 | ```kotlin 91 | class DontCreateMe private constructor () { 92 | } 93 | ``` 94 | 95 | >注意:在 JVM 虚拟机中,如果主构造函数的所有参数都有默认值,编译器会生成一个附加的无参的构造函数,这个构造函数会直接使用默认值。这使得 Kotlin 可以更简单的使用像 Jackson 或者 JPA 这样使用无参构造函数来创建类实例的库。 96 | 97 | ```kotlin 98 | class Customer(val customerName: String = "") 99 | ``` 100 | 101 | ### 创建类的实例 102 | 我们可以像使用普通函数那样使用构造函数创建类实例: 103 | 104 | ```kotlin 105 | val invoice = Invoice() 106 | val customer = Customer("Joe Smith") 107 | ``` 108 | 109 | 注意 Kotlin 没有 `new` 关键字。 110 | 111 | 创建嵌套类、内部类或匿名类的实例参见[嵌套类](http://kotlinlang.org/docs/reference/nested-classes.html) 112 | 113 | ### 类成员 114 | 类可以包含: 115 | >-- 构造函数和初始化代码块 116 | 117 | >-- [函数](FunctionsAndLambdas/Functions.md) 118 | 119 | >-- [属性](ClassesAndObjects/Properties-and-Fields.md)  120 | 121 | >-- [内部类](ClassesAndObjects/NestedClasses.md) 122 | 123 | >-- [对象声明](ClassesAndObjects/ObjectExpressicAndDeclarations.md) 124 | 125 | ### 继承 126 | Kotlin 中所有的类都有共同的父类 `Any` ,它是一个没有父类声明的类的默认父类: 127 | 128 | ```kotlin 129 | class Example // 隐式继承于 Any 130 | ``` 131 | 132 | `Any` 不是 `java.lang.Object`;事实上它除了 `equals()`,`hashCode()`以及`toString()`外没有任何成员了。参看[Java interoperability]( Java interoperability)了解更多详情。 133 | 134 | 声明一个明确的父类,需要在类头后加冒号再加父类: 135 | 136 | ```kotlin 137 | open class Base(p: Int) 138 | 139 | class Derived(p: Int) : Base(p) 140 | ``` 141 | 142 | 如果类有主构造函数,则基类可以而且是必须在主构造函数中使用参数立即初始化。 143 | 144 | 如果类没有主构造函数,则必须在每一个构造函数中用 `super` 关键字初始化基类,或者在代理另一个构造函数做这件事。注意在这种情形中不同的二级构造函数可以调用基类不同的构造方法: 145 | 146 | ```kotlin 147 | class MyView : View { 148 | constructor(ctx: Context) : super(ctx) { 149 | } 150 | constructor(ctx: Context, attrs: AttributeSet) : super(ctx,attrs) { 151 | } 152 | } 153 | ``` 154 | 155 | `open`注解与java中的`final`相反:它允许别的类继承这个类。默认情形下,kotlin 中所有的类都是 final ,对应 [Effective Java](http://www.oracle.com/technetwork/java/effectivejava-136174.html) :Design and document for inheritance or else prohibit it. 156 | 157 | ### 复写方法 158 | 像之前提到的,我们在 kotlin 中坚持做明确的事。不像 java ,kotlin 需要把可以复写的成员都明确注解出来,并且重写它们: 159 | 160 | ```kotlin 161 | open class Base { 162 | open fun v() {} 163 | fun nv() {} 164 | } 165 | 166 | class Derived() : Base() { 167 | override fun v() {} 168 | } 169 | ``` 170 | 171 | 对于 `Derived.v()` 来说`override`注解是必须的。如果没有加的话,编译器会提示。如果没有`open`注解,像 `Base.nv()` ,在子类中声明一个同样的函数是不合法的,要么加`override`要么不要复写。在 final 类(就是没有open注解的类)中,`open` 类型的成员是不允许的。 172 | 173 | 标记为`override`的成员是open的,它可以在子类中被复写。如果你不想被重写就要加 final: 174 | 175 | ```kotlin 176 | open class AnotherDerived() : Base() { 177 | final override fun v() {} 178 | } 179 | ``` 180 | 181 | **等等!我现在怎么hack我的库?!** 182 | 183 | 有个问题就是如何复写子类中那些作者不想被重写的类,下面介绍一些令人讨厌的方案。 184 | 185 | 我们认为这是不好的,原因如下: 186 | 187 | > 最好的实践建议你不应给做这些 hack 188 | 189 | >人们可以用其他的语言成功做到类似的事情 190 | 191 | >如果你真的想 hack 那么你可以在 java 中写好 hack 方案,然后在 kotlin 中调用 (参看[java调用](http://kotlinlang.org/docs/reference/java-interop.html)),专业的构架可以很好的做到这一点 192 | 193 | ### 复写属性 194 | 复写属性与复写方法类似,在一个父类上声明的属性在子类上被重新声明,必须添加`override`,并且它们必须具有兼容的类型。每个被声明的属性都可以被一个带有初始化器的属性或带有getter方法的属性覆盖 195 | 196 | ```kotlin 197 | open class Foo { 198 | open val x: Int get { ... } 199 | } 200 | 201 | class Bar1 : Foo() { 202 | override val x: Int = ... 203 | } 204 | ``` 205 | 206 | 您还可以使用`var`属性覆盖一个`val`属性,但反之则不允许。这是允许的,因为`val`属性本质上声明了一个getter方法,并将其重写为`var`,另外在派生类中声明了setter方法。 207 | 208 | 注意,可以在主构造函数中使用`override`关键字作为属性声明的一部分。 209 | 210 | ```kotlin 211 | interface Foo { 212 | val count: Int 213 | } 214 | 215 | class Bar1(override val count: Int) : Foo 216 | 217 | class Bar2 : Foo { 218 | override var count: Int = 0 219 | } 220 | ``` 221 | 222 | ### 复写规则 223 | 在 kotlin 中,实现继承通常遵循如下规则:如果一个类从它的直接父类继承了同一个成员的多个实现,那么它必须复写这个成员并且提供自己的实现(或许只是直接用了继承来的实现)。为表示使用父类中提供的方法我们用 `super`表示: 224 | 225 | ```kotlin 226 | open class A { 227 | open fun f () { print("A") } 228 | fun a() { print("a") } 229 | } 230 | 231 | interface B { 232 | fun f() { print("B") } // 接口的成员变量默认是 open 的 233 | fun b() { print("b") } 234 | } 235 | 236 | class C() : A() , B { 237 | // 编译器会要求复写f() 238 | override fun f() { 239 | super.f() // 调用 A.f() 240 | super.f() // 调用 B.f() 241 | } 242 | } 243 | ``` 244 | 245 | 可以同时从 A 和 B 中继承方法,而且 C 继承 a() 或 b() 的实现没有任何问题,因为它们都只有一个实现。但是 f() 有俩个实现,因此我们在 C 中必须复写 f() 并且提供自己的实现来消除歧义。 246 | 247 | ### 抽象类 248 | 一个类或一些成员可能被声明成 abstract 。一个抽象方法在它的类中没有实现方法。记住我们不用给一个抽象类或函数添加 open 注解,它默认是带着的。 249 | 250 | 我们可以用一个抽象成员去复写一个带 open 注解的非抽象方法。 251 | 252 | ```kotlin 253 | open class Base { 254 | open fun f() {} 255 | } 256 | 257 | abstract class Derived : Base() { 258 | override abstract fun f() 259 | } 260 | ``` 261 | 262 | ### 伴随对象 263 | 在 kotlin 中不像 java 或者 C# 它没有静态方法。在大多数情形下,我们建议只用包级别的函数。 264 | 265 | 如果你要写一个没有实例类就可以调用的方法,但需要访问到类内部(比如说一个工厂方法),你可以把它写成它所在类的一个[成员](http://kotlinlang.org/docs/reference/object-declarations.html)(you can write it as a member of an object declaration inside that class) 266 | 267 | 更高效的方法是,你可以在你的类中声明一个[伴随对象](http://kotlinlang.org/docs/reference/object-declarations.html#companion-objects),这样你就可以像 java/c# 那样把它当做静态方法调用,只需要它的类名做一个识别就好了 268 | 269 | ### 密封类 270 | 密封类用于代表严格的类结构,值只能是有限集合中的某种类型,不可以是任何其它类型。这就相当于一个枚举类的扩展:枚举值集合的类型是严格限制的,但每个枚举常量只有一个实例,而密封类的子类可以有包含不同状态的多个实例。 271 | 272 | 声明密封类需要在 class 前加一个 sealed 修饰符。密封类可以有子类但必须全部嵌套在密封类声明内部、 273 | 274 | ```Kotlin 275 | sealed class Expr { 276 | class Const(val number: Double) : Expr() 277 | class Sum(val e1: Expr, val e2: Expr) : Expr() 278 | object NotANumber : Expr() 279 | } 280 | ``` 281 | 282 | 注意密封类子类的扩展可以在任何地方,不必在密封类声明内部进行。 283 | 284 | 使用密封类的最主要的的好处体现在你使用 [when 表达式]()。可以确保声明可以覆盖到所有的情形,不需要再使用 else 情形。 285 | 286 | ```Kotlin 287 | fun eval(expr: Expr): Double = when(expr) { 288 | is Const -> expr.number 289 | is Sum -> eval(expr.e1) + eval(expr.e2) 290 | NotANumber -> Double.NaN 291 | // the `else` clause is not required because we've covered all the cases 292 | } 293 | ``` 294 | -------------------------------------------------------------------------------- /GettingStarted/what-new-in-1.1.md: -------------------------------------------------------------------------------- 1 | # Kotlin 1.1 新特性 2 | 新特性列表 3 | 4 | -协程 5 | -其它语言特性 6 | -标准库 7 | -JVM 后端 8 | -JavaScript 后端 9 | 10 | ## JavaScript 11 | 从 Kotlin 1.1 开始,JavaScript 支持不再是实验性的了。所有特性均支持,并为前端开发环境提过了大量新的工具。参看下文了解详细改变 12 | 13 | ## 协程 14 | Kotlin 1.1关键的新特性就是协程,带来了像 `async/wait`,`yield` 这样的编程模式。Kotlin 设计的关键特性是所有协程都是由库实现的,而不是语言。所以你不需要与任何特定的编程范例或者并行库进行绑定。 15 | 16 | 协程是一个高效轻量级的线程,可以挂起并稍后恢复执行。协程是又挂起函数支持的:调用这个函数可能会挂起一个协程,并开启一个新的协程,大多数情况下采用匿名挂起函数(也就是可挂起lambda表达式) 17 | 18 | 让我们看一下在[kotlinx.coroutines](https://github.com/kotlin/kotlinx.coroutines)库中实现的`async`/`await`: 19 | 20 | ``` 21 | // 在后台线程池中运行代码 fun asyncOverlay() = async(CommonPool) { // 开启两个异步操作 val original = asyncLoadImage("original") val overlay = asyncLoadImage("overlay") // and then apply overlay to both results applyOverlay(original.await(), overlay.await()) } // 在 UI 上下文中中开启新的协程 launch(UI) { // wait for async overlay to complete val image = asyncOverlay().await() // and then show it in UI showImage(image) } ``` 22 | 23 | `async{...}` 开启协程,当调用 `await()` 时协程被挂起,等待 original 和 overlay 的执行结果,当两者完成时恢复执行。 24 | 25 | 标准库用协程支持 *lazily generated sequences* 懒生成序列,主要使用 `yield` 和 `yieldAll` 函数。在这样的队列中,返回序列会在每次元素被取出后暂停,并在下次取元素是恢复,例子: 26 | 27 | ```kotlin 28 | val seq = buildSequence { 29 | for (i in 1..5) { 30 | // yield a square of i 31 | yield(i * i) 32 | } 33 | // yield a range 34 | yieldAll(26..28) 35 | } 36 | 37 | // print the sequence 38 | println(seq.toList()) 39 | ``` 40 | 41 | 更多信息请参看 [coroutine documentation ](https://kotlinlang.org/docs/reference/coroutines.html) 以及 [tutorials](https://kotlinlang.org/docs/tutorials/coroutines-basic-jvm.html) 42 | 43 | ## 其它语言特性 44 | ### 类型别名 45 | 类型别名允许给现有的类型定义别名。主要在集合,函数类型中很常用。例子: 46 | 47 | ```kotlin 48 | typealias OscarWinners = Map 49 | 50 | fun countLaLaLand(oscarWinners: OscarWinners) = 51 | oscarWinners.count { it.value.contains("La La Land") } 52 | 53 | // Note that the type names (initial and the type alias) are interchangeable: 54 | fun checkLaLaLandIsTheBestMovie(oscarWinners: Map) = 55 | oscarWinners["Best picture"] == "La La Land" 56 | ``` 57 | 参看[documentation](https://kotlinlang.org/docs/reference/type-aliases.html)[KEEP](https://github.com/Kotlin/KEEP/blob/master/proposals/type-aliases.md) 了解更多详细信息。 58 | 59 | ### Bound callable references(绑定可执行引用 暂时没想好怎么翻译,如果你有好的建议请发issue) 60 | 使用 `::` 操作符可以获取一个指向特定对象实例的方法或者熟悉的成员引用。之前这只能用在 lambda 表达式上。例子: 61 | 62 | ```Kotlin 63 | val numberRegex = "\\d+".toRegex() 64 | val numbers = listOf("abc", "123", "456").filter(numberRegex::matches) 65 | ``` 66 | 67 | 参看[documentation](https://kotlinlang.org/docs/reference/reflection.html#bound-function-and-property-references-since-11)[KEEP](https://github.com/Kotlin/KEEP/blob/master/proposals/type-aliases.md) 了解更多详细信息。 68 | 69 | ## 密封类和数据类 70 | Kotlin 1.1 移除了一些密封类和数据类的限制。现在能在同一个文件中定义顶级密封类类的子类,而不必是内嵌类或者密封类类。数据类现在可以扩展其它类。这样可以更优雅简洁的定义表达式类的等级: 71 | 72 | ```Kotlin 73 | sealed class Expr 74 | 75 | data class Const(val number: Double) : Expr() 76 | data class Sum(val e1: Expr, val e2: Expr) : Expr() 77 | object NotANumber : Expr() 78 | 79 | fun eval(expr: Expr): Double = when (expr) { 80 | is Const -> expr.number 81 | is Sum -> eval(expr.e1) + eval(expr.e2) 82 | NotANumber -> Double.NaN 83 | } 84 | val e = eval(Sum(Const(1.0), Const(2.0))) 85 | ``` 86 | 参看 [documentation](https://kotlinlang.org/docs/reference/sealed-classes.html#relaxed-rules-for-sealed-classes-since-11) or [sealed class](https://github.com/Kotlin/KEEP/blob/master/proposals/sealed-class-inheritance.md) 和 [data class](https://github.com/Kotlin/KEEP/blob/master/proposals/data-class-inheritance.md) KEEPs 获取更详细的信息。 87 | 88 | ## lambdas 表达式的解构 89 | 现在可以使用[destucting declaration](https://kotlinlang.org/docs/reference/multi-declarations.html) 语法取出 lambda 中的参数: 90 | 91 | ```kotlin 92 | val map = mapOf(1 to "one", 2 to "two") 93 | // before 94 | println(map.mapValues { entry -> 95 | val (key, value) = entry 96 | "$key -> $value!" 97 | }) 98 | // now 99 | println(map.mapValues { (key, value) -> "$key -> $value!" }) 100 | ``` 101 | 102 | 参看[documentation](https://kotlinlang.org/docs/reference/multi-declarations.html#destructuring-in-lambdas-since-11) 和 [KEEP](https://github.com/Kotlin/KEEP/blob/master/proposals/destructuring-in-parameters.md) 获取更详细的信息。 103 | 104 | ## 用下划线标注下未使用的参数 105 | 对于有多个参数的 lambda ,你可以用 `_` 字符代替你不使用的参数。 106 | 107 | ```kotlin 108 | map.forEach { _, value -> println("$value!") } 109 | ``` 110 | 111 | 在[destructuring declarations](https://kotlinlang.org/docs/reference/multi-declarations.html) 中也可以用 112 | 113 | ```kotlin 114 | val (_, status) = getResult() 115 | ``` 116 | 117 | 阅读[KEEP](https://github.com/Kotlin/KEEP/blob/master/proposals/underscore-for-unused-parameters.md)获取更详细信息 118 | 119 | ## 数字值中的下划线 120 | 像 java8 一样 Kotlin 现在支持在数字值中使用下划线划分组: 121 | 122 | ```Kotlin 123 | val oneMillion = 1_000_000 124 | val hexBytes = 0xFF_EC_DE_5E 125 | val bytes = 0b11010010_01101001_10010100_10010010 126 | ``` 127 | 128 | 阅读[[KEEP](https://github.com/Kotlin/KEEP/blob/master/proposals/underscores-in-numeric-literals.md)获取更详细信息 129 | 130 | ## 属性简写 131 | 用表达式定义个 get 属性,现在可以省略属性类型了: 132 | 133 | ```kotlin 134 | data class Person(val name: String, val age: Int){ 135 | val isAdult get() = age >= 20 // 属性类型被推断为 'Boolean' 136 | } 137 | ``` 138 | 139 | ## 内联属性访问器 140 | 现在没有 backing field 的属性访问器可以用 'inline' 修饰。 141 | 142 | 这些属性访问器和内联函数的编译方式是一样的。 143 | 144 | ```kotlin 145 | public val List.lastIndex: Int 146 | inline get() = this.size -1 147 | ``` 148 | 149 | [documentation](http://kotlinlang.org/docs/reference/inline-functions.html#inline-properties-since-11)[KEEP](https://github.com/Kotlin/KEEP/blob/master/proposals/inline-properties.md) 了解更多详细信息。 150 | 151 | ## 本地代理属性 152 | 现在可以在本地变量上使用 [代理属性](http://kotlinlang.org/docs/reference/delegated-properties.html) 了。一个应用场景就是定义一个懒求值的本地变量: 153 | 154 | ```kotlin 155 | val answer by lazy { 156 | println("Calculating the answer...") 157 | 42 158 | } 159 | if (needAnswer()) { // returns the random value 160 | println("The answer is $answer.") // answer is calculated at this point 161 | } 162 | else { 163 | println("Sometimes no answer is the answer...") 164 | } 165 | ``` 166 | 167 | 阅读 [KEEP](https://github.com/Kotlin/KEEP/blob/master/proposals/local-delegated-properties.md) 了解更多详细信息。 168 | 169 | ## 拦截代理属性绑定 170 | 代理属性现在可以通过 'provideDelegate' 运算符拦截属性绑定。比如,在绑定属性名之前,想要检查属性名字,可以像下面这样做: 171 | 172 | ```kotlin 173 | class ResourceLoader(id: ResourceID) { 174 | operator fun provideDelegate(thisRef: MyUI, prop: KProperty<*>): ReadOnlyProperty { 175 | checkProperty(thisRef, prop.name) 176 | ... // property creation 177 | } 178 | 179 | private fun checkProperty(thisRef: MyUI, name: String) { ... } 180 | } 181 | 182 | fun bindResource(id: ResourceID): ResourceLoader { ... } 183 | 184 | class MyUI { 185 | val image by bindResource(ResourceID.image_id) 186 | val text by bindResource(ResourceID.text_id) 187 | } 188 | ``` 189 | 190 | 'provideDelegate' 方法可以可以在创建 MyUI 实例的每个属性时调用,并做相应的验证检查。 191 | 192 | ## 通用枚举值的访问 193 | 现在可以用通用方法罗列枚举类的所有值。 194 | 195 | ```kotlin 196 | enum class RGB { RED, GREEN, BLUE } 197 | 198 | inline fun > printAllValues() { 199 | print(enumValues().joinToString { it.name }) 200 | } 201 | 202 | ## 标准库 203 | ### string to number 的转换 204 | 在 String 类新增了转为数字而不抛出异常的扩展: String.toIntOrNull(): Int?, String.toDoubleOrNull(): Double? etc. 205 | 206 | val port = System.getenv("PORT")?.toIntOrNull() ?: 80 207 | 208 | ### onEach() 209 | 'onEach' 是一个很小的扩展,但却对集合和序列很有用,这样就可以对集合和队列采用链式调用执行一些操作。 210 | 211 | to be continue 212 | -------------------------------------------------------------------------------- /ClassesAndObjects/DelegationProperties.md: -------------------------------------------------------------------------------- 1 | ## 委托属性 2 | Kotlin 很多常用属性,虽然我们可以在每次需要的时候手动实现它们,但更好的办法是一次实现多次使用,并放到库里。比如: 3 | 4 | > 延迟属性:只在第一次访问时计算它的值。 5 | > 可观察属性:监听者从这获取这个属性更新的通知。 6 | > 把多个属性储存在一个映射(map)中,而不是每个存在单独的字段中。 7 | 8 | 为了满足这些情形,Kotlin 支持委托属性: 9 | 10 | ```kotlin 11 | class Example { 12 | var p: String by Delegate() 13 | } 14 | ``` 15 | 16 | 语法结构是: `val/var : by ` 在 by 后面的表达式就是*委托*,因为`get()` `set()` 对应的属性会被 `getValue()` `setValue()` 方法委托。属性委托不需要任何接口的实现,但必须要提供 `getValue()` 方法(如果是 var 还需要 `setValue()`)。像这样: 17 | 18 | ```kotlin 19 | class Delegate { 20 | operator fun getValue(thisRef: Any?, property: KProperty<*>): String { 21 | return "$thisRef, thank you for delegating '${property.name}' to me!" 22 | } 23 | 24 | operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { 25 | println("$value has been assigned to '${property.name} in $thisRef.'") 26 | } 27 | } 28 | ``` 29 | 30 | 当我们从 `p` 也就委托给 `Delegate` 的一个实例进行读取操作时,会调用 `Delegate` 的 `getValue()` 方法,因此第一个参数是我们从 `p` 中读取的,第二个参数是持有 `p` 的一个描述。比如: 31 | 32 | ```kotlin 33 | val e = Example() 34 | println(e.p) 35 | ``` 36 | 37 | 打印结果:  38 | 39 | >Example@33a17727, thank you for delegating ‘p’ to me! 40 | 41 | 同样当我们给 `p` 赋值时 `setValue()` 函数就将被调用。前两个参数是一样的,第三个持有分配的值: 42 | 43 | ```kotlin 44 | e.p = "NEW" 45 | ``` 46 | 47 | 打印结果:  48 | 49 | >NEW has been assigned to ‘p’ in Example@33a17727. 50 | 51 | 从 Kotlin 1.1 开始支持在函数内部或者代码块内声明委托,而不必是类成员。你可以在后面的例子中找到用法 52 | 53 | ### 标准委托 54 | Kotlin 标准库为几种常用的委托提供了工厂方法 55 | 56 | #### 延迟 57 | `lazy()` 是接受一个 lambda 并返回一个 `Lazy ` 实例的函数,返回的实例可以作为实现延迟属性的委托:第一次调用 `get()` 执行传递给 `lazy()` 的lamdba,并存储结果,以后每次调用 `get()` 时只是简单返回之前存储的值。 58 | 59 | ```kotlin 60 | val lazyValue: String by lazy { 61 | println("computed!") 62 | "Hello" 63 | } 64 | 65 | fun main(args: Array) { 66 | println(lazyValue) 67 | println(lazyValue) 68 | } 69 | ``` 70 | 71 | 上面代码的输出是: 72 | 73 | ``` 74 | computed! 75 | Hello 76 | Hello 77 | ``` 78 | 79 | 默认情况下延迟属性的计算是同步的:该值的计算只在一个线程里,其他所有线程都将读取同样的值。如果委托不需要同步初始化,而且允许出现多线程同时执行该操作,可以传 `LazyThreadSafetyMode.PUBLICATION` 参数给 `lazy()` 。如果你确信初始化只会在单线程中出现,那么可以使用 `LazyThreadSafetyMode.NONE` 该模式不会提供任何线程安全相关的保障。 80 | 81 | 如果你想要线程安全,使用 `blockingLazy()`: 它还是按照同样的方式工作,但保证了它的值只会在一个线程中计算,并且所有的线程都获取的同一个值。 82 | 83 | #### 可观察属性 84 | `Delegates.observable()` 需要两个参数:一个初始值和一个用于修改的 handler 。每次我们给属性赋值时都会调用handler (在初始赋值操作后)。它有三个参数:一个将被赋值的属性,旧值,新值: 85 | 86 | ```kotlin 87 | import kotlin.properties.Delegates 88 | 89 | class User { 90 | var name: String by Delegates.observable("") { 91 | prop, old, new -> 92 | println("$old -> $new") 93 | } 94 | } 95 | 96 | fun main(args: Array) { 97 | val user = User() 98 | user.name = "first" 99 | user.name = "second" 100 | } 101 | ``` 102 | 打印结果 103 | 104 | > \ -> first   105 | > first -> second 106 | 107 | 如果你想打断赋值并“否决”它,就使用 [`vetoable()`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlinproperties/-delegates/vetoable.html) 取代 `observable()`。在属性被赋新值生效之前会调用传递给 `vetoable` 的处理程序。 108 | 109 | #### 在 Map 中存储属性 110 | 把属性值存储在 map 中是一种常见的使用方式,这种操作经常出现在解析 JSON 或者其它动态的操作中。这种情况下你可以使用 map 来委托它的属性。 111 | 112 | ```kotlin 113 | class User(val map: Map) { 114 | val name: String by map 115 | val age: Int by map 116 | } 117 | ``` 118 | 119 | 在这个例子中,构造函数接收一个 map : 120 | 121 | ```kotlin 122 | val user = User(mapOf( 123 | "name" to "John Doe", 124 | "age" to 25 125 | )) 126 | ``` 127 | 128 | 委托属性将从这个 map 中取值(通过属性的名字): 129 | 130 | ```kotlin 131 | println(user.name) // Prints "John Doe" 132 | println(user.age) // Prints 25 133 | ``` 134 | 135 | var 属性可以用 `MutableMap` 代替只读的 `Map`: 136 | 137 | ```kotlin 138 | class MutableUser(val map: MutableMap) { 139 | var name: String by map 140 | var age: Int by map 141 | } 142 | ``` 143 | 144 | ### 本地委托属性(从1.1开始支持) 145 | 146 | 你可以声明本地变量作为委托属性。比如你可以创建一个本地延迟变量: 147 | 148 | ```kotlin 149 | fun example(computeFoo: () -> Foo) { 150 | val memoizedFoo by lazy(computeFoo) 151 | 152 | if (someCondition && memoizedFoo.isValid()) { 153 | memoizedFoo.doSomething() 154 | } 155 | } 156 | ``` 157 | 158 | `memoizedFoo` 只会在第一次访问时求值。如果 `someCondition` 不符合,那么该变量将根本不会被计算。 159 | 160 | ### 属性委托的要求 161 | 162 | 这里总结一些委托对象的要求。 163 | 164 | 只读属性 (val),委托必须提供一个名字叫 `getValue` 的函数并接受如下参数: 165 | 166 | > `thisRef`接收者--必须于属性拥有者是同一种类型,或者是其父类 (对于扩展属性——指被扩展的类型) 167 | 168 | > `property` 必须是 KProperty<*> 或者它的父类 169 | 170 | 这个函数必须返回同样的类型或子类作为属性。 171 | 172 | 可变属性 (var),委托必须添加一个叫 `setValue` 的函数并接受如下参数: 173 | 174 | > `thisRef `与 `getValue()` 一样 175 | > 176 | > `property` 与 `getValue()` 一样 177 | > 178 | > 新值--必须和属性类型一致或是它的父类 179 | 180 | `getValue()` 和 `setValue()` 函数可以由委托类的成员函数或者扩展函数提供。扩展函数对与想要对对象委托原本没有的函数来说很方便。两种函数必须标记 `operator` 关键字。 181 | 182 | 委托类可能实现 `ReadOnlyProperty` 和 `ReadWriteProperty` 中的一个并要求带有 `operator` 方法。这些接口在 Kotlin 标准库中有声明: 183 | 184 | ```kotlin 185 | interface ReadOnlyProperty { 186 | operator fun getValue(thisRef: R, property: KProperty<*>): T 187 | } 188 | 189 | interface ReadWriteProperty { 190 | operator fun getValue(thisRef: R, property: KProperty<*>): T 191 | operator fun setValue(thisRef: R, property: KProperty<*>, value: T) 192 | } 193 | ``` 194 | 195 | ### 转换规则 196 | 197 | 在每个委托属性的实现的背后,Kotlin 编译器都会生成辅助属性并委托给它。 例如,对于属性 `prop`,生成隐藏属性 `prop$delegate`,而访问器的代码只是简单地委托给这个附加属性: 198 | 199 | ```kotlin 200 | class C { 201 | var prop: Type by MyDelegate() 202 | } 203 | 204 | // this code is generated by the compiler instead: 205 | class C { 206 | private val prop$delegate = MyDelegate() 207 | var prop: Type 208 | get() = prop$delegate.getValue(this, this::prop) 209 | set(value: Type) = prop$delegate.setValue(this, this::prop, value) 210 | } 211 | ``` 212 | 213 | Kotlin 编译器在参数中提供了关于 `prop` 的所有必要信息:第一个参数 `this` 引用到类 `C` 外部的实例而 `this::prop` 是 `KProperty` 类型的反射对象,该对象描述 `prop` 自身。 214 | 215 | 注意,直接在代码中引用[绑定的可调用引用](http://kotlinlang.org/docs/reference/reflection.html#bound-function-and-property-references-since-11)的语法 `this::prop` 自 Kotlin 1.1 起才可用。 216 | 217 | ### 提供委托(自 1.1 起) 218 | 219 | 通过定义 `provideDelegate` 操作符,可以扩展创建属性实现所委托对象的逻辑。 如果 `by` 右侧所使用的对象将 `provideDelegate` 定义为成员或扩展函数,那么会调用该函数来创建属性委托实例。 220 | 221 | `provideDelegate` 的一个可能的使用场景是在创建属性时检查属性一致性。 222 | 223 | 例如,如果你想要在绑定之前检查属性名称,可以这样写: 224 | 225 | ```kotlin 226 | class ResourceLoader(id: ResourceID) { 227 | operator fun provideDelegate( 228 | thisRef: MyUI, 229 | prop: KProperty<*> 230 | ): ReadOnlyProperty { 231 | checkProperty(thisRef, prop.name) 232 | // create delegate 233 | } 234 | 235 | private fun checkProperty(thisRef: MyUI, name: String) { ... } 236 | } 237 | 238 | fun bindResource(id: ResourceID): ResourceLoader { ... } 239 | 240 | class MyUI { 241 | val image by bindResource(ResourceID.image_id) 242 | val text by bindResource(ResourceID.text_id) 243 | } 244 | ``` 245 | 246 | `provideDelegate` 的参数与 `getValue` 相同: 247 | 248 | - `thisRef` —— 必须与 *属性所有者* 类型(对于扩展属性——指被扩展的类型)相同或者是它的超类型, 249 | - `property` —— 必须是类型 `KProperty<*>` 或其超类型。 250 | 251 | 在创建 `MyUI` 实例期间,为每个属性调用 `provideDelegate` 方法,并立即执行必要的验证。 252 | 253 | 如果没有这种打断属性与其委托之间的绑定的能力,为了实现相同的功能, 你必须显式传递属性名,这不是很方便: 254 | 255 | ```kotlin 256 | // Checking the property name without "provideDelegate" functionality 257 | class MyUI { 258 | val image by bindResource(ResourceID.image_id, "image") 259 | val text by bindResource(ResourceID.text_id, "text") 260 | } 261 | 262 | fun MyUI.bindResource( 263 | id: ResourceID, 264 | propertyName: String 265 | ): ReadOnlyProperty { 266 | checkProperty(this, propertyName) 267 | // create delegate 268 | } 269 | ``` 270 | 271 | 在生成的代码中, `provideDelegate` 方法用来初始化辅助 `prop$delegate` 属性的初始化。 下面是属性声明 `val prop: Type by MyDelegate()` 生成的代码与 [上面](http://kotlinlang.org/docs/reference/delegated-properties.html#translation-rules)(当 `provideDelegate` 方法不存在时)生成的代码的对比: 272 | 273 | ```kotlin 274 | class C { 275 | var prop: Type by MyDelegate() 276 | } 277 | 278 | // this code is generated by the compiler 279 | // when the 'provideDelegate' function is available: 280 | class C { 281 | // calling "provideDelegate" to create the additional "delegate" property 282 | private val prop$delegate = MyDelegate().provideDelegate(this, this::prop) 283 | val prop: Type 284 | get() = prop$delegate.getValue(this, this::prop) 285 | } 286 | ``` 287 | 288 | 注意,`provideDelegate` 方法只影响辅助属性的创建,并不会影响为 getter 或 setter 生成的代码。 289 | -------------------------------------------------------------------------------- /ClassesAndObjects/Generics.md: -------------------------------------------------------------------------------- 1 | ## 泛型 2 | 像 java 一样,Kotlin 中的类可以拥有类型参数: 3 | 4 | ```kotlin 5 | class Box(t: T){ 6 | var value = t 7 | } 8 | ``` 9 | 10 | 通常来说,创建一个这样类的实例,我们需要提供类型参数: 11 | 12 | ```kotlin 13 | val box: Box = Box(1) 14 | ``` 15 | 16 | 但如果类型有可能是推断的,比如来自构造函数的参数或者通过其它的一些方式,一个可以忽略类型的参数: 17 | 18 | ```kotin 19 | val box = Box(1)//1是 Int 型,因此编译器会推导出我们调用的是 Box 20 | ``` 21 | 22 | ### 变型 23 | java 类型系统最棘手的一部分就是通配符类型。但 kotlin 没有,代替它的是两种其它的东西:声明变型和类型投影(declaration-site variance and type projections)。 24 | 25 | 首先,我们想想为什么 java 需要这些神秘的通配符。这个问题在[Effective Java](http://www.oracle.com/technetwork/java/effectivejava-136174.html),条目18中是这样解释的:使用界限通配符增加 API 的灵活性。首先 java 中的泛型是不变的,这就意味着 `List` 不是 `List` 的子类型。为什么呢,如果 List 不是不变的,就会引发下面的问题: 26 | 27 | ```java 28 | // Java 29 | List strs = new ArrayList(); 30 | List objs = strs; // !!! The cause of the upcoming problem sits here. Java prohibits this! 31 | objs.add(1); // Here we put an Integer into a list of Strings 32 | String s = strs.get(0); // !!! ClassCastException: Cannot cast Integer to String 33 | ``` 34 | 因此 java 禁止了这样的事情来保证运行时安全。但这有些其它影响。比如,`Collection` 接口的 `addAll()` 方法。这个方法的签名在哪呢?直觉告诉我们应该是这样的: 35 | 36 | ```java 37 | //java 38 | interface Collection ... { 39 | void addAll(Collection items); 40 | } 41 | ``` 42 | 但接下来我们就不能做下面这些操作了(虽然这些操作都是安全的): 43 | 44 | ```java 45 | // Java 46 | void copyAll(Collection to, Collection from) { 47 | to.addAll(from); // !!! Would not compile with the naive declaration of addAll: 48 | // Collection is not a subtype of Collection 49 | } 50 | ``` 51 | 52 | 这就是为什么 `addAll()` 的签名是下面这样的: 53 | 54 | ```java 55 | //java 56 | interface Collection ... { 57 | void addAll(Colletion items); 58 | } 59 | ``` 60 | 61 | 这个通配符参数 `? extends E` 意味着这个方法接受一些 E 类型的子类而不仅仅是 E 类型本身。这就是说我们可以安全的读 `E's`(这里表示 E 子类元素的集合),但不能写,因为我们不知道 E 的子类究竟是什么样的,针对这样的限制,我们很想要这样的行为:`Collection` 是 `Collection`的子类。换句话讲,带 **extends** 限定(**上界**)的通配符类型使得类型是**协变的(covariant)**。 62 | 63 | 这个技巧其实很简单:如果你只能从集合中读数据,那么使用`String` 集合并从中读取 `Objects` 是安全的,如果你只能给 `存入` 集合 ,那么给 `Objects` 集合存入 `String` 也是可以的:在 Java 中`List` 是 `List`的超类。 64 | 65 | 后者称为**逆变性(contravariance)**,并且对于 `List ` 你只能调用接受 String 作为参数的方法 (例如,你可以调用 `add(String)` 或者 `set(int, String)`),当然 如果调用函数返回 `List` 中的 `T`,你得到的并非一个 `String` 而是一个 `Object`。 66 | 67 | Joshua Bloch 称只能**读取**的对象为**生产者**,只能**写入**的对象为**消费者**。他建议:“*为了灵活性最大化,在表示生产者或消费者的输入参数上使用通配符类型*”,并提出了以下助记符: 68 | 69 | *PECS 代表生产者-Extens,消费者-Super(Producer-Extends, Consumer-Super)。* 70 | 71 | *注意*:如果你使用一个生产者对象,如 `List`,在该对象上不允许调用 `add()` 或 `set()`。但这并不意味着 该对象是**不可变的**:例如,没有什么阻止你调用 `clear()`从列表中删除所有项目,因为 `clear()` 根本无需任何参数。通配符(或其他类型的型变)保证的唯一的事情是**类型安全**。不可变性完全是另一回事。 72 | 73 | 74 | 75 | ### 声明处变型 76 | 77 | 假如有个范型接口`Source`,没有任何接收 `T` 作为参数的方法,唯一的方法就是返回 `T`: 78 | 79 | ```Kotlin 80 | // Java 81 | interface Source { 82 | T nextT(); 83 | } 84 | ``` 85 | 86 | 存储一个`Source`的实例引用给一个类型为 `Source` 是十分安全的。但 Java并不知道,而且依然禁止这么做: 87 | 88 | ```Kotlin 89 | // Java 90 | void demo(Source strs) { 91 | Source objects = strs; // !!! Not allowed in Java 92 | // ... 93 | } 94 | ``` 95 | 96 | 为次,我们不得不声明对象类型为 `Source`,这样做并没有太大的意义,因为我们可以像以前一样调用所有方法,因此并没有通过复杂的类型添加什么值。但编译器不知道。 97 | 98 | 在 Kotlin 中,有种可以将这些东西解释给编译器的办法,叫做声明处变型:通过注解**类型参数** `T` 的来源,来确保它仅从 `Source` 成员中**返回**(生产),并从不被消费。 为此,我们提供 **out** 修饰符: 99 | 100 | ```Kotlin 101 | abstract class Source { 102 | abstract fun nextT(): T 103 | } 104 | 105 | fun demo(strs: Source) { 106 | val objects: Source = strs // This is OK, since T is an out-parameter 107 | // ... 108 | } 109 | ``` 110 | 111 | 一般原则是:当一个类 `C` 的类型参数 `T` 被声明为 **out** 时,它就只能出现在 `C` 的成员的**输出**-位置,结果是 `C` 可以安全地作为 `C`的超类。 112 | 113 | 更聪明的说法就是,当类 C 在类型参数 T 之下是协变的,或者 T 是一个协变类型。可以把 C 想象成 T 的生产者,而不是 T 的消费者。 114 | 115 | `out` 修饰符本来被称之为变型注解,但由于同处与类型参数声明处,我们称之为声明处变型。这与 Java 中的使用处变型相反。 116 | 117 | 另外除了 **out**,Kotlin 又补充了一个变型注释:**in**。它接受一个类型参数**逆变**:只可以被消费而不可以 被生产。非变型类的一个很好的例子是 `Comparable`: 118 | 119 | ```Kotlin 120 | abstract class Comparable { 121 | abstract fun compareTo(other: T): Int 122 | } 123 | 124 | fun demo(x: Comparable) { 125 | x.compareTo(1.0) // 1.0 has type Double, which is a subtype of Number 126 | // Thus, we can assign x to a variable of type Comparable 127 | val y: Comparable = x // OK! 128 | } 129 | ``` 130 | 131 | 我们相信 **in** 和 **out** 两词是自解释的(因为它们已经在 C# 中成功使用很长时间了), 因此上面提到的助记符不是真正需要的,并且可以将其改写为更高的目标: 132 | 133 | **[存在性(The Existential)](https://en.wikipedia.org/wiki/Existentialism) 转变:消费者 in, 生产者 out!** :-) 134 | 135 | ### 类型投影 136 | 137 | #### 使用处变型:类型投影 138 | 139 | 声明类型参数 T 为 *out* 很方便,而且可以避免在使用出子类型的麻烦,但有些类 **不能** 限制它只返回 `T` ,Array 就是一个例子: 140 | 141 | ```kotlin 142 | class Array(val size: Int) { 143 | fun get(index: Int): T { /* ... */ } 144 | fun set(index: Int, value: T) { /* ... */ } 145 | } 146 | ``` 147 | 148 | 这个类既不能是协变的也不能是逆变的,这会在一定程度上降低灵活性。考虑下面的函数: 149 | 150 | ```kotlin 151 | fun copy(from: Array, to: Array) { 152 | assert(from.size == to.size) 153 | for (i in from.indices) 154 | to[i] = from[i] 155 | } 156 | ``` 157 | 158 | 该函数作用是复制 array ,让我们来实际应用一下: 159 | 160 | ```kotlin 161 | val ints: Array = arrayOf(1, 2, 3) 162 | val any = Array(3) { "" } 163 | copy(ints, any) // Error: expects (Array, Array) 164 | ``` 165 | 166 | 这里我们又遇到了同样的问题 `Array` 中的`T` 是不可变型的,因此 `Array` 和 `Array` 互不为对方的子类,导致复制失败。为什么呢?应为复制可能会有不合适的操作,比如尝试写入,当我们尝试将 Int 写入 String 类型的 array 时候将会导致 `ClassCastException` 异常。 167 | 168 | 我们想做的就是确保 `copy()` 不会做类似的不合适的操作,为阻止向`from`写入,我们可以这样: 169 | 170 | ```kotlin 171 | fun copy(from: Array, to: Array) { 172 | // ... 173 | } 174 | ``` 175 | 176 | 这就是类型投影:这里的`from`不是一个简单的 array, 而是一个投影,我们只能调用那些返回类型参数 `T` 的方法,在这里意味着我们只能调用`get()`。这是我们处理调用处变型的方法,类似 Java 中`Array`,但更简单。 177 | 178 | 当然也可以用`in`做投影: 179 | 180 | ```kotin 181 | fun fill(dest: Array, value: String) { 182 | // ... 183 | } 184 | ``` 185 | 186 | `Array` 对应 Java 中的 `Array`,`fill()`函数可以接受任何`CharSequence` 类型或 `Object`类型的 array 。 187 | 188 | #### 星投影 189 | 190 | 有时你对类型参数一无所知,但任然想安全的使用它。保险的方法就是定一个该范型的投影,每个该范型的正确实例都将是该投影的子类。 191 | 192 | Kotlin 提供了一种星投影语法: 193 | 194 | - For `Foo`, where `T` is a covariant type parameter with the upper bound `TUpper`, `Foo<*>` is equivalent to `Foo`. It means that when the `T` is unknown you can safely *read* values of `TUpper` from `Foo<*>`. 195 | - For `Foo`, where `T` is a contravariant type parameter, `Foo<*>` is equivalent to `Foo`. It means there is nothing you can *write* to `Foo<*>` in a safe way when `T` is unknown. 196 | - For `Foo`, where `T` is an invariant type parameter with the upper bound `TUpper`, `Foo<*>` is equivalent to `Foo` for reading values and to `Foo` for writing values. 197 | 198 | If a generic type has several type parameters each of them can be projected independently. For example, if the type is declared as `interface Function` we can imagine the following star-projections: 199 | 200 | - `Function<*, String>` means `Function`; 201 | - `Function` means `Function`; 202 | - `Function<*, *>` means `Function`. 203 | 204 | *Note*: star-projections are very much like Java's raw types, but safe. (这部分暂未翻译) 205 | 206 | ### 范型函数 207 | 208 | 函数也可以像类一样有类型参数。类型参数在函数名之前: 209 | 210 | ```kotlin 211 | fun singletonList(item: T): List { 212 | // ... 213 | } 214 | 215 | fun T.basicToString() : String { // extension function 216 | // ... 217 | } 218 | ``` 219 | 220 | 调用范型函数需要在函数名后面制定类型参数: 221 | 222 | ```kotlin 223 | val l = singletonList(1) 224 | ``` 225 | 226 | ### 范型约束 227 | 228 | 指定类型参数代替的类型集合可以用通过范型约束进行限制。 229 | 230 | #### 上界(**upper bound**) 231 | 232 | 最常用的类型约束是上界,在 Java 中对应 `extends`关键字: 233 | 234 | ```kotlin 235 | fun > sort(list: List) { 236 | // ... 237 | } 238 | ``` 239 | 240 | 冒号后面指定的类型就是上界:只有 `Comparable`的子类型才可以取代 `T` 比如: 241 | 242 | ```kotlin 243 | sort(listOf(1, 2, 3)) // OK. Int is a subtype of Comparable 244 | sort(listOf(HashMap())) // Error: HashMap is not a subtype of Comparable> 245 | ``` 246 | 247 | 默认的上界是 `Any?`。在尖括号内只能指定一个上界。如果要指定多种上界,需要用 **where** 语句指定: 248 | 249 | ```kotlin 250 | fun cloneWhenGreater(list: List, threshold: T): List 251 | where T : Comparable, 252 | T : Cloneable { 253 | return list.filter { it > threshold }.map { it.clone() } 254 | } 255 | ``` 256 | 257 | -------------------------------------------------------------------------------- /FunctionsAndLambdas/Cocroutines.md: -------------------------------------------------------------------------------- 1 | ## 协程(Coroutines)有些 APIs 是需要长时间运行,并且需要调用者阻塞直到这些调用完成(比如网络 IO ,文件 IO ,CPU 或者 GPU 比较集中的工作)。协程提供了一种避免线程阻塞并且用一种更轻量级,更易操控到操作:协程暂停。 2 | 3 | 4 | 5 | 协程把异步编程放入库中来简化这类操作。程序逻辑在协程中顺序表述,而底层的库会将其转换为异步操作。库会将相关的用户代码打包成回调,订阅相关事件,调度其执行到不同的线程(甚至不同的机器),而代码依然想顺序执行那么简单。 6 | 7 | 很多其它语言中的异步模型都可以用 Kotlin 协程实现为库。比如 C# ECMAScipt 中的 [async/wait](https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md#composing-suspending-functions) ,Go 语言中的  [channels](https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md#channels) 和 [`select`](https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md#select-expression) ,以及 C# 和 Python 的 [generators/`yield`](http://kotlinlang.org/docs/reference/coroutines.html##generators-api-in-kotlincoroutines)  模型。下面的描述会详细解释提供这些结构的库。 8 | 9 | 10 | 11 | ### 阻塞和挂起 12 | 一般来说,协程是一种可以不阻塞线程但却可以被挂起的计算过程。线程阻塞总是昂贵的,尤其是在高负载的情形下,因为只有小部分的线程是实际运行的,因此阻塞它们会导致一些重要的任务被延迟。 13 | 14 | 而协程的挂起基本没有什么开销。没有上下文切换或者任何的操作系统的介入。最重要的是,挂起是可以背用户库控制的,库的作者可以决定在挂起时根据需要进行一些优化/日志记录/拦截等操作。 15 | 16 | 另一个不同就是协程不能被任意的操作挂起,而仅仅可以在被标记为 *挂起点* 的地方进行挂起。 17 | 18 | ### 挂起函数 19 | 当一个函数被 `suspend` 修饰时表示可以被挂起。 20 | 21 | ```kotlin 22 | suspend fun doSomething(foo: Foo): Bar{ 23 | ... 24 | } 25 | ``` 26 | 这样的函数被称为 *挂起函数*,因为调用它可能导致挂起协程(库可以在调用结果已经存在的情形下决定取消挂起)。挂起函数可以想正常函数那样接受参数返回结果,但只能在协程中调用或着被其他挂起函数调用。事实上启动一个协程至少需要一个挂起函数,而且常常时匿名的(比如lambda)。下面这个例子是一个简单的`async()` 函数(来自[`kotlinx.coroutines`](http://kotlinlang.org/docs/reference/coroutines.html#generators-api-in-kotlincoroutines)  库): 27 | 28 | ```kotlin 29 | fun async(block: suspend() -> T) 30 | ``` 31 | 32 | 这里的 `async()`只是一个普通的函数(不是挂起函数),但 `block` 参数是一个带有 `suspend` 修饰的函数类型,所以当传递一个 lambda 给`async()`时,这会是一个挂起 lambda ,这样我们就可以在这里调用一个挂起函数了。 33 | 34 | ```kotlin 35 | async{ 36 | doSomething(foo) 37 | } 38 | ``` 39 | 40 | 继续类比,`await()` 函数可以是一个挂起函数(因此在 `await(){}` 语句块内仍然可以调用),该函数会挂起协程直至指定操作完成并返回结果: 41 | 42 | ```kotlin 43 | async { 44 | ... 45 | val result = computation.await() 46 | ... 47 | } 48 | ``` 49 | 50 | 更多关于 `async/await` 原理的内容请看[这里](https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md#composing-suspending-functions) 51 | 52 | 注意 `await()`和 `doSomething()` 不能在像 main() 这样的普通函数中调用: 53 | 54 | ```kotlin 55 | fun main(args: Array) { 56 | doSomething() // 错误:挂起函数从非协程上下文调用 57 | } 58 | ``` 59 | 还有一点,挂起函数可以是虚函数,当覆写它们时,必须指定 suspend 修饰符: 60 | 61 | ```kotlin 62 | interface Base { 63 | suspend fun foo() 64 | } 65 | 66 | class Derived: Base { 67 | override suspend fun foo() { …… } 68 | } 69 | ``` 70 | 71 | ### `@RestrictsSuspension` 注解 72 | 73 | 扩展函数(以及lambda)可以被标记为`suspend`。这样方便了用户创建其他[DSLs](http://kotlinlang.org/docs/reference/type-safe-builders.html)以及扩展其它API。有些情况下,库的作者需要阻止用户添加新的挂起线程的方案。 74 | 75 | 这时就需要`@RestrictsSuspension`注解了。当一个接收者类或者接口`R`被标注时,所有可挂起扩展都需要代理`R`的成员或者其它扩展。由于扩展时不能互相无限代理(会导致程序终止),这就保障了所有挂起都是通过调用`R`的成员,这样库作者就能完全掌控挂起方式了。 76 | 77 | 不过这样的场景不常见,它需要所有的挂起都通过库的特殊方式实现。比如,用下面的 [`buildSequence()`](http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines.experimental/build-sequence.html) 函数实现生成器时,必须保证协程中所有的挂起都是通过调用`yield()`或者`yieldAll()`来实现。这就是为什么[`SequenceBuilder`](http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines.experimental/-sequence-builder/index.html) 被标注为 `@RestrictsSuspension`: 78 | 79 | ```kotlin 80 | @RestrictsSuspension 81 | public abstract class SequenceBuilder { 82 | ... 83 | } 84 | ``` 85 | 86 | 可以参看[Github](https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/src/kotlin/coroutines/experimental/SequenceBuilder.kt) 源码 87 | 88 | ### 协程内部机制 89 | 90 | 这里并不打算全盘解释协程内部的工作原理,而是给大家一个整体上的概念。 91 | 92 | 协程完全时通过编译技术(并不需要 VM 或者 OS 方面的支持)实现,挂起时借由代码转换实现。基本上所有的挂起函数(当然是有些优化措施,但这里我们不会深入说明)都被转换为状态机。在挂起前,下一个状态会存储在编译器生成的与本地变量关联的类中。到恢复协程时,本地变量会被恢复为挂起之前的状态。 93 | 94 | 挂起的协程可以存储以及作为一个对象进行传递,该协程会继续持有其状态和本地变量。这样的对象的类型时`Continuation`,代码转换的整体实现思路是基于经典的 [Continuation-passing style](https://en.wikipedia.org/wiki/Continuation-passing_style) 。所有挂起函数要有一个额外的参数类型`Continuation`。 95 | 96 | 更多的细节可以参看[设计文档](https://github.com/Kotlin/kotlin-coroutines/blob/master/kotlin-coroutines-informal.md) 。其它语言(比如C# ECMASript2016)中类似的 async/await 模型在这里都有描述,当然了其它语言的实现机制和 Kotlin 有所不同 97 | 98 | ### 协程的实验状态 99 | 100 | 协程的设计是[实验性](http://kotlinlang.org/docs/reference/compatibility.html#experimental-features)的,也就是说在后面的 releasees 版本中可能会有所变更。当在 Kotlin1.1 中编译协程时,默认会有警告:*The feature "coroutines" is experimental* 。可以通过 [opt-in flag](http://kotlinlang.org/docs/diagnostics/experimental-coroutines.html) 来移除警告。 101 | 102 | 由于处于实验状态,协程相关的标准库都在`kotlin.coroutines.experimental`包下。当设计确定时实验状态将会取消,最后的API将会移到 `kotlin.coroutines`,实验性的包将会保留(或许是作为一个单独的构建中)以保持兼容。 103 | 104 | **千万注意**: 建议库作者可以采用同样的转换:为基于协程的 API 采用 "experimental" 前缀作包名(比如`com.example.experimental`)。当最终 API 发布时,遵循下面的步骤: 105 | 106 | - 复制所有 API 到 `com.example`包下 107 | - 保留实验性大包做兼容。 108 | 109 | 这样可以减少用户的迁移问题。 110 | 111 | ### 标准 API 112 | 113 | 协程主要在三种层级中支持: 114 | 115 | - 语言层面的支持(比如支持函数挂起) 116 | - Kotlin 标准库中核心底层 API 117 | - 可以直接在代码中使用的高级 API 118 | 119 | #### 底层 API:`kotlin.coroutines` 120 | 121 | 底层 API 比较少,强烈建议不要使用,除非要创建高级库。这部分 API 主要在两个包中: 122 | 123 | - [`kotlin.coroutines.experimental`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines.experimental/index.html) 带有主要类型与下述原语 124 | - [`createCoroutine()`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines.experimental/create-coroutine.html) 125 | - [`startCoroutine()`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines.experimental/start-coroutine.html) 126 | - [`suspendCoroutine()`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines.experimental/suspend-coroutine.html) 127 | - [`kotlin.coroutines.experimental.intrinsics`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines.experimental.intrinsics/index.html) 带有更底层的内联函数如 [`suspendCoroutineOrReturn`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines.experimental.intrinsics/suspend-coroutine-or-return.html) 128 | 129 | 关于这些 API 用法的更多细节可以在[这里](https://github.com/Kotlin/kotlin-coroutines/blob/master/kotlin-coroutines-informal.md)找到。 130 | 131 | #### `kotlin.coroutines`中的生成器API: 132 | 133 | `kotlin.coroutines.experimental` 中唯一的“应用层面”的函数是: 134 | 135 | - [`buildSequence()`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines.experimental/build-sequence.html) 136 | - [`buildIterator()`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines.experimental/build-iterator.html) 137 | 138 | 这些和 `kotlin-stdlib` 打包在一起,因为和序列相关。事实上,这些函数(这里单独以 `buildSequence()` 作为事例)实现生成器提供了一种更加简单的构造延迟序列的方法: 139 | 140 | 141 | 142 | ```kotlin 143 | val fibonacciSeq = buildSequence { 144 | var a = 0 145 | var b = 1 146 | 147 | yield(1) 148 | 149 | while (true) { 150 | yield(a + b) 151 | 152 | val tmp = a + b 153 | a = b 154 | b = tmp 155 | } 156 | } 157 | ``` 158 | 这里通过调用 `yield()`函数生成新的斐波那契数,就可以生成一个无限的斐波那契数列。当遍历这样的数列时,每遍历一步就生成一个斐波那契数,这样就可以从中取出无限的斐波那契数。比如 `fibonacciSeq.take(8).toList()`会返回`[1, 1, 2, 3, 5, 8, 13, 21]`。协程让这一实现开销更低。 159 | 160 | 为了演示正真的延迟序列,在`buildSequence()`中打印一些调试信息: 161 | 162 | ```kotlin 163 | val lazySeq = buildSequence { 164 | print("START ") 165 | for (i in 1..5) { 166 | yield(i) 167 | print("STEP ") 168 | } 169 | print("END") 170 | } 171 | 172 | // Print the first three elements of the sequence 173 | lazySeq.take(3).forEach { print("$it ") } 174 | ``` 175 | 176 | 运行上面的代码运,如果我们输出前三个元素的数字与生成循环的 `STEP` 有交叉。这意味着计算确实是惰性的。要输出 `1`,我们只执行到第一个 `yield(i)`,并且过程中会输出 `START`。然后,输出 `2`,我们需要继续下一个 `yield(i)`,并会输出 `STEP`。`3` 也一样。永远不会输出再下一个 `STEP`(以及`END`),因为我们没有请求序列的后续元素。 177 | 178 | 使用 `yieldAll()` 函数可以一次性生成序列所有值: 179 | 180 | ```kotlin 181 | val lazySeq = buildSequence { 182 | yield(0) 183 | yieldAll(1..10) 184 | } 185 | 186 | lazySeq.forEach { print("$it ") } 187 | ``` 188 | 189 | `buildIterator()` 与 `buildSequence()`作用相似,只不过返回值时延迟迭代器。 190 | 191 | 通过给`SequenceBuilder`类写挂起扩展,可以给 `buildSequence()`添加自定义生成逻辑: 192 | 193 | ```kotlin 194 | suspend fun SequenceBuilder.yieldIfOdd(x: Int) { 195 | if (x % 2 != 0) yield(x) 196 | } 197 | 198 | val lazySeq = buildSequence { 199 | for (i in 1..10) yieldIfOdd(i) 200 | } 201 | ``` 202 | 203 | 204 | 205 | #### 其它高级API:`kotlinx.coroutines` 206 | 207 | Kotlin 标准库只提供与协程相关的核心 API 。主要有基于协程的库核心原语和接口可以使用。 208 | 209 | 大多数基于协程的应用程序级API都作为单独的库发布:[`kotlinx.coroutines`](https://github.com/Kotlin/kotlinx.coroutines)。这个库覆盖了 210 | 211 | - 平台无关的异步编程此模块`kotlinx-coroutines-core` 212 | - 包括类似 Go 语言的`select` 和其他便利原语 213 | - 这个库的综合指南[在这里](https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md)查看。 214 | - 基于 JDK 8 中的 `CompletableFuture` 的 API:`kotlinx-coroutines-jdk8` 215 | - 基于 JDK 7 及更高版本 API 的非阻塞 IO(NIO):`kotlinx-coroutines-nio` 216 | - 支持 Swing (`kotlinx-coroutines-swing`) 和 JavaFx (`kotlinx-coroutines-javafx`) 217 | - 支持 RxJava:`kotlinx-coroutines-rx` 218 | 219 | 这些库既提供了方便的 API ,也可以作为构建其它基于协程库的样板参考。 -------------------------------------------------------------------------------- /coroutines/ComposingSuspendingFunctions.md: -------------------------------------------------------------------------------- 1 | 2 | **目录** 3 | 4 | * [组合挂起函数](#组合挂起函数) 5 | * [默认顺序调用](#默认顺序调用) 6 | * [使用 async 并发](#使用 async 并发) 7 | * [惰性启动的 async](#惰性启动的-async) 8 | * [async 风格的函数](#async-风格的函数) 9 | * [使用 async 的结构化并发](#使用-async-的结构化并发) 10 | 11 | 12 | ## 组合挂起函数 13 | 14 | 本节介绍了挂起函数的组合方法。 15 | 16 | ### 默认顺序调用 17 | 18 | 假设我们在其他地方定义了两个挂起函数,它们可以像某种远程服务调用或计算一样有用。 我们只是假装它们很有用,但实际上每个只是为了这个例子的目的而延迟一秒: 19 | 20 | ```kotlin 21 | suspend fun doSomethingUsefulOne(): Int { 22 | delay(1000L) // pretend we are doing something useful here 23 | return 13 24 | } 25 | 26 | suspend fun doSomethingUsefulTwo(): Int { 27 | delay(1000L) // pretend we are doing something useful here, too 28 | return 29 29 | } 30 | ``` 31 | 32 | 如果按顺序调用它们,首先调用 doSomethingUsefulOne , 接下来调用 doSomethingUsefulTwo 并且计算它们结果的和? 实际上,如果我们要根据第一个函数的结果来决定是否我们需要调用第二个函数或者决定如何调用它时,我们就会这样做。 33 | 34 | 我们使用普通的顺序来进行调用,因为这些代码是运行在协程中的,只要像常规的代码一样顺序都是默认的。下面的示例展示了测量执行两个挂起函数所需要的总时间: 35 | 36 | ```kotlin 37 | import kotlinx.coroutines.* 38 | import kotlin.system.* 39 | 40 | fun main() = runBlocking { 41 | val time = measureTimeMillis { 42 | val one = doSomethingUsefulOne() 43 | val two = doSomethingUsefulTwo() 44 | println("The answer is ${one + two}") 45 | } 46 | println("Completed in $time ms") 47 | } 48 | 49 | suspend fun doSomethingUsefulOne(): Int { 50 | delay(1000L) // pretend we are doing something useful here 51 | return 13 52 | } 53 | 54 | suspend fun doSomethingUsefulTwo(): Int { 55 | delay(1000L) // pretend we are doing something useful here, too 56 | return 29 57 | } 58 | ``` 59 | 60 | 结果会像下面这样: 61 | 62 | > The answer is 42 63 | > Completed in 2017 ms 64 | 65 | ### 使用 async 并发 66 | 67 | 如果 doSomethingUsefulOne 与 doSomethingUsefulTwo 之间没有依赖,如何能更快的得到结果,让它们进行并发执行呢? [async](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html) 可以很好的完成这点。 68 | 69 | 概念上讲 async 与 launch 是一致的. 它启动了一个单独的协程,该协程是一个轻量级的线程并可以和其它所有的协程并发工作. 不同的是 launch 会返回一个 job 并且不带有任何结果值, 而 async 会返回一个 延期[Deffered](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/index.html) 一个轻量级的非阻塞的 future 表示一个会稍后提供结果的 promise. 你可以在延期值上使用 .await() 以获得结果,同时 Deffered 也是一个 job ,可以执行取消操作. 70 | 71 | ```kotlin 72 | import kotlinx.coroutines.* 73 | import kotlin.system.* 74 | 75 | fun main() = runBlocking { 76 | val time = measureTimeMillis { 77 | val one = async { doSomethingUsefulOne() } 78 | val two = async { doSomethingUsefulTwo() } 79 | println("The answer is ${one.await() + two.await()}") 80 | } 81 | println("Completed in $time ms") 82 | } 83 | 84 | suspend fun doSomethingUsefulOne(): Int { 85 | delay(1000L) // pretend we are doing something useful here 86 | return 13 87 | } 88 | 89 | suspend fun doSomethingUsefulTwo(): Int { 90 | delay(1000L) // pretend we are doing something useful here, too 91 | return 29 92 | } 93 | ``` 94 | 95 | 结果如下: 96 | 97 | ``` 98 | The answer is 42 99 | Completed in 1017 ms 100 | ``` 101 | 102 | 速度提升了两倍,这是因为我们并发执行了两个协程.记住协程的并发永远是显式的. 103 | 104 | ### 惰性启动的async 105 | 106 | 使用值为 CoroutineStart.LAZY 的可选启动参数进行异步时设置惰性选项。 它仅在出现 await 调用或者调用 start 函数时才启动协程。 运行以下示例: 107 | 108 | ```kotlin 109 | import kotlinx.coroutines.* 110 | import kotlin.system.* 111 | 112 | fun main() = runBlocking { 113 | val time = measureTimeMillis { 114 | val one = async(start = CoroutineStart.LAZY) { doSomethingUsefulOne() } 115 | val two = async(start = CoroutineStart.LAZY) { doSomethingUsefulTwo() } 116 | // some computation 117 | one.start() // start the first one 118 | two.start() // start the second one 119 | println("The answer is ${one.await() + two.await()}") 120 | } 121 | println("Completed in $time ms") 122 | } 123 | 124 | suspend fun doSomethingUsefulOne(): Int { 125 | delay(1000L) // pretend we are doing something useful here 126 | return 13 127 | } 128 | 129 | suspend fun doSomethingUsefulTwo(): Int { 130 | delay(1000L) // pretend we are doing something useful here, too 131 | return 29 132 | } 133 | ``` 134 | 135 | 运行结果如下: 136 | 137 | The answer is 42 138 | Completed in 1017 ms 139 | 140 | 所以,这里定义了两个协程,但是没有像前面的例子那样执行,但是程序员在完全通过调用start开始执行时会给出控制权。 我们首先启动一个,然后启动两个,然后等待各个协程完成。 141 | 142 | 注意,如果我们在println中调用了await并且在各个协程上省略了start,那么我们就会得到顺序行为,因为await启动协程执行并等待执行完成,这不是懒惰的预期用例。 在计算值涉及挂起函数的情况下,async(start = CoroutineStart.LAZY)的用例是标准惰性函数的替代。 143 | 144 | ### async风格的函数 145 | 146 | 我们可以使用具有显式 GlobalScope 引用的异步协程生成器来定义异步样式函数,这些函数可以异步调用doSomethingUsefulOne和doSomethingUsefulTwo。 我们为这些函数名添加“Async”后缀,以突出显示它们只启动异步计算的事实,并且需要使用生成的延迟值来获取结果。 147 | 148 | ```kotlin 149 | // The result type of somethingUsefulOneAsync is Deferred 150 | fun somethingUsefulOneAsync() = GlobalScope.async { 151 | doSomethingUsefulOne() 152 | } 153 | 154 | // The result type of somethingUsefulTwoAsync is Deferred 155 | fun somethingUsefulTwoAsync() = GlobalScope.async { 156 | doSomethingUsefulTwo() 157 | } 158 | ``` 159 | 160 | 请注意,这些xxxAsync函数不是挂起函数。 它们可以在任何地方使用。 但是,它们的使用总是意味着它们的动作与调用代码的异步(这里意味着并发)。 161 | 162 | 以下示例显示了它们在协程之外的用法: 163 | 164 | ```kotlin 165 | import kotlinx.coroutines.* 166 | import kotlin.system.* 167 | 168 | // note, that we don't have `runBlocking` to the right of `main` in this example 169 | fun main() { 170 | val time = measureTimeMillis { 171 | // we can initiate async actions outside of a coroutine 172 | val one = somethingUsefulOneAsync() 173 | val two = somethingUsefulTwoAsync() 174 | // but waiting for a result must involve either suspending or blocking. 175 | // here we use `runBlocking { ... }` to block the main thread while waiting for the result 176 | runBlocking { 177 | println("The answer is ${one.await() + two.await()}") 178 | } 179 | } 180 | println("Completed in $time ms") 181 | } 182 | 183 | fun somethingUsefulOneAsync() = GlobalScope.async { 184 | doSomethingUsefulOne() 185 | } 186 | 187 | fun somethingUsefulTwoAsync() = GlobalScope.async { 188 | doSomethingUsefulTwo() 189 | } 190 | 191 | suspend fun doSomethingUsefulOne(): Int { 192 | delay(1000L) // pretend we are doing something useful here 193 | return 13 194 | } 195 | 196 | suspend fun doSomethingUsefulTwo(): Int { 197 | delay(1000L) // pretend we are doing something useful here, too 198 | return 29 199 | } 200 | ``` 201 | 202 | > 这里提供了具有异步功能的编程风格,仅用于说明,因为在其他编程语言中很流行。 由于下面解释的原因,强烈建议不要将这种风格与Kotlin协程一起使用。 203 | 204 | 考虑一下如果 val one = somethingUsefulOneAsync() 这一行和 one.await() 表达式这里在代码中有逻辑错误, 并且程序抛出了异常以及程序在操作的过程中被中止,将会发生什么。 通常情况下,一个全局的异常处理者会捕获这个异常,将异常打印成日记并报告给开发者,但是反之该程序将会继续执行其它操作。但是这里我们的 somethingUsefulOneAsync 仍然在后台执行, 尽管如此,启动它的那次操作也会被终止。这个程序将不会进行结构化并发,如下一小节所示。 205 | 206 | ### 使用async的结构化并发 207 | 208 | 让我们使用使用 async 的并发这一小节的例子并且提取出一个函数并发的调用 doSomethingUsefulOne 与 doSomethingUsefulTwo 并且返回它们两个的结果之和。 由于 async 被定义为了 CoroutineScope 上的扩展,我们需要将它写在作用域内,并且这是 coroutineScope 函数所提供的: 209 | 210 | ```kotlin 211 | suspend fun concurrentSum(): Int = coroutineScope { 212 | val one = async { doSomethingUsefulOne() } 213 | val two = async { doSomethingUsefulTwo() } 214 | one.await() + two.await() 215 | } 216 | ``` 217 | 218 | 这种情况下,如果在 concurrentSum 函数内部发生了错误,并且它抛出了一个异常, 所有在作用域中启动的协程都将会被取消。 219 | 220 | ```kotlin 221 | import kotlinx.coroutines.* 222 | import kotlin.system.* 223 | 224 | fun main() = runBlocking { 225 | val time = measureTimeMillis { 226 | println("The answer is ${concurrentSum()}") 227 | } 228 | println("Completed in $time ms") 229 | } 230 | 231 | suspend fun concurrentSum(): Int = coroutineScope { 232 | val one = async { doSomethingUsefulOne() } 233 | val two = async { doSomethingUsefulTwo() } 234 | one.await() + two.await() 235 | } 236 | 237 | suspend fun doSomethingUsefulOne(): Int { 238 | delay(1000L) 239 | return 13 240 | } 241 | 242 | suspend fun doSomethingUsefulTwo(): Int { 243 | delay(1000L) 244 | return 29 245 | } 246 | ``` 247 | 248 | 从上面的 main 函数的输出可以看出,我们仍然可以同时执行这两个操作: 249 | 250 | ```kotlin 251 | The answer is 42 252 | Completed in 1017 ms 253 | ``` 254 | 255 | 取消始终通过协程的层次结构来进行传递: 256 | 257 | ```kotlin 258 | import kotlinx.coroutines.* 259 | 260 | fun main() = runBlocking { 261 | try { 262 | failedConcurrentSum() 263 | } catch(e: ArithmeticException) { 264 | println("Computation failed with ArithmeticException") 265 | } 266 | } 267 | 268 | suspend fun failedConcurrentSum(): Int = coroutineScope { 269 | val one = async { 270 | try { 271 | delay(Long.MAX_VALUE) // 模拟一个长时间的运算 272 | 42 273 | } finally { 274 | println("First child was cancelled") 275 | } 276 | } 277 | val two = async { 278 | println("Second child throws an exception") 279 | throw ArithmeticException() 280 | } 281 | one.await() + two.await() 282 | } 283 | ``` 284 | 285 | 注意,当第一个子协程失败的时候第一个 async 是如何等待父线程被取消的: 286 | 287 | ``` 288 | Second child throws an exception 289 | First child was cancelled 290 | Computation failed with ArithmeticException 291 | ``` --------------------------------------------------------------------------------