Kotlin 快速入门

前言

人生苦多,快来 Kotlin ,快速学习Kotlin!

什么是Kotlin?

Kotlin 是种静态类型编程语言 用于现代基本上平台利用
100%可是及Java™和Android™互操作,它是[JetBrains]开之冲JVM的言语
开发IDE :
Intellij /
AndroidStudio3.0
preview

参考: Kotlin 官网
/ Kotlin
语言中文站

Example

Github
KotlinDemo
Hexo Site

自文件与保险

Kotlin 源文件为 kt 结尾.
源文件所有内容(无论是类还是函数)都蕴含在宣称的包内.
NOTE: 源文件一般以管教声明起,
没有指明包,则该文件之始末属于无名字的默认包(属于root package)。

package demo 

NOTE: 若声明的包路径与公事路径不均等,亦足正常编译. 不了照面出如Java
一样的警示 Package directive doesn’t match file location

默认导入

Kotlin 如同Java 一样 默认源文件会导入以下包:

kotlin.*
kotlin.annotation.*
kotlin.collections.*
kotlin.comparisons.* (自 1.1 起)
kotlin.io.*
kotlin.ranges.*
kotlin.sequences.*
kotlin.text.*
//根据目标平台还会导入额外的包:
JVM:
java.lang.*
kotlin.jvm.*
JS:
kotlin.js.*

import

导入语法

import (used by preamble)
  : "import" SimpleName{"."} ("." "*" | "as" SimpleName)? SEMI?
  ;`

Java vs Kotlin
1.比方起名字冲突,Kotlin 可以采用 as
关键字于当地重命名冲突项来消歧义
2.Kotlin 的显要字 import
不仅仅限于导入类,还得导入顶层函数和性,在靶声明中宣示的函数和总体性,枚举常量等.

NOTE: 与 Java 不同,Kotlin 没有独立的 import static 语法;
所有这些声明还为此 import 关键字导入。

顶层声明

1.同 package 下的Kotlin
源文件,在顶层所声明的常量,变量和函数等非容许再次定义,否则报Conflicting
错误。

2.若声称用 private 可见性修饰符修饰时,属于即文件私出。

主导数据列

Numbers

Kotlin
一切都是对象。Kotlin提供以下代表数字、字符的放置类型(这充分相近Java)

Type Bit width 包装器类型
Double 64 Double
Float 32 Float
Long 64 Long
Int 32 Integer
Short 16 Short
Byte 8 Byte
Char 16 (Unicode character) Character

NOTE:仅 Char 不是Kotlin的数字。如下

val c:Char='c'
val i: Int = c.toInt()
println(c) // 'c'
println(i) // 99

字面常量

Kotlin 唯独不支持八进制

  • 十进制: 123
  • 二进制: 0b00001011
  • 十六进制: 0x0F

Kotlin 数值表示方法

  • 默认 Double:123.5123.5e10
  • Float 用 f 或者 F 标记: 123.5f
  • Long 用大写 L 标记: 123L

NOTE:支持数字字面值中的下划线(自 kotlin1.1 起)

val oneMillion = 1_000_000

Kotlin 装箱机制

Kotlin 内置类型在 Java 平台及是储存吗 JVM
的原生类型,但一个可空的援(如
Int?)或泛型情况下(如 Array<Int>,List<Int> ...)
会把数字与字符自动装箱成对应包装类, 请参考
Numbers。

val low = -127
val high = 127
val noInIntegerCache = 128
var boxedA: Int? = low
var anotherBoxedA: Int? = low
println(boxedA == anotherBoxedA) //true
println(boxedA === anotherBoxedA) //true
boxedA = high
anotherBoxedA = high
println(boxedA == anotherBoxedA) //true
println(boxedA === anotherBoxedA) //true
boxedA = noInIntegerCache
anotherBoxedA = noInIntegerCache
println(boxedA == anotherBoxedA) //true
println(boxedA === anotherBoxedA) //false

===== 请参考
品类相等性

val anIntegerA: Int? = 123 //对应 java.lang.Integer 一个装箱的 Int
val anIntegerB: Int? = 123 //对应 java.lang.Integer
println(anIntegerA === anIntegerB) //true
println(anIntegerA?.javaClass) //int
println((anIntegerA as Number).javaClass) //java.lang.Integer
val anIntegerArray: Array<Int> = arrayOf(1,2,3)
val anIntegerList: List<Int> = listOf(1,2,3)
println(anIntegerArray.toString())
println(anIntegerList.toString())
println((anIntegerList[0] as Number).javaClass) //

NOTE:一个可空的援(如
Int?)能无克装换成 Int ,答案是大势所趋之。强制转换或者 !!

 val anIntegerA: Int? = 123 
 val anNewIntA: Int = anIntegerA  //编译错误
 val anNewIntB: Int = anIntegerA!!  //或者 anIntegerA as Int
 val anNewIntC: Int = anIntegerA //Start cast to kotlin.Int

显式转换

每个数字型支持如下的换:

  • toByte(): Byte
  • toShort(): Short
  • toInt(): Int
  • toLong(): Long
  • toFloat(): Float
  • toDouble(): Double
  • toChar(): Char

一般来说示例 Java 中,包装类非克隐式转换, Kotlin 也是这么,
不同品种中是不能互相隐式转换的。

Byte a  = 1;
Integer b = a;//error

val b: Byte = 1 // OK, 字面值是静态检测的
val i: Int = b // 错误

运算

Kotlin支持数字运算的标准集,运算被定义为相应的类似成员(但编译器会将函数调用优化为相应的命)。
参见操作符重载。

对各项运算,没有特殊字符来代表,而独可用中缀方式调用命名函数,例如:

val x = (1 shl 2) and 0x000FF000

当即是共同体的位运算列表(只用于 IntLong):

  • shl(bits) – 有标志左移 (Java 的 <<)
  • shr(bits) – 有号右移 (Java 的 >>)
  • ushr(bits) – 无符号右移 (Java 的 >>>)
  • and(bits) – 位与
  • or(bits) – 位或
  • xor(bits) – 位异或
  • inv() – 位非

字符串

字符串用 String 类型表示。字符串是不可变的。
字符串的素——字符可以使用索引运算符访问: s[i]。 可以用 for
循环迭代字符串:

val s = "Hello, world!\n" //字符串字面值
//字符串
for (c in s) {
    print(c)
}
//原生字符串 使用三个引号(""")分界符括起来
val text = """
for (c in s) {
    print(c)
}
"""
println(text)
//字符串模板
val str = "$s.length is ${s.length}"
println(str)

NOTE:
模板表达式以美元入($)开头,若要对象属性时如花括号括起来,若使代表字面值
$ 字符z则:

val price = "${'$'}9.99"
println(price)

数组

数组在 Kotlin 中使用 Array 类来代表,它定义了 getset
函数(按照运算符重载约定就会扭转也 [])和 size
属性,以及有任何有效之成员函数:

class Array<T> private constructor() {
    val size: Int
    operator fun get(index: Int): T
    operator fun set(index: Int, value: T): Unit

    operator fun iterator(): Iterator<T>
    // ……
}

Library.ktarrayOf() arrayOfNulls
函数以及Array构造函数能创数数组:

val args: Array<Int> = arrayOf(1, 2, 3)
val arrayOfNulls = arrayOfNulls<Int>(10) //空数组
val initArray = Array(5, { i -> (i * i).toString() }) //构造函数init
println(arrayOfNulls.size)

NOTE: 与 Java 不同之凡,Kotlin 中数组是不型变的(invariant)。这表示
Kotlin 不为我们把 Array<String>赋值给
Array<Any>,以防范或的运转时失败(但是你可以用 Array<out Any>,
参见类投影)。

事先所说之在泛型情况下Kotlin 会把数字与字符自动装箱成相应包装类,
Arrays.kt 中生以下

ByteArray
CharArray
ShortArray
IntArray
LongArray
FloatArray
DoubleArray
BooleanArray

无装箱开销的特别的好像来表示原生类型数组, 和 Array
并无继续关系,但是它们来平等的章程属性集。它们为还发出对应的工厂方法。

 val x: IntArray = intArrayOf(1, 2, 3)
 x[0] = x[1] + x[2]

数组迭代经过 iterator() 函数返回 Iterator<T> 对象进行迭代:

 val iterator = args.iterator()
 while (iterator.hasNext()) {
     print("" + iterator.next())
 }
 println()
 //forEach
 args.iterator().forEach { print(it) }
 println()
 //for-
 for (it in initArray/*.iterator()*/) {
     print(it)
 }
 println()
 //下标索引
 args.forEachIndexed { index, i -> println("$index = $i") }

NOTE: forEach forEachIndexed 这些是Array 的扩大函数,
背后实现吗是 [for 循环 ](#For 循环)

区间

间隔表达式由具有操作符形式 ..rangeTodownTo 函数帮以 in
!in 形成。
区间是吧其它可于类型(Comparable<in T>)定义之,但于整型原生类型(Int
,Long,Char),Ranges.kt 中落实了常用之整型区间(IntRange
LongRangeCharRange),而在 Primitives.kt中的 Int ,Long,Char
类实现了rangeTo 函数。以下是采用间隔的局部演示

println((1.rangeTo(3)).contains(1)) //使用区间rangeTo函数
println(1 in (1..3)) //使用区间操作符

.. 创建一个区间, 实际是调用 rangeTo 函数回原生类型 *Range 对象,
in 则调用 contains函数。in *Range 还可以据此在迭代(for-循环)中。

for (index in 1..4) print(index)

NOTE:rangeTo 创建的间隔, 范围值是稍微至不可开交, downTo 反之。他们默认 step
分别吗1,-1

//    val intRange = 1..4 //step 1 default
    val intRange = 1..4 step 2 //step 2
    val is2 = 2 in intRange
    val is4 = 4 in intRange
    println("first = ${intRange.first},last = ${intRange.last},step = ${intRange.step}")
    println(is2)
    println(is4)
    println(is2 or is4)

//    for (index in 1..4) print(index)
    for (index in intRange) print(index)
    println()
    for (index in intRange.reversed()) print(index)
    println()

    for (index in 10..1) print(index) //Nothing
    println()
    val intProgression = 10 downTo 1 /*step 2*/ //step默认为1  倒序迭代
    println("first = ${intProgression.first},last = ${intProgression.last},step = ${intProgression.step}")
    for (index in intProgression) print(index)
    println()

    for (index in 1..4) print(index) // 输出“1234”

    val isIn = 3 in 1.rangeTo(100)
    println(isIn)

    for (i in 'a'..'z') print(i)

背后实现原理

距离实现了该库中的一个公共接口:ClosedRange<T>

ClosedRange<T> 在数学意义及象征一个闭区间,它是为可正如类型定义的。
它来半点个端点:startendInclusive 他们还富含在距离内。
其重要操作是 contains,通常以 in/!in 操作符形式利用。

整型数列(IntProgressionLongProgression
CharProgression)表示等差数列。 数排列由 first 元素、last
元素和非零的 step 定义。 第一只要素是
first,后续元素是前面一个要素加上 steplast
元素总会让迭代命中,除非该数列是拖欠的。

数列是 Iterable<N> 的子类型,其中 N 分别为 IntLong 或者
Char,所以它们可用于 for-循环以及像 mapfilter 等函数中。 对
Progression 迭代一定给 Java/JavaScript 的根据索引的 for-循环:

for (int i = first; i != last; i += step) {
  // ……
}

对于整型类型,.. 操作符创建一个还要落实 ClosedRange<T>
*Progression 的对象。 例如,IntRange 实现了 ClosedRange<Int>
并扩张自 IntProgression,因此为 IntProgression
定义的有着操作为只是用于 IntRangedownTo()step()
函数的结果总是一个 *Progression

数列由以其伴生对象吃定义的 fromClosedRange 函数构造:

IntProgression.fromClosedRange(start, end, step)

数列的 last 元素这样算:对于正之 step 找到不高于 end
值的太充分价值、或者对因的 step 找到不低于 end 值的无比小价,使得
(last - first) % increment == 0

有些实用函数

  • rangeTo()
  • downTo()
  • reversed()
  • step()

程序结构

变量

独家使用 var ,val 声明可变和不可变的变量.例子如下

val s = "Example" // A Immutable String
var v = "Example" // A Mutable String

扬言可更换变量语法

var <propertyName>[: <PropertyType>] [= <property_initializer>]

宣称不可变变量(仅赋值一涂鸦就念变量)语法

var <propertyName>[: <PropertyType>] = <property_initializer>

默认 Kotlin
变量类型是能经过赋值时智能推断该变量的色,且该var变量只能该型的的价值。显式确定变量类型,必须使收取该档的初始化。通过一个简单例子说明

val aImmutableIntVariable = 0x001 //aImmutableIntVariable 类型为 Int
var aMutableIntVariable: Int = "0x002" //语法error
var bMutableIntVariable: Int = 0x002
var cMutableVariable: Any //显式确定变量类型,必须要接收该类型的初始化。

aImmutableIntVariable = 1 //不能重新分配 Val cannot be reassigned
bMutableIntVariable = ""//一旦类型确定,只能接受该类型的值

NOTE: var 变量直接赋值为 null ,该变量则不吻合预期的种类
简单来说(Nothing),再次赋值时报错。

var aNullable = null
aNullable = 1;//Nothing

再详尽的项目介绍:项目安全和智能转换

常量 (编译期)

曾知值的习性可以以 const 修饰符标记否 编译期常量.必须满足以下需要

  1. 坐落顶层要是 object 的一个成员
  2. String 或原生类型 值初始化
  3. 尚未从定义 getter

const val CONST_VAL = 1
//const val CONST_VAL_GET get() = 1 //Error: Const 'val' should not have a getter
//const val CONST_VAL_TEST :Any = 1  //error
fun testConstInFunction() {
//    const val CONST_VAL = 1 //error
}
object Kotlin {
    const val CONST_VAL: String = "object 常量"
}

骨子里字段

Kotlin
中接近不可知生出字段。然而,当用从定义访问器时,有时有一个秘而不宣字段(backing
field)有时是必不可少的。为夫 Kotlin 提供一个自动幕后字段,它而由此运用
field 标识符访问。

var counter = 0 // 此初始器值直接写入到幕后字段
set(value) {
  if (value >= 0)
  field = value
}

field 标识符只能用当性能的走访器内。

若属性至少一个访问器使用默认实现,或者由定义访问器通过 field
引用幕后字段,将会见也该属性生成一个秘而不宣字段。

比如,下面的情景下, 就没有帐篷后字段:

val isEmpty: Boolean
get() = this.size == 0

假如您的需不称当下套“隐式的私下字段”方案,那么到底可以下
冷性能(backing property)

private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
    get() {
        if (_table == null) {
            _table = HashMap() // 类型参数已推断出
        }
        return _table ?: throw AssertionError("Set to null by another thread")
    }

自每地方看,这多亏和 Java 相同之措施。因为通过默认 getter 和 setter
访问私有属性会吃优化,所以无见面引入函数调用开销。

控制流:if、when、for、while

if 语句、if – else 表达式

在 Kotlin 中,没有Java中三状元运算符(条件 ? 然后 : 否则), 因为if –
else
是一个表达式,即她见面回到一个价值。

val num1 = 1
val num2 = 2
val max = if (num1 > num2) num1 else num2
println(max)
println(if (num1 < num2) "if - else 表达式" else num2)

if的分段可以是代码块,最后的表达式作为该块的价:

println(
        if (num1 < num2) {
            println("num1 < num2")
            "if - else 表达式"
        } else num2
)

When 表达式

在 Kotlin 中,when 取代了Java中 switch 。声明语法如下:

when[(表达式)]{
  [条件分支1,条件分支2(可多个 逗号分隔)] -> controlStructureBody [SEMI]
  [else 分支] -> controlStructureBody [SEMI]
}

SEMI 代表 ;或者 换行 , 在controlStructureBody
是代码块都发生变量声明时采取, 示例:

when {
} //最简单的形式

val randomNum = Random().nextInt(5)
when (randomNum) {
  0, 1 -> println("randomNum == 0 or randomNum == 1") //多个分支条件放在一起,用逗号分隔
  2 -> println("randomNum == 2")
  else -> println("otherwise")
}

//如果不提供参数,所有的分支条件都是简单的布尔表达式,而当一个分支的条件为真时则执行该分支
when {
  randomNum == 0 -> {
    var a = 2; println("is 0") //;
    var b = 3
    println("is 0") //换行
  }
}
//其他分支都不满足条件将会求值 else 分支
when {
  randomNum == 0 -> println("randomNum is 0")
  randomNum == 1 -> println("randomNum is 1")
  else -> println("otherwise")
}

NOTE: when 作为一个表达式使用,则要产生 else 分支,
除非所有的也许情况尚且早就覆盖了。

val an = when (1) {
    1 -> 1
    else -> "never arrive"
}
println(an)
when (randomNum == 3) {
    true -> println("is 3")
    false -> println(randomNum)
}

For 循环

for
循环可以对其它提供迭代器(iterator)的对象开展遍历。语法

for (item in collection) print(item)
for (index in collection.indices) print(collection[index])

示例

val array = arrayOf(1, 2, 3)
//for
for (index in array.indices) print(array[index]);println() //索引遍历一个数组或者一个 list
for (item in array) print(item);println()
//库函数 forEachIndexed
array.forEachIndexed { index, item ->  print("[$index] = $item \t")}
println()
//库函数 withIndex
for ((index, value) in array.withIndex()) {
    println("the element at $index is $value")
}

由此看来,for 可以循环遍历任何提供了迭代器的对象。即:

  • 发一个成员函数或者扩展函数 iterator(),它的归路 Iterator<T>
  • 有一个成员函数或者扩展函数 next()
  • 发一个分子函数或者扩展函数 hasNext() 返回 Boolean

立刻三独函数都得标记为 operator

While 循环

whiledo..while 照常动用。小白应该吗可以搞掂吧。。。

巡回中的Break和continue

以循环中 Kotlin 支持传统的 breakcontinue 操作符。

回来跟跳转

Kotlin 有三种结构化跳转表达式:

  • return。默认从最直白包围它的函数或者匿名函数返回。
  • break。终止最直白包围它的轮回。
  • continue。继续下一样不好最好直白包围它的巡回。

标签

于 Kotlin 中任何表达式都可以据此竹签(label)来号。
标签的格式为标识符后跟 @
符号,例如:abc@fooBar@犹是中之竹签(参见语法)。
要也一个表达式加标签,我们若在其眼前加标签即可。

Break 和 Continue 的竹签控制跳转, return 标签控制返回目标,示例:

out@ for (i in 1..3) {
    for (j in 1..3) {
        if (j == 2) break@out;print("[$i , $j] ")
    }
}
println()
out@ for (i in 1..3) {
    for (j in 1..3) {
        if (j == 2) continue@out;print("[$i , $j] ")
    }
}
println()
var nullInt: Int? = 1
  nullInt = null
var anLong = nullInt?.toLong() ?: return
fun performLabelReturn() {
    val array = arrayOf(1, 2, 3)
    array.forEach {
        if (it == 2) return  //
        println(it)
    }
    println("performLabelReturn can't reach")
}
performLabelReturn()
println()
fun performLabelLambdaLimitReturn() {
    val array = arrayOf(1, 2, 3)
    array.forEach {
        if (it == 2) return@forEach //
        println(it)
    }
    println("performLabelLambdaLimitReturn can reach")
}
performLabelLambdaLimitReturn()
println()
fun performLabelLimitReturn() {
    val array = arrayOf(1, 2, 3)
    array.forEach limit@ {
        if (it == 2) return@limit //
        println(it)
    }
    println("performLabelLimitReturn can reach")
}
performLabelLimitReturn()
println()
fun performLabelAnonymousLambdaLimitReturn() {
    val array = arrayOf(1, 2, 3)
    array.forEach(fun(value: Int) {
        if (value == 2) return  // local return to the caller of the anonymous fun, i.e. the forEach loop
        println(value)
    })
    println("performLabelAnonymousLambdaLimitReturn can reach")
}
performLabelAnonymousLambdaLimitReturn()
println()
fun a(): Int {
    return@a 12
}
println(a())

面向对象

类似以及对象

声明类(class)语法

[访问修饰符 默认public] [非访问修饰符 默认 final] class 类名 
    [访问修饰符 默认public] [主构造函数] [参数] [: 父类 默认为 Any]  [类体]

定义类,我们透过下的事例来证明:

class EmptyClass  
println(EmptyClass() is Any)

NOTE: [] 代表可以看看略. Kotliin 中修饰符 与Java
略不同,Java语言提供了无数修饰符,主要分为以下简单像样:

  • 看修饰符
  • 非访问修饰符

再度详细的 Java 修饰符 请参见
Java 修饰符 _
菜鸟教程

Kotliin 中从未显式声明修饰符 ,默认可见性是 public

访问控制修饰符

类、对象、接口、构造函数、方法、属性与它们的 setter 都得生出
可见性修饰符。 (getter 总是与性有着相同之可见性。) 在 Kotlin
中发出这四个可见性修饰符:privateprotectedinternal
public

修饰符 是否支持顶级声明 当前文件 同一模块
private Y Y N
protected N ~~~~ ~~~~
internal Y Y Y
public Y Y Y

NOTE:

  1. protected 不支持顶级声明,因为文件没有继续关系。

  2. internal 是编译在一齐的等同法 Kotlin 文件:
    >

    • 一个 IntelliJ IDEA 模块;
    • 一个 Maven 项目;
    • 一个 Gradle 源集;
    • 一致次 <kotlinc> Ant 任务执行所编译的相同模拟文件。
  3. 对此类似以及接口内部宣称的成员可见修饰符与Java 类似:
    >

    • private 仅该类和接口内部可见;
    • protectd 该类和接口内部可见且子类可见
    • internal 欠模块内 可见
    • public 都可见

匪访问控制修饰符

kotlin 定义类、对象、构造函数、方法、属性时默认加了 final 修饰符,
接口默认是 open 与的相反。能被连续、被挂。

NOTE:在 final 修饰 class 下 用 open 修饰该类的积极分子无效,在 final
缺省修饰符下 再就此 final 修饰显得 Redundant 冗余,但在 override
时可运final 关键字再度修饰

咱俩由此下面的例证来证实:

open class Father {
    private val name = "哔哔" //private can't open
    protected open val bloodType = "AB"
    internal val number = 1000
    open val age = 28

    protected class Nested {
        val body = {}
        private val cipher = null

        private fun print() {
            //can't access private
//            println(name)
//            println(bloodType)
//            println(number)
//            println(age)

            body

        }
    }

    open fun print() {
        println(name) //can't access private
        println(bloodType)
        println(number)
        println(age)

        Nested().body

//        Nested().cipher//Kotlin 中外部类不能访问内部类的 private 成员

    }

}

class Son : Father() {
    override final val bloodType: String = "O" //protected // final Redundant
//    override public val bloodType: String = "O" // 能覆盖

    override val age: Int = 10 // public

    override open fun print() { //Warning: 'open' has no effect in a final class
//        println(name) //can't access private
        println(bloodType)
        println(number)
        println(age)

        Nested().body
    }

}

open class BigSon : Father() {
    override final val bloodType: String = "AB"  //can use final
}

NOTE:局部变量、函数和类似非能够闹可见性修饰符。Kotlin
中外部类非可知访问中类的 private 成员(与Java不同)。

类成员

类似可以涵盖

  • 构造函数暨初始化块
  • 函数
  • 属性
  • 镶套类和中类
  • 对象声明

构造函数

一个像样可产生一个预示构造函数和一个还是多个糟糕构造函数。主构造函数是类头的同一片段:它跟在类名(和走访修饰符
[默认 public])后。主构造函数有注解或可见性修饰符,这个
constructor
关键字是必不可少的,并且这些修饰符在其前面。非抽象类没有声明任何(主还是软)构造函数,它会有一个变动的免带来参数的兆构造函数。构造函数的可见性是
public。

NOTE:若使修改主构造函数的可见性,需要丰富一个显式 constructor 关键字

class A private constructor() { …… }

Kotlin 十分便民,
可以在预告构造函数外声明属性(可变的(var)或独自念之(val))以及初始化属性默认值(糟糕构造函数凡匪允许的),
且为此类成员属性, 预告构造函数内不克包含除了声明属性外的代码。提供了
init 关键字当前缀的初始化块(initializer blocks)

浅构造函数

扬言在类体内为 constructor
关键字之函数。若该类有主构造函数,次构造函数都用因此 this
关键字直接或间接委托给主构造函数。

open class Person /*private*/ constructor(firstName: String) {
    class A //empty class 下面接着是次构造函数 ,Error: Expecting member declaration, 期待成员声明

    val money = 1000_000

    init {
        println("init block: firstName= $firstName")
        println("init block: money= $money")
    }

    //次构造函数
    constructor(firstName: String, age: Int) : this(firstName) {
        println("secondary constructor: firstName= $firstName")
        println("secondary constructor: age= $age")
        println("init block: money= $money")
    }

    constructor (firstName: String, age: Int, money: Int) : this(firstName, age) {
        println("secondary constructor: firstName= $firstName")
        println("secondary constructor: age= $age")
        println("init block: money= $money")
    }

}

注意:在 JVM 上,如果主构造函数的所有的参数都有默认值,编译器会生成 一个额外的无参构造函数,它将使用默认值。这使得 Kotlin 更易于使用像 Jackson 或者 JPA 这样的通过无参构造函数创建类的实例的库。

class Customer(val customerName: String = "")

创办类的实例

Kotlin 并不需要 new 关键字创建实例, 像普通函数一样调用构造函数即可。

继承

Java 的超类是 Object , 而 Kotlin 的是 Any。

如父类有主构造函数且带来参数,子类必须用主构造函数将参数初始化,如下:

class Student(firstName: String) : Person(firstName) {
}

只顾:参数初始化时,子父类必须一致。

父类没有主构造函数, 那么每个次构造函数必须下 super
关键字初始化其基类型。

open class Human {
    constructor(name: String) {

    }

    constructor(name: String, age: Int) {

    }
}

class Woman : Human {
    constructor(name: String) : super(name)
    constructor(name: String, age: Int) : super(name, age)
}

//允许通过主构造函数覆盖次构造函数
class Man(name: String) : Human(name)

覆盖(override)

final , open 是否可覆盖修饰符 和 override
标注覆盖类、对象、接口、构造函数、方法、属性。

蒙面规则

在 Kotlin
中,实现持续由下述规则规定:如果一个近乎从其的一直超类继承相同成员的差不多独实现,
它要覆盖这成员并提供其好之落实(也许用持续来的其中有)来扫除歧义。
为了表示用 从哪个超类型继承的实现,我们运用由尖括号丁超类型名限定的
super ,如 super<Base> :

open class Thread {
    open fun run() {
        println("Thread#run")
    }

    fun start() {
        println("Thread#start")
    }
}

interface Runnable {
    fun run() {
        println("Thread#run")
    } // 接口成员默认就是“open”的


}

class HandlerThread() : Runnable, Thread() {
    //编译器要求覆盖 run():
    override fun run() {
        super<Thread>.run() // 调用 Thread.run()
        super<Runnable>.run() // 调用 Runnable.run()
    }
}

抽象类

类似及里面的某些成员可以声明也 abstract 。 抽象成员以本类中只是因为非用实现。
需要小心的是,我们并不需要用 open
标注一个抽象类或者函数——因为及时眼看。

abstract class AbstractClass{ //open 多余的,因为抽象类终究是父类,所以更不能用final 修饰
    open fun doSomething() {
    }

    abstract fun fly() //子类必须 override
}

class AbstractClassImpl : AbstractClass() {
    override fun fly() {
    }

    override fun doSomething() {//override 开放成员
        super.doSomething()
    }
}

接口

故而要字 interface 来定义接口。Kotlin 的接口函数可以生落实,
属性必须是空泛的(默认抽象), 或者提供 get 访问器实现,
且不可知发生幕后字段(backing field)。

fun main(args: Array<String>) {
    val kotlinLanguage = KotlinLanguage()
    println(kotlinLanguage.language)
    println(kotlinLanguage.that)
    println(kotlinLanguage.that === kotlinLanguage)
    kotlinLanguage.onReady()
    kotlinLanguage.onUpgrade()

    MultipurposePrinter().print()
}

interface KotlinInterface {
    val language get() = "Kotlin"
    val that: KotlinInterface

    fun onUpgrade() {
        println("call#onUpgrade")
    }

    fun onReady() //

}

class KotlinLanguage : KotlinInterface {
    override val that: KotlinInterface
        get() = this

    override fun onReady() {
        println("call#onReady")
    }

}

interface Printer {
    fun print()
}

interface ColorPrinter : Printer {
    override fun print() {
        println("ColorPrinter#print")
    }

//    val printerType get() = "ColorPrinter"
}


interface BlackPrinter : Printer {
    override fun print() {
        println("BlackPrinter#print")
    }

    val printerType get() = "BlackPrinter"
}

class MultipurposePrinter : ColorPrinter, BlackPrinter {

    override fun print() {
        println("MultipurposePrinter#print")
        super<BlackPrinter>.print()
        super<ColorPrinter>.print()

        super.printerType
    }
}

镶套类和中类

接近可嵌套在其他类中

fun main(args: Array<String>) {
    println(KotlinNestedInnerClass.KotlinNestedClass().bra())
    println(KotlinNestedInnerClass().KotlinInnerClass().bra())
    println(KotlinNestedInnerClass().KotlinInnerClass().reference())
}

private class KotlinNestedInnerClass {
    private val bra: String = "C"

    class KotlinNestedClass {
        fun bra() = KotlinNestedInnerClass().bra
    }

    //内部类 标记为 inner 以便能够访问外部类的成员。内部类会带有一个对外部类的对象的引用
    inner class KotlinInnerClass {
        fun bra() = bra
        fun reference() = this@KotlinNestedInnerClass  //This 表达式
    }

    //匿名内部类 @see 对象声明(object)

}

万一目标是函数式 Java 接口(即具有单个抽象方法的 Java 接口)的实例,
你得用带来接口类型前缀的lambda表达式创建它:

val run  = Runnable {  }

对象(object)

当Java 中, 匿名内部类随处可见。然而 Kotlin 用 object
关键字提供了目标声明和对象表达式特性, 创建单例、匿名对象,
伴生对象(类中的目标声明) so easy。

val point = object /*: Any()*/ { //默认继承 Any
    var x: Int = 0 //必须进行初始化
    var y: Int = 0
    override fun toString(): String {
        return "point[$x,$y]"
    }
}
point.x = 100
point.y = 300
println(point)
val singleton = Singleton
val singleton1 = Singleton
println(singleton === singleton1)

//对象声明
object Singleton { //决不能声明局部作用域(函数中)
}

NOTE: 如何区分对象声明和对象表达式, 顾名思义, 有名字的是目标声明(object
Singleton), 没名字的是目标表达式(anonymous object)。

关于 object 使用细节,下面通过一个简练例子也大家演示:

class KotlinObject {

    private fun privateObject() = object { //返回: <anonymous object : Any>
        val name = "123"
    }

    fun publicObject() = object { // 返回Any 建议private
        val name = "ABC"
    }

    fun run() {
        println(privateObject().name)
        //println(publicObject().name) //错误:未能解析的引用“name”
        var visible = true
        call(object : CallBack {
            override fun call() {
                visible //对象表达式中的代码可以访问来自包含它的作用域的变量
                println("Anonymous#call@${this.hashCode()}")
            }

        })
        call (object : CallBack {
            override fun call() {
                visible //对象表达式中的代码可以访问来自包含它的作用域的变量
                println("Anonymous#call@${this.hashCode()}")
            }

        })
        call(OneCallBack)
        call(OneCallBack)

    }

    object OneCallBack : CallBack {
        //因为对象表达式不能绑定名字,这称为对象声明
        override fun call() {
            println("OneCallBack#call@${this.hashCode()}")
        }
    }

    fun call(call: CallBack) {
        call.call()
    }

    interface CallBack {
        fun call(): Unit
    }
}

fun main(args: Array<String>) {
    KotlinObject().run()
}

私家函数时,返回object类型是匿名对象类型, 否则是 Any。与Java
不同内部类为可是看非 final 变量。对象声明实则是单例。

伴生对象(companion object)

与 Java 或 C# 不同,在 Kotlin
中类没有静态方法。在多数场面下,它建议简单地运包级函数。

接近中的靶子声明可以用 companion 关键字记:

open class World {

    //Companion 是companion object 默认名字可省略,仅且有一个伴生对象
    companion object Companion : Observer {
        @JvmField //@JvmField 标注这样的属性使其成为与属性本身具有相同可见性的静态字段。
        val time = System.nanoTime()

        const val VERSION = "1.1.4.2" //kotlin 常量(const 标注的(在类中以及在顶层的)属性), 在 Java 中会成为静态字段:

        override fun update(o: Observable?, arg: Any?) {
        }


        //        @JvmStatic  //打开注释编译报错,存在相同的函数声明, 这充分地证明了伴生对象的成员看起来像其他语言的静态成员,在运行时他们仍然是真实对象的实例成员
        fun sayHello() {
            println("sayHello@${this.hashCode()} ")
        }
    }

    fun sayHello() {
        println("sayHello@${this.hashCode()} ")
    }

}

fun main(args: Array<String>) {
    World.sayHello()
    World.Companion.sayHello()
    World().sayHello()
}

Java 中调用

public class StaticTest {
    public static void main(String[] args) {
        System.out.println(World.Companion);
        System.out.println(World.VERSION);
        System.out.println(World.time);
    }
}

NOTE:伴生对象实际是目标的实例成员, JVM 平台,如果采取 @JvmStatic
注解,你可将伴生对象的成员生成为真正的静态方法和字段。更详细信息请参见Java
互操作性一节

靶表达式和对象声明里产生一个至关重要的语义差别:

  • 目标表达式是于采取他们之地方立即实行(及初始化)的
  • 靶声明是在率先次于让访到常延迟初始化的
  • 伴生对象的初始化是以相应的类似让加载(解析)时,与 Java
    静态初始化器的语义相配合

数据类

咱常创建有只是保留数据的接近。在这些近似中,一些正经函数往往是起数量机械推导而来之。在
Kotlin 中,这给做 数据类 并记为 data

fun main(args: Array<String>) {
    val user1 = KotlinDataClass.User("小明", 19)
    val user2 = KotlinDataClass.User("小明", 19)
    println(user1 == user2)
    println(user1)
    val copyXiaoMing = user1.copy(age = 20)
    println(copyXiaoMing)
    println(user1.component1())
    val bb = KotlinDataClass.User("bb")
    println(bb)

    //数据类和解构声明
    val (name, age) = KotlinDataClass.User("Lisa", 18)
    println("$name, $age years of age")

    //标准数据类
    val anPair: Pair<Char, Char> = Pair('A', 'B')
    println("first = ${anPair.first}, second = ${anPair.second}")
    val (a,b,c) = Triple('A','B','C')
    println("($a, $b, $c)")
}
private class KotlinDataClass {

    open class Person

    //数据类本身是 final,必须有主构造器,至少一个参数
    data class User(val name: String, val age: Int = 0) : Person() {

        //编译器会根据主构造函数的参数生成以下函数,根据需求 override

//    override fun equals(other: Any?): Boolean {
//        return super.equals(other)
//    }
//
//    override fun hashCode(): Int {
//        return super.hashCode()
//    }
//
//    override fun toString(): String {
//        return super.toString()
//    }

//    Error: Conflicting overloads:
//    fun component1(){
//
//    }
    }

}

编译器自动从主构造函数中扬言的拥有属性导出以下成员:

  • equals()/hashCode() 对,
  • toString() 格式是 "User(name=John, age=42)"
  • componentN()
    函数
    按声明顺序对诺受拥有属性,
  • copy() 函数, 复制一个靶只是改变一些性能。

为保险生成的代码的一致性与有义的行,数据类必须满足以下要求:

  • 主构造函数需要至少发生一个参数;
  • 预示构造函数的兼具参数需要标记为 valvar
  • 多少类非可知是架空、开放、密封或者其中的;
  • (在1.1事先)数据类只能兑现接口。

自 1.1
起,数据类可以扩大外类似(示例请参见密封类)。

每当 JVM
中,如果转的好像需要含有一个无参的构造函数,则有的属性必须凭定默认值。
(参见构造函数)。

密封类

潜在封类用来表示受限的好像继承结构:当一个价也寡集中之种、而休可知发任何其他品种时。在某种意义上,他们是枚举类的恢弘:枚举类型的价值集合也是受限的,但每个枚举常量只在一个实例,而黑封类的一个子类可以出可含蓄状态的大半只实例。

NOTE: sealed 不克修饰 interface ,abstract class(会报
warning,但是不见面油然而生编译错误)

fun main(args: Array<String>) {
    val kotlinSealedClass = ChildrenKotlinSealedClass()
    println(eval(kotlinSealedClass))
}

sealed class KotlinSealedClass

class ChildrenKotlinSealedClass : KotlinSealedClass()

class GirlKotlinSealedClass : KotlinSealedClass()

private fun eval(k: KotlinSealedClass): String = when (k) {
    is ChildrenKotlinSealedClass -> "ChildrenKotlinSealedClass"
    is GirlKotlinSealedClass -> "GirlKotlinSealedClass"
    //不再需要 else 分支 已经覆盖了所有的情况
}

枚举类

枚举类的无比中心的用法是贯彻项目安全之枚举, 每个枚举常量都是一个对象,
需用逗号分开。示例如下

fun main(args: Array<String>) {
    for (it in KotlinEnumClass.Direction.values()) {
        println(it)
    }
    //必须与声明枚举类型名称一致, 否则抛出 IllegalArgumentException 异常。
    val north = KotlinEnumClass.Direction.valueOf("NORTH")
    println(north === KotlinEnumClass.Direction.NORTH)

    //枚举常量都具有在枚举类声明中获取其名称和位置的属性
    val (name, ordinal) = KotlinEnumClass.Direction.EAST
    println("$name $ordinal")


    KotlinEnumClass().printAllValues<KotlinEnumClass.ProtocolState>()
    println()
    KotlinEnumClass().printValue<KotlinEnumClass.ProtocolState>("WAITING")
}


private class KotlinEnumClass {
    //类型安全的枚举
    enum class Direction {
        NORTH, SOUTH, WEST, EAST;
    }

    //枚举都是枚举类的实例,可以初始化
    enum class Color(val rgb: Int) {
        RED(0xFF0000),
        GREEN(0x00FF00),
        BLUE(0x0000FF)
    }

    //枚举常量也可以声明自己的匿名类
    enum class ProtocolState {
        WAITING {
            override fun signal() = TALKING
        },

        TALKING {
            override fun signal() = WAITING
        };

        abstract fun signal(): ProtocolState
    }

    //列出定义的枚举常量
    inline fun <reified T : Enum<T>> printAllValues() {
        print(enumValues<T>().joinToString { it.name })
    }

    //通过名称获取枚举常量
    inline fun <reified T : Enum<T>> printValue(name: String) {
        print(enumValueOf<T>(name))
    }

}

枚举常量还实现了
Comparable
接口, 其中自然顺序是它们以枚举类中定义之一一。

NOTE: val (name, ordinal) = KotlinEnumClass.Direction.EAST
之所以可编译通过,
因为自身对枚举类进行解构声明

//学而致用
operator fun <E : Enum<E>> Enum<E>.component1() = this.name
operator fun <E : Enum<E>> Enum<E>.component2() = this.ordinal

注解类

读书Java
的应有针对注解不生疏,不了解得先看Java的注解。

注解声明

[访问修饰符 默认public] [非访问修饰符 默认只能为 final 不能显式修饰] annotation class 类名 
    [访问修饰符 只能为public] [主构造函数 constructor 关键字可有可无] [val参数] 

internal annotation class KotlinFileName(val name:String)

兴的参数类型有:

  • 针对诺为 Java 原生类型的路(Int、 Long等)以及字符串
  • KClass、枚举
  • 任何注解
  • 点都列类型的数组

NOTE: 注解参数不可知产生可空类型,因为 JVM 不支持将 null
作为注解属性的值存储。如果注解用作其他一个注的参数,则该称不以 @
字符为前缀, 且新的注解类访问权限不能够比较中一个注的参数的拜会权限要格外

   internal annotation class FileScope 
        constructor(@ApplicationScope val file: KotlinFileName)

诠释的叠加属性可以经过用元注解标注注解类来指定:

  • @Target
    指定可以用该注解标注的元素的或是的种类(类、函数、属性、表达式等);
  • @Retention
    指定该注解是否存储在编译后底 class
    文件被,以及她以运作时能否通过反射可见 (默认还是 true);
  • @Repeatable
    允许以么元素上翻来覆去施用同一的欠注解;
  • @MustBeDocumented
    指定该注解是国有 API 的均等有,并且该包含在转移的 API
    文档中显得的接近还是措施的签中。

@Target(AnnotationTarget.CLASS,
        AnnotationTarget.FILE,
        AnnotationTarget.FUNCTION,
        AnnotationTarget.VALUE_PARAMETER,
        AnnotationTarget.EXPRESSION)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
annotation private class ApplicationScope

Lambda 表达式

诠释也得以用来 lambda 表达式。它们会被使用为生成 lambda 表达式体的
invoke() 方法上。

annotation class Anonymous
val run = @KotlinAnnotation.Anonymous { println("run") }

Use-site Targets (使用处 目标)

当对性或预示构造函数参数进行标注时,从相应的 Kotlin 元素生成的 Java
元素会生出多个,因此当变化的 Java 字节码中该注解有多单可能位置
。支持的行使处目标的共同体列表为:

  • file
  • property(具有此目标的注释对 Java 不可见)
  • field
  • get(属性 getter)
  • set(属性 setter)
  • receiver(扩展函数或性质的接收者参数)
  • param(构造函数参数)
  • setparam(属性 setter 参数)
  • delegate(为委托属性存储其委托实例的字段)

可使用同样之语法来诠释整个文件。要实行是操作,请以对象文件之笺注放在文件的顶层,在保险指令之前要以拥有导入之前,如果文件在默认包着:

@file:JvmName("KotlinAnnotationKt")

package demo

一经只要指定精确地指定相应如何充分成该注解,请以以下语法:

@处目标元素:[注解A 注解B ] ... //同一目标只有1个注解时方括号可以省略

简易示例如下:

class User(@field:FieldScope val name: String, @get:[ApplicationScope FunScope] val age: Int)

假定未点名使用处目标,则因在用的笺注的 @Target 注解来摘取对象 。

Java 注解

Java 注解与 Kotlin 100% 兼容:

kotlin

//声明注解
annotation class Targets(vararg val value: KClass<*>)
annotation class TargetArrays(val value: Array<KClass<*>>)

@JavaAnnotation.Describe("see")
class See
@JavaAnnotation.SinceJava(name = "jdk", version = 1_8_0)
class JDK
@JavaAnnotation.Targets(Any::class, String::class)
class Targets
@JavaAnnotation.Targets(*arrayOf(Any::class, String::class))
class Targets2
fun printId(intId: JavaAnnotation.IntId) {
    println(intId.value)
}
@JavaAnnotation.IntId(Int.MAX_VALUE)
class Res
printId(Res::class.annotations[0] as JavaAnnotation.IntId)

java

@KotlinAnnotation.ApplicationScope
public class JavaAnnotation {
    public static void main(String[] args) {
        try {
            Class clazz = Class.forName("jsource.JavaAnnotation");
            Annotation annotation = clazz.getAnnotation(KotlinAnnotation.ApplicationScope.class);
            System.out.println(annotation);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    @KotlinAnnotation.Targets({String.class, Integer.class})
    class TargetClass {
    }

    @KotlinAnnotation.TargetArrays({String.class, Integer.class})
    class TargetArrays {
    }

    public @interface Describe {
        String value();
    }

    public @interface SinceJava {
        String name();

        int version();
    }

    public @interface Targets {
        Class[] value();
    }

    @Retention(RetentionPolicy.RUNTIME)
    public @interface IntId {
        int value() default -1;
    }

}

泛型

同 Java 类似,Kotlin 中的泛型,如下示例:

fun main(args: Array<String>) {

    val emptyListString = List<String>()
    val listString = List("C", "D")
    assertEquals(0, emptyListString.size, "empty")
    printList(listString)
}

//泛型类
private class List<T>(vararg elements: T) : Iterable<T> {
    override fun iterator(): Iterator<T> {
        return elementsArray.iterator()
    }

    val elementsArray = mutableListOf(*elements)

    operator fun get(index: Int): T = elementsArray[index]

    val size: Int = elementsArray.size
}

// 泛型方法 printList
private fun <E> printList(inputList: List<E>) {
    for (element in inputList) {
        println("$element ")
    }
    println()
}

和 Java 不同,Kotlin
中的泛型没有通配符类型,它有:声明处型变(declaration-site
variance)与项目投影(type projections)。

型变

Java 中的泛型是不型变的,这代表 List<String>
不是List<Object> 的子类型。

List<String> strs = new ArrayList<String>();
List<Object> objs =(List) strs;
objs.add(1);
String s = strs.get(0); // !!! ClassCastException:无法将整数转换为字符串

PECS原则,在Java <? extends T>、<? super T>
通配符类型参数,前者只能读取,
不克写入,后者反之。便生同漫长规律,”Producer Extends, Consumer Super”:

  • Producer Extends – 如果你得一个但读List,用它们来produce
    T,那么以? extends T
  • Consumer Super – 如果您待一个单独写List,用其来consume
    T,那么以? super T
  • 要是要而读取以及写入,那么我们就算非可知下通配符了。

同一PECS原则适用于 Kotlin:

  • Producer Extends – 使得项目是协变的(covariant)
  • Consumer Super – 使得项目是逆变性(contravariance)

NOTE: PECS 代表生产者-Extens,消费者-Super(Producer-Extends,
Consumer-Super)。
一个劳动者对象,只是保证花色安全

宣称处型变

Java 中List<String> 不可知一直赋值List<Object> ,在 Kotlin 中,提供
out 修饰符确保接口或近似成员被返回out(生产),并没给 in
(消费)。

val stringList = listOf<String>()
val anyList: List<Any> = stringList

kotlin List 接口声明:

public interface List<out E> : Collection<E> 

in。它让一个门类参数逆变:只堪被消费要休得以让生产。逆变类的一个好好的例子是
Comparable

abstract class Comparable<in T> {
    abstract fun compareTo(other: T): Int
}

fun demo(x: Comparable<Number>) {
    x.compareTo(1.0) // 1.0 拥有类型 Double,它是 Number 的子类型
    // 因此,我们可以将 x 赋给类型为 Comparable <Double> 的变量
    val y: Comparable<Double> = x // OK!
}

色参数 T 被声称也 out 时,虽然 **<Base> 可以安全地作
**<Derived>的超类, 就只能出现输出-位置。

以它们在路参数声明处提供,所以给如做声明处型变。 这与 Java
应用处型变反倒,其种类用途通配符使得项目协变。in 反之。

**NOTE:消费者 in, 生产者 out **

项目投影 (使用处型变)

用项目参数 T 声明也 out
非常便于,并且会免用处子类型化的麻烦,但是有把类似实际上不能克为单纯回回
T 比如 Array:

val ints: Array<out Int> = arrayOf(1, 2, 3)
val any = Array<Any>(3) { "" }
//out 生产者 相当于Java ? extends T
fun copy(from: Array<out Any>, to: Array<Any>) {
    for (index in from.indices) {
        to[index] = from[index]
    }
}
copy(from = ints, to = any)
for (items in any) {
    println(items)
}
//out 消费者 相当于Java ? super T
fun fill(dest: Array<in Int>, value: Int) {
    for (index in dest.indices) {
        dest[index] = (dest[index] as? Int)!!.times(value)
    }
}
fill(any, 2)
for (items in any) {
    println(items)
}

上面out in 类型投影, 也不怕是Java 的下处型变 ? [extends][super] T

星投影

若类型参数一无所知,但照样要为安全之办法下其。
这里的安康法是概念泛型类型的这种投影,该泛型类型的每个具体实例化将凡拖欠投影的子类型。

Kotlin 为夫提供了所谓的星投影语法:

val star: List<*> = listOf("C", "D", 1, 2)
val any1: Any? = star[0]
fun compareTo2(x: Comparable<*>) 
  • 对于 Foo <out T>,其中 T 是一个怀有高达界 TUpper
    的协变类型参数,Foo <*> 等价于 Foo <out TUpper>。 这象征当 T
    未知时,你得安全地从 Foo <*> 读取 TUpper 的值。
  • 对于 Foo <in T>,其中 T 是一个逆变类型参数,Foo <*> 等价于
    Foo <in Nothing>。 这意味当 T
    未知时,没有什么得为安全的章程写入 Foo <*>
  • 对于 Foo <T>,其中 T 是一个怀有高达界 TUpper
    的不型变类型参数,Foo<*> 对于读取值时当于 Foo<out TUpper>
    而对于刻画值时等价于 Foo<in Nothing>

而泛型类型有多独品类参数,则每个类别参数还得独自投影。
例如,如果类型为声称也
interface Function <in T, out U>,我们可想像为下星投影:

  • Function<*, String> 表示 Function<in Nothing, String>
  • Function<Int, *> 表示 Function<Int, out Any?>
  • Function<*, *> 表示 Function<in Nothing, out Any?>

注意:星投影非常像 Java 的原始类型,但是安全。

泛型约束

克替换为定类型参数的所有或类型的会师好由泛型约束限制。

极端广泛的束缚类型是与 Java 的 extends 关键字对应之 上界

fun <T : Number> add(t: T) {
    // ……
}

add(1)
add("") //not allow

默认的上界(如果无声明)是 Any?。在尖括号丁不得不指定一个上界。
如果同型参数需要差不多个上界,我们需要一个单独的 where-子句:

fun <T> cloneWhenGreater(t: T)
        where T : Number,
              //              T : String, 只指定一个class ,接口可以多个
              T : kotlin.Comparable<T>,
              T : Cloneable {
}

扩展

Kotlin 同 C# 和 Gosu
类似,能够壮大一个类的初效能而不管需后续该类或应用诸如装饰者这样的其余类型的设计模式。
这通过叫做 扩展 的超常规声明完成。Kotlin 支持 扩张函数
恢宏属性

推而广之函数和性质

扬言一个恢宏函数和性能,我们需要为此一个 收信人类型
也尽管是被扩大的项目来作他的前缀。

class KotlinExtension {
    //成员函数比扩展函数优先
    fun member() {
        println("call#member")
    }

    fun fileName(): String {
        return "KotlinExtension.class"
    }

    companion object
}

//扩展的对象类型 KotlinExtension
fun KotlinExtension.extensionFun() {
    println("this@${this} call#extensionFun") //
}

fun KotlinExtension.member() {
    println("call#extension") //
}

//接收者类型表达式中使用泛型 要在函数名前声明泛型参数
fun <E> List<E>.addAll(){
    //...
}

//扩展属性(Extension Property)  实际扩展get* 函数而已
val KotlinExtension.fileName
    get() = "KotlinExtension.kt"

NOTE: this
关键字于扩展函数内部针对许交接收者对象(传过来的于点符号前的靶子)

可空接收者

可空的接收者类型为能够定义扩展,在对象变量上调用值为
null时,并且可于部数体内检测 this == null

检测出在扩大函数的内。最好的例证,如 Library.kt中:

public fun Any?.toString(): String

伴生对象的扩展

伴生对象的恢宏以及定义扩展函数和属性一致:

val KotlinExtension.Companion.anProperty: Int get() = 1
fun KotlinExtension.Companion.extensionFun() {
    println("call#Companion.extensionFun")
}

扩展的作用域

大部在顶层概念扩展,要使用所定义包之外的一个恢宏,导包就足以运用她。类里也得以声明扩展(我觉着当下并任卵用)在如此的扩展内部,该类的目标同接收者的对象成员,自由看。扩展声明所于的近乎的实例称为
分发接收者,扩展方法调用所在的接收者类型的实例称为 恢宏接收者

class KotlinInteriorExtension {
    fun start() {
        println("call#start")
    }

    fun KotlinExtension.stop(){
        start()
        member() //扩展声明为成员时 扩展函数优先
        this@KotlinInteriorExtension.member() //使用 限定this
    }

    fun member() {
        println("call#member")
    }
}

推而广之是静态解析的

谨记扩展不克真的的修改他们所扩展的接近,
仅仅是足以经过该档的变量用点表达式去调用这个新函数。

扩充函数是静态分发的,是由函数调用所于的表达式的色来控制。

//扩展是静态解析的
open class LocalBookmark

class CloudBookmark : LocalBookmark()

open class LocalBookmarkManage {

    open fun LocalBookmark.sync() {
        println("syncToCloud")
    }

    open fun CloudBookmark.sync() {
        println("syncFromCloud")
    }

    fun syncLocal(localBookmark: LocalBookmark) {
        localBookmark.sync()
    }
}

class CloudBookmarkManage : LocalBookmarkManage() {

    override fun LocalBookmark.sync() {
        println("syncFromLocal")
    }

    override fun CloudBookmark.sync() {
        println("syncToLocal")
    }

}

//run
LocalBookmarkManage().syncLocal(localBookmark) //输出 syncToCloud
CloudBookmarkManage().syncLocal(cloudBookmark) //输出 syncFromLocal —— 分发接收者虚拟解析

LocalBookmarkManage().syncLocal(cloudBookmark)//输出 syncToCloud —— 扩展接收者静态解析
CloudBookmarkManage().syncLocal(localBookmark)//输出 syncFromLocal —— 分发接收者虚拟解析

函数的散发对于分发接收者类型是虚拟的,但对于扩大接收者类型一定是静态的。

委托

kotlin 支持委托类和总体性, 使用主要字 by .

类委托

interface Printer {
    fun print()
}
class ColorPrinter : Printer {
    override fun print() {
        println("ColorPrinter#print")
    }
}
class BlackPrinter : Printer {
    override fun print() {
        println("BlackPrinter#print")
    }
}
class MultipurposePrinter(val printer: Printer) : Printer by printer {
    //可覆盖 , 不覆盖转发printer print 方法
    override fun print() {
        printer.print()
        println("override#print")
    }
}

fun main(args: Array<String>) {
    MultipurposePrinter(ColorPrinter()).print()
    MultipurposePrinter(BlackPrinter()).print()
}

by xxa -子句表示xxa 将会以 类中内部存储。 并且编译器将变转发让
xxa 的具有成员函数。

委托属性

kotlin 标准库实现如下常见的性能类型:

  • 推迟性(lazy properties): 其值只当首不行造访时算,
  • 唯独察性(observable properties):
    监听器会收到有关这个属性变更的打招呼,
  • 管多个属于性储存在一个射(map)中,而休是每个有单独的字段中。
缓性 Lazy

lazy()
是接受一个 lambda 并回一个 Lazy <T>
实例的函数,返回的实例可以看作落实延迟属性之托: 第一浅调整用 get()
会执行业已传递让 lazy() 的 lambda 表达式并记下结果, 后添调用 get()
只是回来记录的结果。

默认情况下,对于 lazy
属性的求值是和步锁的(synchronized):该值只在一个线程中计算,并且所有线程会看到同一的价值。如果初始化委托的旅锁不是不可或缺的,这样基本上独线程可以以推行,那么将
LazyThreadSafetyMode.PUBLICATION 作为参数传递给 lazy() 函数。
而若您规定初始化将连发生在么线程,那么您可动用
LazyThreadSafetyMode.NONE 模式,
它不会见出另线程安全之保险和相关的支出。

val lazyValue by lazy<String>(LazyThreadSafetyMode.SYNCHRONIZED) {
    println("computed!")
    "Hello" //同步锁的(synchronized)
}
println(lazyValue)
println(lazyValue)

夫例子输出:

computed!
Hello
Hello
不过察性 Observable

Delegates.observable()
接受两只参数:初始值和改时处理程序(handler)。
每当我们受属性赋值时见面调用该处理程序(在赋值行)。它有三只参数:被赋值的性、旧值和新值。

假若你想能够收获一个赋值并“否决”它,就用
vetoable()
取代 observable()。 在性为予以新价值生效之前会面调用传递让 vetoable
的处理程序。

var name by Delegates.observable("No Name") { prop, old, new ->
    println("被赋值的属性:${prop.name},  $old > $new")
}
name = "両儀式"
name = "式"
var skip by Delegates.vetoable("Null") { property, oldValue, newValue ->
    println("被赋值的属性:${property.name},  $oldValue > $newValue")
    false
}
skip = "Test"
println(skip)

夫例子输出:

被赋值的属性:name,  No Name > 両儀式
被赋值的属性:name,  両儀式 > 式
被赋值的属性:skip,  Null > Test
Null
将属性储存在投中

Map 可当委托来兑现委托属性。

val languageMap = mapOf("language" to "kotlin")
val language by languageMap //变量名就是map的key 否则找不到该key Exception: NoSuchElementException
println(language)

若要 var 属性只需要利用 MutableMap 。同样也适用于类

class User(map: Map<String, Any?>) {
    val name: String by map
    val age: Int     by map
    fun make() {
        println("make")
    }
    fun enable() = true
}

val user = User(mapOf(
        "name" to "John Doe",
        "age" to 25
))
println("${user.name} ${user.age}")//ok
有些委托属性

what? 看 lazy() 强大的初始化:

fun letMake(take: () -> User) {
    val lazyUser by lazy(take)
    //todo change true
    if (false && lazyUser.enable()) {
        lazyUser.make()
    }
}

//... 
letMake { ->
    println("init")
    User(mapOf("Twins" to 17))
}
起定义委托

var 属性需要贯彻 getValue() setValue() 函数,val
只是内需getValue() 即可。两套数还要为此 operator 关键字来进行标记。

委托类还好实现包含所需 operator 方法的 ReadOnlyProperty
ReadWriteProperty 接口之一。 这俩接口是于 Kotlin 标准库中声称的:

class Delegate {
  operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
    return "$thisRef, thank you for delegating '${property.name}' to me!"
  }

  operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
    println("$value has been assigned to '${property.name} in $thisRef.'")
  }
}

class ReadDelegate : ReadOnlyProperty<Any?, String> {
  override operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
    return "$thisRef, thank you for delegating '${property.name}' to me!"
  }
  //不需要 setValue
}


//test
var p: String  by Delegate()
p = "default"
p = "$p  \nchange"
val read by ReadDelegate()
println(read)
背后原理

以采用委托的经常, 不难发现该属性是寄托项目。比如: p is String
,输出false。

于每个委托属性之实现的暗中,Kotlin 编译器都见面变卦辅助属性并寄给她。
例如,对于属于性 prop,生成隐藏属性
prop$delegate,而访问器的代码只是简短地委托给此附加属性:

class C {
    var prop: Type by MyDelegate()
}

// 这段是由编译器生成的相应代码:
class C {
    private val prop$delegate = MyDelegate()
    var prop: Type
        get() = prop$delegate.getValue(this, this::prop)
        set(value: Type) = prop$delegate.setValue(this, this::prop, value)
}

Kotlin 编译器在参数中提供了有关 prop 的持有必要信息:第一独参数 this
引用到表面类 C 的实例而 this::propKProperty
类型的反射对象,该对象描述 prop 自身。

提供委托

kotlin 提供 provideDelegate
操作符,可以扩大创建属性实现所委托对象的逻辑。使用状况是当创立属性时(而不仅仅在那个
getter 或 setter 中)检查属性一致性。

class R {
  object id {
    val textView = 0x003
    val imageView = 0x004
  }

  object string {
    val hello_world = 0x001
  }

  object drawable {
    val icon_launch = 0x002
  }
}

open class View(val id: Int)
open class ImageView(id: Int) : View(id)
open class TextView(id: Int, var text: String = "") : View(id)


class MyActivity {

  val helloWorld by findResourceById<String>(R.string.hello_world)
  val textView by findResourceById<TextView>(R.id.textView)

  inline fun <reified T> findResourceById(id: Int): ResourceLoader<T> {
    return ResourceLoader<T>(id)
  }

  fun draw() {
    println(helloWorld)
    textView.text = "Hello"
    println(textView.text)
  }
}

class ResourceLoader<out T>(val id: Int) {
  operator fun provideDelegate(
    thisRef: MyActivity,
    prop: KProperty<*>
  ): ReadOnlyProperty<MyActivity, T> {
    return ResDelegate<T>(id)
  }

  private class ResDelegate<out V>(val id: Int) : ReadOnlyProperty<MyActivity, V> {
    val cacheKProperty = mutableMapOf<String, Any>()

    override fun getValue(thisRef: MyActivity, property: KProperty<*>): V {
      val last = cacheKProperty[property.name]
      if (last != null) {
        return last as V
      }
      val value = when (property.returnType.classifier) {
        String::class -> property.name as V
        View::class -> View(id) as V
        TextView::class -> TextView(id) as V
        ImageView::class -> ImageView(id) as V
        else -> throw NoSuchElementException()
      }
      cacheKProperty.put(property.name, value!!)
      return value
    }
  }
}

供委托, 并无复杂。通过一个函数去抱委托而已。provideDelegate
方法就影响辅助属性的缔造,并无见面影响也 getter 或 setter 生成的代码。

函数

函数用法

Kotlin 中之函数使用 fun 关键字声明

fun funName(参数)[: returnType(默认 Unit)] ...

函数参数规则

  • 函数参数使用 Pascal 表示拟定义,即 name: type , 参数用逗号隔开。
  • 每个参数必须来显式类型,
    参数还可生出默认值,当省略相应的参数时行使默认值, 以缩减重载数量。
  • 盖带有默认参数值的方式时,默认参数值省略。
  • 设若一个默认参数在一个无默认值的参数之前,那么该默认值只能通过使用取名参数调用该函数来使用
  • 万一最终一个 lambda
    表达式参数从括号外传给函数函数调用,那么允许默认参数不传值

fun invoke(method: String, invoke: Any = this) {
  println("call#method= $method $invoke")
}

fun invokeWithNameParameter(status: Int = 0, method: String, invoke: Any = this) {
  println("call#method= $method $invoke")
}

fun invokeWithLambda(status: Int = 0, method: String = "invokeWithLambda", invoke: Any = this, apply: () -> Unit) {
  println("call#method= $method $invoke")
}

abstract class Source {
  abstract fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size)
}

class FileSource : Source() {
  override fun read(b: Array<Byte>, off: Int, len: Int) {
    println("b.length = ${b.size} off = $off len = $len")
  }
}

//test
invoke("invoke")
invokeWithNameParameter(method = "invokeWithNameParameter")
invokeWithLambda(status = 1) { println("invokeWithLambda") }
invokeWithLambda { println("invokeWithLambda") }
FileSource().read(arrayOf('A'.toByte(), 'B'.toByte()))

可是换多少的参数(Varargs)

函数的参数(通常是最后一个)可以用 vararg 修饰符标记:

fun varargFun(method: String = "varargFun", vararg s: Int) {
  s.forEach { print(it) }
}

val b = intArrayOf(6, 8)
// vararg 参数 类型是基本类型,即是 *Array 类型 否则 Array<out T>
varargFun("1", 2, 4, *b, 10)

伸展(spread)操作符(在反复组前加 *),可以数组元素添加到vararg
变量中失去

返回 Unit 的函数

比方一个函数不回外有效之值,它的归来路是 UnitUnit
是均等种就来一个值——Unit 的门类。这个价未需要显式返回。Unit 就比如Java 的
Void

fun printHello(name: String?): Unit {
  if (name != null)
    println("Hello ${name}")
  else
    println("Hi there!")
  // `return Unit` 或者 `return` 是可选的
}

单表达式函数

当函数返回单个表达式时,可以简单花括号又于 =
符号之后指定代码体即可。当回值类型可由于编译器推断时,显式声明回路是可选的,
但具有块代码体的函数必须一直显式指定返回路。

fun double(x: Int) = x * 2

中缀表示法

Kotlin支持数字运算的标准集,正是因此了中缀表示拟,当函数满足以下规则虽会为此
infix 关键字标明

  • 他俩是成员函数或扩张函数
  • 她们仅发一个参数

infix fun String.append(s: String): String {
    return "$this$s"
}
infix fun call(method: String) {
    println("call#method= $method")
}

val s = "infix" append " gc"
println(s)
this call ("append")

函数作用域

以 Kotlin 中函数可以在文书顶层声明,这象征你免需要像有的言语如
Java、C# 或 Scala
那样创建一个类似来保存一个函数。此外除了顶层函数,Kotlin
中函数也得声明在有的作用域、作为成员函数和扩大函数。

  • 当类似还是对象中定义之函数——成员函数
  • 一个函数在外一个函数内部——局部函数

 //成员函数
fun memberFun() {
  val visited = ""
  fun partialFun() {    //局部函数
    println(visited)
  }
  partialFun()
}

泛型函数

函数可以生泛型参数,通过当部数称作前使用尖括声泪俱下指定。

fun <T> singletonList(item: T): List<T> {
  return listOf(item)
}

高阶函数

高阶函数是用函数用作参数或返回值的函数。

//函数用作参数 () -> Unit 不带参数并 且返回 Unit 类型值的函数
fun post(runnable: () -> Unit) {
  println("post before")
  runnable()
  println("post after")
}

fun postDelay(delay: Int, runnable: () -> Unit) {
  println("postDelay before")
  runnable()
  println("postDelay after")
}
fun test() {
  post(this::sayHi) //函数引用
  post { println("post") }
  postDelay(1000) { println("postDelay") }
}

() -> Unit
被称为函数类型
, ::
操作符可参见函数引用,
当一个函数接受任何一个函数作为最后一个参数,lambda
表达式参数可以于圆括号参数列表之外传递。 参见
callSuffix
的语法。

Lambda 表达式与匿名函数

一个 lambda 表达式或匿名函数是一个“函数字面值”,即一个休声明的函数,
作为表达式传递。

Lambda 表达式语法

lambda
表达式总是为大括号括着,完整语法形式的参数声明在括号内,并起可卜的档次标注,
函数体跟在一个 -> 符号之后。

println({}) //输出: () -> kotlin.Unit
println({ "String" })//输出: () -> kotlin.String
val string = { "String" }
println(string())//输出: String

挖槽,上面的凡什么不好。没了解Lambda
表达式
的,当然会纳闷不已。

fun explicitAnonymous(): () -> Int {
  return { -> 1 } //没参数不能有括号() -> 也可略
}

这样一来就简单明了。{} 声明了单匿名函数,编译器作以下处理

local final fun <anonymous>(): Unit

当一个缺损参数的匿名函数, 如 { "String" } ,编译器会将lambda
主体中之末梢一个要可能是单个)表达式会视为返回值。若是{ "String";1 }
则输出 () -> kotlin.Int

然而选取的色标注,单表达式函数时,显式声明回路是可选的,匿名的参数类型为是可选的。非单表达式函数时,则变量名可选。

val sum = { x: Int, y: Int -> x + y }  //val sum: (Int, Int) → Int
val sum2: (Int, Int) -> Int = { x, y -> x + y } //val sum2: (Int, Int) → Int
fun sum3(sum: (Int, Int) -> Int) {
  println(sum(0,0))
}
fun sum4(sum: (a: Int, b: Int) -> Int) {
  println(sum)
}
sum3 { a, b -> 1 + 3 }
println(sum(1, 2))

以 Kotlin 中Lambda表达式约定

  • 函数的结尾一个参数是一个函数,并且你传递一个 lambda
    表达式作为相应的参数,你可当圆括号之外传递
  • lambda 是该调用的绝无仅有参数,则调用中之圆括号可以了省略。
  • 部数字面值才发一个参数时, 那么她的扬言可以简简单单(连同
    ->),其名目是 it
  • 不以的变量可用下划线取代该名目
  • lambda
    隐式返回最后一个表达式的价值,可以为此克的回到语法显式返回

fun <T> filter(predicate: (T) -> Boolean) {
  TODO()
}
filter<Int>() { it > 0 } //() 可略
filter<Int> { it > 0 }
filter<Int> { _ -> false }
filter<Int> {
  val shouldFilter = it > 0
  return@filter shouldFilter
}
匿名函数

顾名思义,与常规函数一样不需指定函数誉为

val sumAnonymous = fun(x: Int, y: Int) = x + y //返回类型可以自动推断
println(sumAnonymous(1, 3))
val sumAnonymous2 = fun(x: Int, y: Int): Int {
  return x + y
}
filter<Int>(fun(item) = item > 0) //推断出的参数类型可以省略. 只能在括号内传递

匿名函数和lambda 是起分之,匿名函数参数只会以括号内传递。
允许以函数留于圆括号外的简写语法仅适用于 lambda
表达式。Lambda表达式与匿名函数之间的其它一个分别是不有返回的作为。一个请勿带标签的
return 语词总是以用 fun 关键字声明的函数中回到。这表示 lambda
表达式中之 return 将自包含它的函数返回,而匿名函数中之
return以自匿名函数自身返回。

闭包

Lambda
表达式或者匿名函数(以及部分函数和目标表达式)
可以看该 闭包 ,即于外部作用域中声明的变量。 与 Java
不同的是好改闭包被抓获的变量:

var aNumber = 0
run {
  aNumber += 1
}
val add = fun() {
  aNumber += 1
}
add()
println("aNumber: $aNumber")
拉动接收者的部数字面值

Kotlin 提供了采用指定的 收信人对象 调用函数字面值的作用。
在函数字面值的函数体中,可以调用该接收者对象上的方而不论需外附加的限定符。
这类似于扩充函数,它同意而当函数体内访问接收者对象的分子。
其用法的极其关键之演示有是列安全的
Groovy-风格构建器。

val sumR = fun Int.(other: Int): Int = this + other //val sumR: Int.(Int) → Int
println(1.sumR(2))

内联函数

倘理解用高阶函数常,每一个函数都是一个对象,且会捕获一个闭包。
所以带来有运行时的频率损失,即那些当函数体内会访问到的变量。
内存分配(对于函数对象和相近)和虚构调用会引入运行时刻支出。

kotlin 支持 inline
修饰有lambda参数的函数,以消除这类似的开销。(仅支持顶层、成员函数,即非支持企业函数)

inline fun <T> lockInline(lock: Lock, body: () -> T): T {
  lock.lock()
  try {
    return body()
  } finally {
    lock.unlock()
  }
}

内联原理其实是编译器拷贝代码副本(如:body () -> T),这说不定致变化的代码增加,但以循环中的“超多态(megamorphic)”
情况下,将于性质达到有着升级。

匪持有lambda参数的函数:

inline fun test() { //warn 内联函数最适用于具有lambda参数的函数

NOTE:内联函数不支持部分函数

夺内联

对持有多独lambda参数的内联函数来说,默认内联, 可用 noinline
修饰lambda参数,禁用内联。

inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) {
  // ……
}

noinline 仅在内联函数吃但是使用

inline fun foo(noinline notInlined: () -> Unit) {
  // …… 仅一个参数又用 noinline 修饰, inline 将无效
}

未有返回

lambda 表达式内部不允许无标签的return , 但传为的函数是内联的,该
return 也堪内联,所以其是容返回。称为匪有返回

fun <T> lock(body: () -> T): Unit {
}
inline fun <T> lockInline(body: () -> T): Unit {
}

lock {
    return  // 不允许不带标签的return. return@lock
}
lockInline{
  return
}

循环中常因此这种结构

fun hasZeros(ints: List<Int>): Boolean {
  ints.forEach {
    if (it == 0) return true // 从 hasZeros 返回
  }
  return false
}

有的内联函数可能调用传被它的匪是一直来源函数体、而是源于其他一个履行上下文的
lambda 表达式参数,例如来自局部对象要嵌套函数。在这种气象下,该 lambda
表达式中呢非容许非有控制流。为了标识这种情景,该 lambda
表达式参数需要为此 crossinline 修饰符标记:

inline fun post(crossinline body: () -> Unit) {
  Runnable { body() }
}

具体化的类别参数

外联函数能发具体化的品种参数(Reified type parameters),用 reified
修饰符来限制品种参数

于头里泛型函数学习着,是无可知发具体化参数,获取 class时带来诸多不便。

fun <T> findType(t: T) {
  //因为T 不是静态已知的 Kotlin 类的引用,所以不能 T::class
  println((t as Any)::class)
}

//内联函数支持具体化的类型参数,不需要反射,正常的操作符如 !is 和 as 能正常使用
inline fun <reified T : Number> findReifiedType(t: T) {
  println(T::class)
  println(Int.MIN_VALUE is T)
}

内联属性

inline
修饰符还足以修饰没有帐篷后字段的性质的访问器(有setter/getter),可独立标注。

val max inline get() = Int.MAX_VALUE
inline val max1 get() = Int.MAX_VALUE
inline val max2 inline get() = Int.MAX_VALUE //编译也ok 。。。

//Inline property cannot have backing field
var count = 0
var counter
inline get() = count //set/get 其中一个标注为inline, 都不能使用 backing field
inline set(value) {
  count = value
}
//
inline var doubleCounter
get() = count * 2 //set/get 其中一个标注为inline, 都不能使用 backing field
set(value) {
  count *= value
}

国有 API 内联函数之限

当一个内联函数是 publicprotected 而不是 privateinternal
声明的平等片经常,就会见觉得它是一个模块级的公有
API。可以于另模块中调用它,并且为堪于调用处内联这样的调用。

立刻带来了一些由于模块做这么改时造成的二进制兼容的风险——声明一个内联函数而调用它的模块于她修改后并没更编译。

以祛除这种由国有 API 变更引入的莫般配的高风险,公有 API
内联函数体内未允用非公有声明,即,不同意采取 privateinternal
声明和该构件。

一个 internal 声明可以由 @PublishedApi 标注,这会同意她在国有 API
内联函数惨遭采用。当一个 internal 内联函数标记有 @PublishedApi
时,也会见如公有函数一样检查其函数体。

//公有 API 内联函数限制使用private 与 internal 声明以及其部件 (顶层声明)
inline fun publishApi(body: () -> Unit) {
    privateFun()
    internalFun()
}

@PublishedApi //检查其函数体加以限制
internal inline fun internalApi(body: () -> Unit) {
    privateFun()
    internalFun()
}

private fun privateFun(): Unit {

}

internal fun internalFun(): Unit {

}

Kotlin与 Java 混合开发

Kotlin 中调用 Java

业已映射类型

当Kotlin 中使Java 代码,编译期间, Java 的原生类型映射到对应的 Kotlin
类型,运行时表示维持无变换。

Kotlin 类型 Java 类型
kotlin.Byte byte
kotlin.Short short
kotlin.Int int
kotlin.Long long
kotlin.Char char
kotlin.Float float
kotlin.Double double
kotlin.Boolean boolean

Java 的装箱原始类型映射到可空的 Kotlin 类型:

Kotlin 类型 Java 类型
kotlin.Byte? java.lang.Byte
kotlin.Short? java.lang. Short
kotlin.Int? java.lang.Integer
kotlin.Long? java.lang.Long
kotlin.Char? java.lang.Character
kotlin.Float? java.lang.Float
kotlin.Double? java.lang.Double
kotlin.Boolean? java.lang. Boolean

局部非原生的内置类型也会作映射:

Kotlin 类型 Java 类型
kotlin.Any! java.lang.Object
kotlin.Cloneable! java.lang.Cloneable
kotlin.Comparable! java.lang.Comparable
kotlin.Enum! java.lang.Enum
kotlin.Annotation! java.lang.Annotation
kotlin.Deprecated! java.lang.Deprecated
kotlin.CharSequence! java.lang.CharSequence
kotlin.String! java.lang.String
kotlin.Number! java.lang.Number
kotlin.Throwable! java.lang.Throwable

NOTE: String!
为平台型表示法

聚集类型在 Kotlin 中得以是独自念之或可变的,因此 Java 集合类型作如下映射:
(下表中的拥有 Kotlin 类型都驻留于 kotlin.collections包中):

Java 类型 Kotlin 只读类型 Kotlin 可变类型 加载的平台类型
Iterator<T> Iterator<T> MutableIterator<T> (Mutable)Iterator<T>!
Iterable<T> Iterable<T> MutableIterable<T> (Mutable)Iterable<T>!
Collection<T> Collection<T> MutableCollection<T> (Mutable)Collection<T>!
Set<T> Set<T> MutableSet<T> (Mutable)Set<T>!
List<T> List<T> MutableList<T> (Mutable)List<T>!
ListIterator<T> ListIterator<T> MutableListIterator<T> (Mutable)ListIterator<T>!
Map<K, V> Map<K, V> MutableMap<K, V> (Mutable)Map<K, V>!
Map.Entry<K, V> Map.Entry<K, V> MutableMap.MutableEntry<K,V> (Mutable)Map.(Mutable)Entry<K, V>!

伸手留心,用作类型参数的装箱原始类型映射到平台项目:
例如,List<java.lang.Integer> 在 Kotlin 中见面变成 List<Int!>

Java 的数组按如下所述映射:

Java 类型 Kotlin 类型
int[] kotlin.IntArray!
String[] kotlin.Array<(out) String>!

拖欠安全和平台类型

Java 中任何引用都可能是 null,而Kotlin 类型安全(空安全)。 Java
声明的种类在 Kotlin
中空检查跟Java相同(可空,非空)称为阳台型。平台类可用助记符加于后边来代表,但切记勿克于先后中这样描写,kotlin
并没有对应语法,IDE Doc 可以显得。

val nullAny = JavaDataType.nullObj //实际: val nullAny: Any!
val safeNullAny: Any? = JavaDataType.nullObj

println(safeNullAny?.hashCode())
println(nullAny?.hashCode()) //null check

val notNullAny: Any = JavaDataType.nullObj //赋值时 NPE
nullAny.hashCode() //使用时 NPE

NOTE:只要非是Java基本项目,在Kotlin中还见面炫耀为 T!

Getter 和 Setter

遵循 Java 约定的 getter 和 setter 的方法(名称以 get
开头的无参数方法及坐 set 开头的仅仅参数方法)在 Kotlin 中代表也性。
Boolean 访问器方法(其中 getter 的称谓以 is 开头而 setter 的名以
set 开头)会代表也跟 getter 方法有同样名称的特性。 例如:

import java.util.Calendar

fun calendarDemo() {
    val calendar = Calendar.getInstance()
    if (calendar.firstDayOfWeek == Calendar.SUNDAY) {  // 调用 getFirstDayOfWeek()
        calendar.firstDayOfWeek = Calendar.MONDAY      // 调用ll setFirstDayOfWeek()
    }
    if (!calendar.isLenient) {                         // 调用 isLenient()
        calendar.isLenient = true                      // 调用 setLenient()
    }
}

要留意,如果 Java 类只出一个 setter,它于 Kotlin
中未会见作性能可见,因为 Kotlin 目前匪支持不过写(set-only)属性。

返回 void 的方法

若是一个 Java 方法返回 void,那么从 Kotlin 调用时着回到 Unit
万一模一样有人以其返回值,它以由 Kotlin 编译器在调用处赋值,
因为该值本身是事先了解之(是 Unit)。

用 Kotlin 中是至关重要字之 Java 标识符进行转义

部分 Kotlin 关键字当 Java 中凡是行之有效标识符:inobjectis 等等。
如果一个 Java 库使用了 Kotlin
关键字当艺术,你仍然可以通过反引号(`)字符转义它来调整用该方法

foo.`is`(bar)

Java 泛型

Kotlin 的泛型与 Java
有接触不同(参见泛型)。当用
Java 类型导入 Kotlin 时,我们会履有换:

  • Java 的通配符转换成类型投影
    • Foo<? extends Bar> 转换成 Foo<out Bar!>!
    • Foo<? super Bar> 转换成 Foo<in Bar!>!
  • Java的固有类型转换成星投影
    • List 转换成 List<*>!,即 List<out Any?>!

暨 Java 一样,Kotlin
在运转时无保留泛型,即对象非带走传递到她们构造器中之那些类型参数的实际上类型。
ArrayList<Integer>()ArrayList<Character>() 是不能够分别的。
这令执行 is-检测不可能照顾到泛型。 Kotlin 只允许
is-检测星投影的泛型类型:

if (a is List<Int>) // 错误:无法检查它是否真的是一个 Int 列表
// but
if (a is List<*>) // OK:不保证列表的内容

Java 集合

java 集合类型映射的阳台项目且是可变的,用法而kotlin 一样,而且
操作符约定同样有效

Java 数组

跟 Java 不同,Kotlin 中之数组是休型变的。这象征 Kotlin
不同意我们拿一个 Array<String> 赋值给一个 Array<Any>
从而避免了也许的周转时故障。Kotlin
也禁止我们管一个子类的数组当做超类的数组传递给 Kotlin 的方式, 但是对于
Java 方法,这是同意的(通过 Array<(out) String>!
这种形式的阳台项目)。

Java 平台及,数组会使用原生数据类型以避免装箱/拆箱操作的付出。 由于
Kotlin 隐藏了这些实现细节,因此要一个变通方法来跟 Java 代码进行相互。
对于每种原生类型的数组都发生一个特化的类(IntArrayDoubleArray
CharArray 等等)来处理这种情况。 它们和 Array 类无关,并且会编译成
Java 原生类型数组为获得最佳性能。

要是发生一个收受 int 数组索引的 Java 方法:

public class JavaArrayExample {

    public void removeIndices(int[] indices) {
        // 在此编码……
    }
}

以 Kotlin 中公可这样传递一个原来生类型的数组:

val javaObj = JavaArrayExample()
val array = intArrayOf(0, 1, 2, 3)
javaObj.removeIndices(array)  // 将 int[] 传给方法

当编译为 JVM
字节代码时,编译器会优化对屡次组的拜访,这样就算未会见引入任何付出:

val array = arrayOf(1, 2, 3, 4)
array[x] = array[x] * 2 // 不会实际生成对 get() 和 set() 的调用
for (x in array) { // 不会创建迭代器
    print(x)
}

即使当我们应用索引定位时,也无见面引入任何付出

for (i in array.indices) {// 不会创建迭代器
    array[i] += 2
}

最后,in-检测也从不额外开销

if (i in array.indices) { // 同 (i >= 0 && i < array.size)
    print(array[i])
}

Java 可转移参数

Java 类有时声明一个具备可易多少参数(varargs)的方法来采取索引。

public class JavaArrayExample {

    public void removeIndicesVarArg(int... indices) {
        // 在此编码……
    }
}

以这种状况下,你需要运用进行运算符 * 来传递 IntArray

val javaObj = JavaArrayExample()
val array = intArrayOf(0, 1, 2, 3)
javaObj.removeIndicesVarArg(*array)

眼前无法传递 null 给一个声称也而转移参数的艺术。

操作符

由于 Java 无法标记用于运算符语法的点子,Kotlin
允许所有无可争辩名称及签名的其余 Java
方法作为运算符重载和另约定(invoke() 等)使用。
不同意行使着缀调用语法调用 Java 方法。

受检异常

于 Kotlin
中,所有特别都是免受检的,这表示编译器不见面迫使你捕获其中的另外一个。
因此,当您调用一个扬言受检异常的 Java 方法时,Kotlin
不会见迫使你开任何事情:

对象方法

当 Java 类型导入到 Kotlin 中不时,类型 java.lang.Object 的具备援都成了
Any。 而因为 Any 不是平台指定的,它就声明了
toString()hashCode()equals() 作为其成员, 所以为了能够为此到
java.lang.Object 的另外成员,Kotlin
要为此到扩充函数。

wait()/notify()

Effective
Java
第 69 久善意地建议事先利用并发工具(concurrency utilities)而休是
wait()notify()。 因此,类型 Any 的援不提供这半单法子。
如果你真正需要调用它们来说,你可用那变为 java.lang.Object

(foo as java.lang.Object).wait()
getClass()

万一得到对象的 Java
类,请于类引用上使用
java 扩展属性。

val fooClass = foo::class.java

面的代码应用了自 Kotlin 1.1
起支撑之绑定的切近援。你吧得使
javaClass 扩展属性。

val fooClass = foo.javaClass
clone()

要覆盖 clone(),需要继承 kotlin.Cloneable

class Example : Cloneable {
    override fun clone(): Any { …… }
}

毫不遗忘 Effective
Java
的第 11 条: 当心地改成写clone

finalize()

要覆盖 finalize(),所有你需要开的就是是简简单单地宣称其,而不待 override
关键字:

class C {
    protected fun finalize() {
        // 终止化逻辑
    }
}

根据 Java 的规则,finalize() 不能是 private 的。

做客静态成员

Java
类的静态成员会形成该类的“伴生对象”。我们鞭长莫及用这样的“伴生对象”作为价值来传递,
但可以显式访问该成员,例如:

val character = Character
if (Character.isLetter('A')) {
  // ……
}

Java 反射

Java 反射适用于 Kotlin 类,反之亦然。如上所述,你可使
instance::class.java,ClassName::class.java 或者 instance.javaClass
通过 java.lang.Class 来进入 Java 反射。

别支持的状态包括为一个 Kotlin 属性获取一个 Java 的 getter/setter
方法要私自字段、为一个 Java 字段得到一个 KProperty、为一个
KFunction 获取一个 Java 方法或者构造函数,反之亦然。

SAM 转换

即便比如 Java 8 一样,Kotlin 支持 SAM 转换。这表示 Kotlin
函数字面值可以被活动的变换成只发生一个非默认方法的 Java
接口的兑现,只要是措施的参数类型会与此 Kotlin
函数的参数类型相配合。

汝得这么创建 SAM 接口的实例:

val runnable = Runnable { println("This runs in a runnable") }

……以及以方式调用中:

val executor = ThreadPoolExecutor()
// Java 签名:void execute(Runnable command)
executor.execute { println("This runs in a thread pool") }

假使 Java 类有差不多单受函数式接口的主意,那么好经过应用以 lambda
表达式转换为一定的 SAM
类型的适配器函数来选择得调用的不二法门。这些适配器函数也会见依照需要由编译器生成。

executor.execute(Runnable { println("This runs in a thread pool") })

求留心,SAM
转换只适用于接口,而未适用于抽象类,即使这些抽象类为只来一个虚幻方法。

还要注意,此意义就适用于 Java 互操作;因为 Kotlin
具有确切的函数类型,所以无需用函数自动转换为 Kotlin
接口的兑现,因此不深受支持。

Java中调用 Kotlin

Java 可以轻松调用 Kotlin 代码。

属性

Kotlin 属性会编译成以下 Java 元素:

  • 一个 getter 方法,名称通过加前缀 get 算出;
  • 一个 setter 方法,名称通过加前缀 set 算出(只适用于 var 属性);
  • 一个私字段,与性能名称相同(仅适用于有幕后字段的性质)。

例如,var firstName: String 编译成以下 Java 声明:

private String firstName;

public String getFirstName() {
    return firstName;
}

public void setFirstName(String firstName) {
    this.firstName = firstName;
}

假若属性之称谓以 is 开头,则采用不同的名目映射规则:getter
的称号以及性能名称相同,并且 setter 的名是经过将 is 替换为 set
获得。 例如,对于属于性 isOpen,其 getter 会称做 isOpen(),而其 setter
会称做 setOpen()。 这无异于条条框框适用于外项目的特性,并不仅仅限于
Boolean

包级函数

org.foo.bar 包内的 example.kt
文件被声称的具有的函数和性,包括扩大函数, 都编译成一个称呼也
org.foo.bar.ExampleKt 的 Java 类的静态方法。

// example.kt
package demo

class Foo

fun bar() {
}

// Java
new demo.Foo();
demo.ExampleKt.bar();

足下 @JvmName 注解修改生成的 Java 类的类名:

@file:JvmName("DemoUtils")

package demo

class Foo

fun bar() {
}

// Java
new demo.Foo();
demo.DemoUtils.bar();

而多只文件中生成了平等的 Java 类名(包名相同而类名相同或者来平等的
@JvmName 注解)通常是谬误的。然而,编译器能够挺成一个十足的 Java
外观类,它具备指定的称谓还富含来自具备文件被享有该名的所有宣称。
要启用生成这样的外观,请以装有有关文件被采用 @JvmMultifileClass 注解。

// oldutils.kt
@file:JvmName("Utils")
@file:JvmMultifileClass

package demo

fun foo() {
}

// newutils.kt
@file:JvmName("Utils")
@file:JvmMultifileClass

package demo

fun bar() {
}

// Java
demo.Utils.foo();
demo.Utils.bar();

实例字段

假若要以 Java 中以 Kotlin 属性作为字段暴露,那即便需用 @JvmField
注解对那标注。
该字段将具有与底层属性相同之可见性。如果一个特性有幕后字段(backing
field)、非私有、没有 open /override 或者
const修饰符并且不是吃托付的特性,那么您得用 @JvmField 注解该属性。

class C(id: String) {
    @JvmField val ID = id
}

// Java
class JavaClient {
    public String getID(C c) {
        return c.ID;
    }
}

推迟初始化的属性(在Java中)也会暴露吗字段。
该字段的可见性与 lateinit 属性的 setter 相同。

静态字段

以命名对象或伴生对象被宣称的 Kotlin
属性会在该命名对象要包含伴生对象的近乎吃保有静态幕后字段。

习以为常这些字段是个人的,但得透过以下办法有暴露出来:

  • @JvmField 注解;
  • lateinit 修饰符;
  • const 修饰符。

使用 @JvmField
标注这样的特性使该变为同性能本身装有同等可见性的静态字段。

class Key(val value: Int) {
    companion object {
        @JvmField
        val COMPARATOR: Comparator<Key> = compareBy<Key> { it.value }
    }
}

// Java
Key.COMPARATOR.compare(key1, key2);
// Key 类中的 public static final 字段

在命名对象要伴生对象被的一个推迟初始化的性能具有跟特性
setter 相同可见性的静态幕后字段。

object Singleton {
    lateinit var provider: Provider
}

// Java
Singleton.provider = new Provider();
// 在 Singleton 类中的 public static 非-final 字段

const 标注的(在类似中及当顶层的)属性在 Java 中会成静态字段:

// 文件 example.kt

object Obj {
    const val CONST = 1
}

class C {
    companion object {
        const val VERSION = 9
    }
}

const val MAX = 239

在 Java 中:

int c = Obj.CONST;
int d = ExampleKt.MAX;
int v = C.VERSION;

静态方法

总的来说,Kotlin 将包级函数表示也静态方法。 Kotlin
还可以吗命名对象要伴生对象吃定义之函数生成静态方法,如果您将这些函数标注为
@JvmStatic 的话语。
如果你下该注解,编译器既会于对应对象的类中生成静态方法,也会见于靶自我中变化实例方法。
例如:

class C {
    companion object {
        @JvmStatic fun foo() {}
        fun bar() {}
    }
}

现在,foo() 在 Java 中凡静态的,而 bar() 不是:

C.foo(); // 没问题
C.bar(); // 错误:不是一个静态方法
C.Companion.foo(); // 保留实例方法
C.Companion.bar(); // 唯一的工作方式

对命名对象为如出一辙:

object Obj {
    @JvmStatic fun foo() {}
    fun bar() {}
}

在 Java 中:

Obj.foo(); // 没问题
Obj.bar(); // 错误
Obj.INSTANCE.bar(); // 没问题,通过单例实例调用
Obj.INSTANCE.foo(); // 也没问题

@JvmStatic 注解也得以用叫对象或伴生对象的性能, 使该 getter 和
setter 方法在该目标要含该伴生对象的类中是静态成员。

可见性

Kotlin 的可见性以下列方式映射到 Java:

  • private 成员编译成 private 成员;
  • private 的顶层声明编译成包级局部声明;
  • protected 保持 protected(注意 Java
    允许看与一个包中其他类的吃保障成员, 而 Kotlin 不克,所以 Java
    类会访问更广泛的代码);
  • internal 声明会成为 Java 中的 publicinternal
    类的成员会通过名字修饰,使其重新难在 Java 中竟然用及,并且根据
    Kotlin 规则而其兴重载相同签名的分子要互不可见;
  • public 保持 public

KClass

偶你待调用有 KClass 类型参数的 Kotlin 方法。 因为没有从 Class
KClass 的活动转换,所以若必经调用 Class<T>.kotlin
扩展属性之对等形式来手动进行转移:

kotlin.jvm.JvmClassMappingKt.getKotlinClass(MainView.class)

据此 @JvmName 解决签名冲突

偶尔我们纪念给一个 Kotlin 中的命名函数在许节码中发生另外一个 JVM 名称。
最暴的事例是由类型擦除引发的:

fun List<String>.filterValid(): List<String>
fun List<Int>.filterValid(): List<Int>

立刻片只函数不能够以定义,因为它的 JVM
签名是如出一辙的:filterValid(Ljava/util/List;)Ljava/util/List;
如果我们的确要它们以 Kotlin 中之所以同样名称,我们用因此 @JvmName
去标注其中的一个(或鲜单),并指定不同的名号作为参数:

fun List<String>.filterValid(): List<String>

@JvmName("filterValidInt")
fun List<Int>.filterValid(): List<Int>

在 Kotlin 中它可以就此同样的名称 filterValid 来访问,而当 Java
中,它们分别是 filterValidfilterValidInt

一致的技术呢适用于属性 x 和函数 getX() 共存:

val x: Int
    @JvmName("getX_prop")
    get() = 15

fun getX() = 10

转变重载

一般说来,如果您勾勒一个出默认参数值的 Kotlin 函数,在 Java
中唯有会发出一个享参数都设有的整体参数签名的法子可见,如果期望为 Java
调用者暴露多个重载,可以使用 @JvmOverloads 注解。

欠注解也适用于构造函数、静态方法等。它不克用来抽象方法,包括在接口中定义之办法。

class Foo @JvmOverloads constructor(x: Int, y: Double = 0.0) {
    @JvmOverloads fun f(a: String, b: Int = 0, c: String = "abc") {
        ……
    }
}

对此各一个有默认值的参数,都见面转一个分外的重载,这个重载会管这参数与它们右边的所有参数还易除掉。在上例中,会变以下代码

// 构造函数:
Foo(int x, double y)
Foo(int x)

// 方法
void f(String a, int b, String c) { }
void f(String a, int b) { }
void f(String a) { }

请注意,如不好构造函数面临所陈述,如果一个像样的有构造函数参数都产生默认值,那么会为那很成一个国有的无参构造函数。这就是没
@JvmOverloads 注解也中。

受检异常

总的来说,Kotlin 没有受检异常。 所以,通常 Kotlin 函数的 Java
签名不见面声明抛来特别。 于是如我们发一个如此的 Kotlin 函数:

// example.kt
package demo

fun foo() {
    throw IOException()
}

接下来我们怀念使以 Java 中调用它并捕捉这个大:

// Java
try {
  demo.Example.foo();
}
catch (IOException e) { // 错误:foo() 未在 throws 列表中声明 IOException
  // ……
}

因为 foo() 没有声明 IOException,我们由 Java
编译器得到了一个报错消息。 为了化解此问题,要以 Kotlin 中利用
@Throws 注解。

@Throws(IOException::class)
fun foo() {
    throw IOException()
}

拖欠安全性

当由 Java 中调用 Kotlin 函数时,没人拦住我们以 null 作为非空参数传递。
这虽是怎 Kotlin 给持有想非空参数的公有函数生成运行时检测。
这样咱们就会当 Java 代码里就得到 NullPointerException

型变的泛型

当 Kotlin
的近乎应用了声称处型变,有星星点点种选择好由
Java 代码中看到它的用法。让我们如果我们有因下类和片只以其的函数:

class Box<out T>(val value: T)

interface Base
class Derived : Base

fun boxDerived(value: Derived): Box<Derived> = Box(value)
fun unboxBase(box: Box<Base>): Base = box.value

一致种类似理所当然地将立刻俩函数转换成 Java 代码的艺术恐怕会见是:

Box<Derived> boxDerived(Derived value) { …… }
Base unboxBase(Box<Base> box) { …… }

题材是,在 Kotlin 中我们好这样勾画 unboxBase(boxDerived("s")),但是以
Java 中凡废的,因为当 Java 中类 Box 在那个泛型参数 T
上是不型变的,于是 Box<Derived> 并不是 Box<Base> 的子类。 要要其当
Java 中行事,我们仍以下这样定义 unboxBase

Base unboxBase(Box<? extends Base> box) { …… }  

这边我们下 Java
通配符类型? extends Base)来由此使用处型变来模拟声明处型变,因为于
Java 中只能这样。

当它作为参数出现时,为了让 Kotlin 的 API 在 Java
中行事,对于协变定义的 Box 我们生成 Box<Super> 作为
Box<? extends Super> (或者对逆变定义的 Foo 生成
Foo<? super Bar>)。当她是一个赶回值时, 我们无生成通配符,因为否则
Java 客户端将必须处理它们(并且她违反常用 Java
编码风格)。因此,我们的以身作则中的对应函数实际上翻译如下:

// 作为返回类型——没有通配符
Box<Derived> boxDerived(Derived value) { …… }

// 作为参数——有通配符
Base unboxBase(Box<? extends Base> box) { …… }

瞩目:当参数类型是 final 时,生成通配符通常没有意思,所以不管在啊地方
Box<String> 始终转换为 Box<String>

只要我们当默认不生成通配符的地方要通配符,我们得以动用 @JvmWildcard
注解:

fun boxDerived(value: Derived): Box<@JvmWildcard Derived> = Box(value)
// 将被转换成
// Box<? extends Derived> boxDerived(Derived value) { …… }

一方面,如果我们从来无欲默认的通配符转换,我们好应用@JvmSuppressWildcards

fun unboxBase(box: Box<@JvmSuppressWildcards Base>): Base = box.value
// 会翻译成
// Base unboxBase(Box<Base> box) { …… }

注意:@JvmSuppressWildcards
不仅只是用以单个项目参数,还可用来所有声明(如函数或接近),从而抑制其中的持有通配符。

Nothing 类型翻译

类型
Nothing
是不同寻常的,因为她于 Java 中没有自的照应。确实,每个 Java
引用类型,包括java.lang.Void 都可以接受 null 值,但是 Nothing
不行。因此,这种类型不克以 Java 世界面临规范表示。这便是怎以动用
Nothing 参数的地方 Kotlin 生成一个原始类型:

fun emptyList(): List<Nothing> = listOf()
// 会翻译成
// List emptyList() { …… }

在 Kotlin 中使用 JNI

只要声明一个每当地头(C 或 C++)代码中实现之函数,你得运用 external
修饰符来号它:

external fun foo(x: Int): Double

另的长河与 Java 中的工作办法完全相同。

Kotlin 高级编程

领域特定语言 DSL

地段特定语言(DSL)的主导思想是指向一定类型的题目之计算机语言,而非是面向任何项目的软件问题之通用语言。

项目安全之构建器

构建器(builder)的概念在
Groovy 社区中充分看好。
构建器允许以半声称(semi-declarative)的方定义数据。构建器很抱用来生成
XML、
布局 UI
组件、
描述 3D
场景与其它还多职能……

Kotlin 允许反省类的构建器,比 Groovy 自身的动态类型实现还有着吸引力。

HTML DSL kotlin 官方示例:

fun main(args: Array<String>) {
    val result =
            html {
                head {
                    title { +"XML encoding with Kotlin" }
                }
                body {
                    h1 { +"XML encoding with Kotlin" }
                    p { +"this format can be used as an alternative markup to XML" }

                    // an element with attributes and text content
                    a(href = "http://jetbrains.com/kotlin") { +"Kotlin" }

                    // mixed content
                    p {
                        +"This is some"
                        b { +"mixed" }
                        +"text. For more see the"
                        a(href = "http://jetbrains.com/kotlin") { +"Kotlin" }
                        +"project"
                    }
                    p { +"some text" }

                    // content generated from command-line arguments
                    p {
                        +"Command line arguments were:"
                        ul {
                            for (arg in args)
                                li { +arg }
                        }
                    }
                }
            }
    println(result)
}

interface Element {
    fun render(builder: StringBuilder, indent: String)
}

class TextElement(val text: String) : Element {
    override fun render(builder: StringBuilder, indent: String) {
        builder.append("$indent$text\n")
    }
}

@DslMarker
annotation class HtmlTagMarker

@HtmlTagMarker
abstract class Tag(val name: String) : Element {
    val children = arrayListOf<Element>()
    val attributes = hashMapOf<String, String>()

    protected fun <T : Element> initTag(tag: T, init: T.() -> Unit): T {
        tag.init()
        children.add(tag)
        return tag
    }

    override fun render(builder: StringBuilder, indent: String) {
        builder.append("$indent<$name${renderAttributes()}>\n")
        for (c in children) {
            c.render(builder, indent + "  ")
        }
        builder.append("$indent</$name>\n")
    }

    private fun renderAttributes(): String? {
        val builder = StringBuilder()
        for (a in attributes.keys) {
            builder.append(" $a=\"${attributes[a]}\"")
        }
        return builder.toString()
    }


    override fun toString(): String {
        val builder = StringBuilder()
        render(builder, "")
        return builder.toString()
    }
}

abstract class TagWithText(name: String) : Tag(name) {
    operator fun String.unaryPlus() {
        children.add(TextElement(this))
    }
}

class HTML() : TagWithText("html") {
    fun head(init: Head.() -> Unit) = initTag(Head(), init)

    fun body(init: Body.() -> Unit) = initTag(Body(), init)
}

class Head() : TagWithText("head") {
    fun title(init: Title.() -> Unit) = initTag(Title(), init)
}

class Title() : TagWithText("title")

abstract class BodyTag(name: String) : TagWithText(name) {
    fun b(init: B.() -> Unit) = initTag(B(), init)
    fun p(init: P.() -> Unit) = initTag(P(), init)
    fun h1(init: H1.() -> Unit) = initTag(H1(), init)
    fun ul(init: UL.() -> Unit) = initTag(UL(), init)
    fun a(href: String, init: A.() -> Unit) {
        val a = initTag(A(), init)
        a.href = href
    }
}

class Body() : BodyTag("body")
class UL() : BodyTag("ul") {
    fun li(init: LI.() -> Unit) = initTag(LI(), init)
}

class B() : BodyTag("b")
class LI() : BodyTag("li")
class P() : BodyTag("p")
class H1() : BodyTag("h1")

class A() : BodyTag("a") {
    public var href: String
        get() = attributes["href"]!!
        set(value) {
            attributes["href"] = value
        }
}

fun html(init: HTML.() -> Unit): HTML {
    val html = HTML()
    html.init()
    return html
}

点实现 HTML 标签,实际上是调用一个
lambda函数,用一个标签接收者的函数类型zuo作为参数,使在函数内部调整用该实例的成员。

差一点单厉害的 DSL 项目

  • Anko
    用于 Android 的,用于描述 UI 。
  • Gensokyo
    用于 Swing 的,用于描述 UI
  • KotlinTest
    Kotlin测试框架基于完美的Scalatest

协程 Coroutine

于 Kotlin 1.1 中协程是试错性的。另外kotlin
为了削减程序体积,根据需要运用协程,你要进入kotlinx-coroutines-core 库.

一部分 API 启动长时运作的操作(例如网络 IO、文件 IO、CPU 或 GPU
密集型任务相当),并求调用者阻塞直到它就。协程提供了平栽避免阻塞线程并因此重新廉价、更可控的操作替代线程阻塞的章程:协程挂起

协程通过以复杂放入库来简化异步编程。程序的逻辑可以当协程中顺序地表达,而脚库会为我们解决其异步性。该库可以以用户代码的连锁部分包装也回调、订阅相关事件、在不同线程(甚至不同机器!)上调度执行,而代码则维持如同顺序执行同样简单。

无数以外语言中可用的异步机制可以采用 Kotlin 协程实现呢仓库。这包括自
C# 和 ECMAScript 的
async/await、源于
Go 的
管道

select
以及源于 C# 和 Python
生成器/yield。关于提供这些组织的库请参见其下文描述。

阻塞 vs 挂起

大抵,协程计算好被挂起而无需卡住线程。线程阻塞的代价通常是贵的,尤其以青出于蓝负荷时,因为光发相对少量线程实际可用,因此阻塞其中一个见面促成有主要的职责为推迟。

一边,协程挂于几是无代价的。不需达成下文切换或者 OS
的另其它干涉。最着重之凡,挂于可以特别要命程度达是因为用户库控制:作为仓库的撰稿人,我们好决定挂于时有什么并依据需求优化/记日志/截获。

其余一个分是,协程不克于随机的吩咐中吊起于,而不得不于所谓的挂起点昂立于,这会调用特别标志的函数。

悬挂于函数

当我们调用标记有异常修饰符 suspend 的函数时,会来挂于:

suspend fun doSomething(foo: Foo): Bar {
    ……
}

然的函数称为悬挂于函数,因为调用它们可能挂于协程(如果相关调用的结果既可用,库可以操纵继续开展而休悬挂于)。挂于函数能够为同日常函数相同之方法赢得参数和归值,但其只能于协程和另挂于函数中调用。事实上,要启动协程,必须至少有一个悬挂于函数,它一般是匿名的(即她是一个吊于
lambda 表达式)。让我们来拘禁一个例证,一个简化的 async() 函数(源自
kotlinx.coroutines
库):

fun <T> async(block: suspend () -> T)

这里的 async() 是一个通常函数(不是挂于函数),但是其的 block
参数有一个带 suspend 修饰符的函数类型:
suspend () -> T。所以,当我们将一个 lambda 表达式传为 async()
时,它会是挂起 lambda 表达式,于是我们可以从中调用挂于函数:

async {
    doSomething(foo)
    ……
}

连续该类比,await() 可以是一个悬于函数(因此为可于一个 async {}
块中调用),该函数挂于一个协程,直到片乘除好并赶回其结果:

async {
    ……
    val result = computation.await()
    ……
}

更多关于 async/await 函数实际在 kotlinx.coroutines
中哪行事之信息可以当这里找到。

伸手留心,挂于函数 await()doSomething() 不可知以像 main()
这样的常见函数中调用:

fun main(args: Array<String>) {
    doSomething() // 错误:挂起函数从非协程上下文调用
}

还要注意的是,挂于函数可以是虚构的,当覆盖其常,必须指定 suspend
修饰符:

interface Base {
    suspend fun foo()
}

class Derived: Base {
    override suspend fun foo() { …… }
}

@RestrictsSuspension 注解

扩展函数(和 lambda 表达式)也得记为
suspend,就比如普通的同一。这允许创建
DSL
及其他用户可扩大的
API。在某些情况下,库作者需要阻止用户增长新方式来吊于协程。

以兑现就一点,可以采用
@RestrictsSuspension
注解。当接收者类/接口 R 用它标注时,所有悬挂于扩展都待委托为 R
的分子要么外委托给其的恢弘。由于扩展不可知顶相互委托(程序不见面告一段落),这包有悬挂于还经调用
R 的分子有,库的作者就足以完全控制了。

这在少数情景是得之,当每次挂于在仓库中盖超常规措施处理常。例如,当通过
buildSequence()
函数实现下文所陈述之生成器时,我们用保证以协程中之其余挂于调用最终调用
yield()yieldAll() 而非是另外其它函数。这便是干吗
SequenceBuilder
@RestrictsSuspension 注解:

@RestrictsSuspension
public abstract class SequenceBuilder<in T> {
    ……
}

参见其 Github

的源代码。

协程的内机制

咱俩无是当这里叫有一个关于协程如何工作之完全解释,然而粗略地认识产生了啊是一对一重要之。

协程完全通过编译技术实现(不待来自 VM 或 OS
端的支撑),挂于经过代码来收效。基本上,每个挂于函数(优化可能适用,但咱无以此地讨论)都转移为状态机,其中的状态对应于挂于调用。刚好在昂立于前,下同样态与有关部分变量等联袂囤于编译器生成的接近的字段中。在恢复该协程时,恢复部分变量并且状态机从刚好挂于后的状态进行。

挂于底协程可以当作保障其挂于状态和局部变量的靶子来囤和传递。这种对象的品种是
Continuation,而这里描述的浑代码转换对应为经典的延续性传递风格(Continuation-passing
style)。因此,挂于函数有一个
Continuation 类型的附加参数作为高级选项。

关于协程工作规律的复多细节可以当夫企划文档蒙找到。在其余语言(如
C# 或者 ECMAScript 2016)中的 async/await
的类似描述与此相关,虽然其贯彻之语言功能可能无像 Kotlin 协程这样通用。

协程的实验性状态

协程的计划性是实验性的,这意味它可能于将揭晓的版中改变。当于
Kotlin 1.1
中编译协程时,默认情况下会报一个警戒:“协程”功能是试错性的。要转移有拖欠警告,你要指定
opt-in
标志。

出于那个实验性状态,标准库中协程相关的 API 放在
kotlin.coroutines.experimental
包下。当设计完成同时实验性状态解除时,最终的 API 会移动及
kotlin.coroutines,并且尝试包会被保留(可能于一个单身的预制构件被)以实现向后相当。

重点注意事项:我们建议库作者遵循相同惯例:给暴露无遗基于协程 API
的包添加“experimental”后缀(如
com.example.experimental),以要你的堆栈保持第二进制兼容。当最后 API
发布时,请按照下列步骤操作:

  • 将所有 API 复制到 com.example(没有 experimental 后缀),
  • 保障实验包的朝后兼容性。

当即将最小化你的用户的迁徙问题。

标准 API

协程有三只重要有:

  • 语言支持(即要达到所陈述之昂立于效),
  • Kotlin 标准库中的最底层核心 API,
  • 得一直当用户代码中采用的高等级 API。

底层 API:kotlin.coroutines

根 API 相对比较小,并且除了创建更尖端的仓库外,不应下其。
它由少数单举足轻重承保组成:

  • kotlin.coroutines.experimental涵盖主要项目及下述原语
    • createCoroutine()
    • startCoroutine()
    • suspendCoroutine()
  • kotlin.coroutines.experimental.intrinsics
    带有甚至更底层的内在函数如
    suspendCoroutineOrReturn

有关这些 API
用法的重新多细节可以于这里找到。

kotlin.coroutines 中之生成器 API

kotlin.coroutines.experimental 中就局部“应用程序级”函数是

  • buildSequence()
  • buildIterator()

这些带有在 kotlin-stdlib
中以他们和序列有关。这些函数(我们好但限于此的
buildSequence())实现了 生成器 ,即提供平等栽廉价构建惰性序列的计:

val fibonacciSeq = buildSequence {
    var a = 0
    var b = 1

    yield(1)

    while (true) {
        yield(a + b)

        val tmp = a + b
        a = b
        b = tmp
    }
}

顿时通过创办一个协程生成一个惰性的、潜在无限的斐波那么契数列,该协程通过调用
yield()
函数来闹连续的斐波纳契数。当以这样的队的迭代器上迭代每一样步,都见面履行好成下一个往往之协程的另外一样局部。因此,我们得由该队中取出任何有限的数字列表,例如
fibonacciSeq.take(8).toList() 结果是
[1, 1, 2, 3, 5, 8, 13, 21]。协程足够廉价而这老实用。

为演示这样一个行的确实惰性,让咱在调用 buildSequence()
内部输出一些调试信息:

val lazySeq = buildSequence {
    print("START ")
    for (i in 1..5) {
        yield(i)
        print("STEP ")
    }
    print("END")
}

// 输出序列的前三个元素
lazySeq.take(3).forEach { print("$it ") }

运转方面的代码看,是无是我们输出前三独因素的数字和转变循环的 STEP
有陆续。这意味着计算确实是惰性的。要出口 1,我们仅实行及第一单
yield(i),并且经过被见面输出 START。然后,输出 2,我们要连续下一个
yield(i),并会见输出 STEP3 也一如既往。永远不见面输出再下一个
STEP(以及END),因为咱们还为未曾要序列的继承元素。

为一不成产生值的聚众(或序列),可以运用 yieldAll() 函数:

val lazySeq = buildSequence {
    yield(0)
    yieldAll(1..10) 
}

lazySeq.forEach { print("$it ") }

buildIterator() 的工作方法接近于
buildSequence(),但返回一个惰性迭代器。

好经为 SequenceBuilder
类写挂于扩展(带有上文描述的
@RestrictsSuspension 注解)来为 buildSequence()
添加于定义生产逻辑(custom yielding logic):

suspend fun SequenceBuilder<Int>.yieldIfOdd(x: Int) {
    if (x % 2 != 0) yield(x)
}

val lazySeq = buildSequence {
    for (i in 1..10) yieldIfOdd(i)
}

外高档 API:kotlinx.coroutines

除非同协程相关的主干 API 可以打 Kotlin
标准库获得。这要不外乎拥有因协程的堆栈或者使的为主原语和接口。

大部分冲协程的应用程序级API都用作独立的堆栈发布:kotlinx.coroutines。这个库房覆盖了

  • 使用kotlinx-coroutines-core的平台无关异步编程

  • 斯模块包括支持 select 和另外福利原语的接近 Go 的管道

  • 这库房底概括指南在这里。

  • 基于 JDK 8 中的 CompletableFuture
    API:kotlinx-coroutines-jdk8

  • 依据 JDK 7 及重新胜似版本 API 的非阻塞
    IO(NIO):kotlinx-coroutines-nio

  • 支持 Swing (kotlinx-coroutines-swing) 和 JavaFx
    (kotlinx-coroutines-javafx)

  • 支持 RxJava:kotlinx-coroutines-rx

这些库既当使通用任务易用的方便之
API,也当如何构建基于协程的仓库底端到端示例。

更多

集合

和大部分语言不同,Kotlin 区分可变集合和不可变集合(lists、sets、maps
等)。精确控制什么时集合可编制有助于消除 bug 和计划性可以的 API。

先了解一个可变集合的仅仅读 视图
和一个着实的不可变集合之间的界别是非常重大之。它们还好创建,但品种系统非可知发表她的距离,所以出于乃来跟(是否系)。

Kotlin 的 List<out T> 类型是一个提供只读操作而
sizeget等之接口。和 Java 好像,它继续自 Collection<T> 进而继承自
Iterable<T>。改变 list 的主意是出于 MutableList<T>
加入的。这同一模式同样适用于 Set<out T>/MutableSet<T>
Map<K, out V>/MutableMap<K, V>

咱得以看下 list 及 set 类型的中坚用法:

val numbers: MutableList<Int> = mutableListOf(1, 2, 3)
val readOnlyView: List<Int> = numbers
println(numbers)        // 输出 "[1, 2, 3]"
numbers.add(4)
println(readOnlyView)   // 输出 "[1, 2, 3, 4]"
readOnlyView.clear()    // -> 不能编译

val strings = hashSetOf("a", "b", "c", "c")
assert(strings.size == 3)

Kotlin 没有特别的语法结构创建 list 或 set。 要用标准库的法门,如
listOf()mutableListOf()setOf()mutableSetOf()
在无性能关键代码中创造 map
可以用一个简的惯用法来完成:mapOf(a to b, c to d)

留意点的 readOnlyView 变量(译者注:与相应可换集合变量
numbers)指向相同之根 list 并会见随着更改。 如果一个 list
只在但读引用,我们可以设想该集了不可变。创建一个这么的会师的一个简方法如下:

val items = listOf(1, 2, 3)

目前 listOf 方法是使 array list
实现之,但是未来可运用其知道好无能够转换的真情,返回重新省去内存的通通不可变的聚集类型。

小心这些项目是协变的。这意味,你可将一个
List<Rectangle> 赋值给 List<Shape> 假定 Rectangle 继承自
Shape。对于可变集合类型这是免允的,因为这将导致运行时故障。

突发性你想叫调用者返回一个集在某特定时间之一个快照, 一个保险非见面转换的:

class Controller {
    private val _items = mutableListOf<String>()
    val items: List<String> get() = _items.toList()
}

这个 toList 扩展方法就是复制列表项,因此归的 list 保证永远不见面变动。

List 和 set 有成百上千有效之恢宏方法值得熟悉:

val items = listOf(1, 2, 3, 4)
items.first() == 1
items.last() == 4
items.filter { it % 2 == 0 }   // 返回 [2, 4]

val rwList = mutableListOf(1, 2, 3)
rwList.requireNoNulls()        // 返回 [1, 2, 3]
if (rwList.none { it > 6 }) println("No items above 6")  // 输出“No items above 6”
val item = rwList.firstOrNull()

…… 以及拥有你所想之实用工具,例如 sort、zip、fold、reduce 等等。

Map 遵循千篇一律模式。它们可以好地实例化和走访,像这样:

val readWriteMap = hashMapOf("foo" to 1, "bar" to 2)
println(readWriteMap["foo"])  // 输出“1”
val snapshot: Map<String, Int> = HashMap(readWriteMap)

种安全暨智能转换

空安全

Kotlin 的门类系统

  • 然而空类型
  • 非空类型

其消除了诸多编程语言(如: Java)来自于代码空引用,而致的
NullPointerException 或简称 NPE

NOTE: Kotlin 发生 NPE 原因恐怕如下:

  • 显式调用 throw NullPointerException()
  • 运用了下文描述的 !! 操作符
  • 外表 Java 代码导致的

在上面 变量
中, Kotlin 默认声明变量时是非空类型的,要而该变量接收 null 值,需使用
操作符 , 例子如下

var aNullNothing = null
var bNullUnable: Int = null //不能为空
var cNullUnable = 1 //不能为空
var cNullable: Int? = null //能为空
var dNullable: Any? = 1 //能为空 

fun fun0(): Unit {
    aNullNothing = 1 //Nothing error
    cNullUnable = null
    cNullable = 1
    dNullable = null //可以 null
}

当声明可空类型变量时,它是无安全的,访问方法或者性质时得发处理:

  • 以基准被检查 null ,但不过适用于 val 且不可掩盖(即未可知因此 open
    修饰)或者 get 的不可变的变量。
  • 有惊无险之调用 ?. , 若为null 则跳过,否则就调用
  • !! 操作符 ,会回来一个非空的值,否则抛出一个 NPE 异常

规格中反省 nul 例子

open class TestCheckNull {
    val cReadNullable: Int? = 1
    val cGetReadNullable: Int? get() = 1
    open val cOverrideReadNullable: Int? = 1

    fun fun0(): Unit {
        if (cReadNullable != null) {
            cReadNullable.dec() //tips replace safe access expression
        }
        if (cGetReadNullable != null) {
            cGetReadNullable.dec()
        }
        if (cOverrideReadNullable != null) {
            cOverrideReadNullable.dec()
        }
    }
}

安康调用和!! 操作符对比

cNullUnable.dec() //保证不会导致 NPE
val hc = dNullable?.hashCode() //dNullable == null return null, hc is null
val dec = cNullable?.dec() // cNullable !=null return cNullable.dec(),dec is "0"
cNullable!!.dec() // cNullable !=null execute dec()
dNullable!!.toString() // dNullable == null throws NPE
var aNotNullObject = cNullable!!

品种检测及平安之类型转换

  • is !is
    运算符检测一个表达式是否有型的一个实例。在成千上万状下,不待以
    Kotlin 中采用显式转换操作符,因为编译器跟踪不可变值的
    is-检查,并当急需常自动插入(安全的)转换:

val obj: Any = ""
if (obj is String) {
    print(obj.length)
}
if (obj !is String) { // 与 !(obj is String) 相同
    print("Not a String")
} else if (obj is String) {
    print(obj.length)
} else {
    print(obj.length)
}
when(obj){
    is String -> obj.length
}
  • as as? 运算符能把目标转换为对象项目,常规类型转换可能会见招致
    ClassCastException。使用安全的类型转换符
    as?,如果尝试换不成功则回 null

val father = Father()
val son = Son()
println(father is Son)
println(son is Father)

val fatherSon: Father = Son()
println(fatherSon is Son)
println(fatherSon is Father)

val sonFatherSon: Son = fatherSon as Son
println(sonFatherSon != null)

val newFather: Son? = father as? Son
val newFather1 = father as? Son  //newFather1 start define val newFather : Son?
val newFather2 = father as Son // newFather1 start define val newFather : Son
println(newFather == null)

NOTE: Kotlin 类型检测好智能, 想了解请复多参考 Type Checks and
Casts

操作符重载

Kotlin
允许我们为祥和的品类提供预定义的一样组操作符的落实。这些操作符具有定位的号子表示(如
+
*)和固化的优先级。为贯彻如此的操作符,我们也对应的门类(即第二元操作符左侧的花色以及一元操作符的参数类型)提供了一个定点名字的成员函数或推而广之函数。
重载操作符的函数需要为此 operator 修饰符标记。

除此以外,我们讲述为不同操作符规范操作符重载的约定。

一元前缀操作符

表达式 翻译为
+a a.unaryPlus()
-a a.unaryMinus()
!a a.not()

此表是说,当编译器处理例如表达式 +a 时,它实施以下步骤:

  • 确定 a 的类型,令其为 T
  • 也接收者 T 查找一个含 operator 修饰符的凭参函数
    unaryPlus(),即成员函数或扩大函数。
  • 倘函数不存在或者非明明,则致编译错误。
  • 如若函数存在都其回到路也 R,那便发表式 +a 具有类型 R

注意
这些操作与具有其他操作都指向骨干类型开了优化,不见面为她引入函数调用的付出。

以下是安更载同首先减运算符的示范:

data class Point(val x: Int, val y: Int)

operator fun Point.unaryMinus() = Point(-x, -y)

val point = Point(10, 20)
println(-point)  // 输出“(-10, -20)”

递增与递减

表达式 翻译为
a++ a.inc() + 见下文
a– a.dec() + 见下文

inc()dec() 函数必须回到一个价,它用来赋值给采用++--
操作的变量。它们不该变更在其上调用 inc()dec() 的对象。

编译器执行以下步骤来分析后缀花样的操作符,例如 a++

  • 确定 a 的类型,令其为 T
  • 寻一个适用于色为 T 的收信人的、带有 operator
    修饰符的无论参数函数 inc()
  • 检查函数的回路是 T 的子类型。

计量表达式的步调是:

  • a 的初始值存储到临时存储 a0 中,
  • a.inc() 结果赋值给 a
  • a0 作为表达式的结果回到。

对于 a--,步骤是全类似的。

对于前缀形式 ++a--a 以同等方式分析,其步骤是:

  • a.inc() 结果赋值给 a
  • a 的新值作为表达式结果回到。

二元操作算术运算符

表达式 翻译为
a + b a.plus(b)
a – b a.minus(b)
a * b a.times(b)
a / b a.div(b)
a % b a.rem(b)、 a.mod(b) (已弃用)
a..b a.rangeTo(b)

对这个表中的操作,编译器只是解析成翻译为列着之表达式。

请注意,自 Kotlin 1.1 起支持 rem 运算符。Kotlin 1.0 使用 mod
运算符,它在
Kotlin 1.1 中被弃用。

示例

下是一个由被定值起始之 Counter 类的言传身教,它可采取重载的 +
运算符来增加计数。

data class Counter(val dayIndex: Int) {
    operator fun plus(increment: Int): Counter {
        return Counter(dayIndex + increment)
    }
}

In操作符

表达式 翻译为
a in b b.contains(a)
a !in b !b.contains(a)

对于 in!in,过程是平的,但是参数的依次是倒转的。

目录访问操作符

表达式 翻译为
a[i] a.get(i)
a[i, j] a.get(i, j)
a[i_1, ……, i_n] a.get(i_1, ……, i_n)
a[i] = b a.set(i, b)
a[i, j] = b a.set(i, j, b)
a[i_1, ……, i_n] = b a.set(i_1, ……, i_n, b)

方括号转换为调用带有适当数量参数的 getset

调用操作符

表达式 翻译为
a() a.invoke()
a(i) a.invoke(i)
a(i, j) a.invoke(i, j)
a(i_1, ……, i_n) a.invoke(i_1, ……, i_n)

圆括号转换为调用带有适当数量参数的 invoke

广义赋值

表达式 翻译为
a += b a.plusAssign(b)
a -= b a.minusAssign(b)
a *= b a.timesAssign(b)
a /= b a.divAssign(b)
a %= b a.remAssign(b), a.modAssign(b)(已弃用)

对此赋值操作,例如 a += b,编译器执行以下步骤:

  • 倘右列的函数可用
    • 若果相应的次元函数(即 plusAssign() 对应于
      plus())也可用,那么告诉错误(模糊)。
    • 管其返回路是 Unit,否则报告错误。
    • 生成 a.plusAssign(b) 的代码
  • 不然试着生成 a = a + b 的代码(这里包含类型检查:a + b
    的型必须是 a 的子类型)。

注意:赋值在 Kotlin 中不是表达式。

顶与差操作符

表达式 翻译为
a == b a?.equals(b) ?: (b === null)
a != b !(a?.equals(b) ?: (b === null))

注意===!==(同一性检查)不可重载,因此无存在对她们之预定

这个 == 操作符有些与众不同:它叫翻成一个繁杂的表达式,用于筛选 null
值。
null == null 总是 true,对于非空的 xx == null 总是 false
而未见面调用 x.equals()

比操作符

表达式 翻译为
a > b a.compareTo(b) > 0
a < b a.compareTo(b) < 0
a >= b a.compareTo(b) >= 0
a <= b a.compareTo(b) <= 0

有着的较还变为对 compareTo 的调用,这个函数需要返回 Int

性委托操作符

provideDelegategetValue 以及 setValue
操作符函数已于信托属性中描述。

取名函数的中缀调用

咱俩可以通过中缀函数的调用
来模拟自定义中缀操作符。

型相等性

Kotlin 中产生有限栽档次的相等性:

  • 援相等(两独援指向同一对象)

  • 结构相当(用 equals() 检查)

引用相等

援相等由 ===(以及那个否定形式 !==)操作判断。a === b 当还只有当 a
和 b 依为与一个靶时求值为 true。

组织相当

布局等由 ==(以及那否定形式 !=)操作判断。按照惯例,像 a == b
这样的表达式会翻成

a?.equals(b) ?: (b === null)

也就是说要 a 不是 null 则调用 equals(Any?) 函数,否则(即 a
null)检查 b 是否与 null 引用相等。

请注意,当与 null 显式比较常了没必要优化你的代码:a == null
会被电动转换为 a=== null。同类型才生可比性。

This表达式

为表示手上的 接收者 我们采取 this 表达式:

  • 在类的成员中,this
    指的是此类的脚下目标
  • 在恢宏函数或者带动接收者的套数字面值中,
    this 表示于点左侧传递的 接收者 参数。

如果 this
没有限定符,它依靠的凡极内层的涵盖它的作用域。要引用其他作用域中的
this,请使用 标明签限定符

fun main(args: Array<String>) {
    val kotlinThisExpression = KotlinThisExpression()
    println(kotlinThisExpression.leftReference() === kotlinThisExpression)
    kotlinThisExpression.InnerKotlinThisExpression().test()
}

private class KotlinThisExpression {
    val thisClassObject get() = this

    inner class KotlinThisExpression {
        //val thisClassObject get() = this@KotlinThisExpression //不明确label
        val thisClassObject get() = this //内部类名相同,不能用限定的 this

    }


    inner class InnerKotlinThisExpression { // 隐式标签 @InnerKotlinThisExpression
        fun InnerKotlinThisExpression.fuck() { // 隐式标签 @fuck
            val a = this@KotlinThisExpression // KotlinThisExpression 的 this
            val b = this@InnerKotlinThisExpression // InnerKotlinThisExpression 的 this

            val c = this // fuck() 的接收者,一个 InnerKotlinThisExpression
            val d = this@fuck // fuck() 的接收者,一个 InnerKotlinThisExpression

            val label = label@ fun String.() {
                println(this)// label 的接收者
            }

            "label".label()
            val lambda = { ->
                // fuck() 的接收者,因为它包含的 lambda 表达式
                // 没有任何接收者
                println(this)
            }

            lambda()
        }

        fun test() {
            fuck()
        }
    }
}

private fun KotlinThisExpression.leftReference() = this.thisClassObject //this 表示在点左侧传递的 接收者 参数。

Nothing 类型

如果用 null
来初始化一个使想类型的值,而以没外信息但用以确定还现实的门类时,编译器会想出
Nothing? 类型:

val nothingInt/*: Nothing?*/ = null
val list:List<Nothing?> = listOf(null)

另外Kotlin 中 throw 是表达式, 表达式的种是异常类别 Nothing
该型没有价值,而是用来标记永远不能够上的代码位置。Nothing
可以就此来号一个永恒不见面回去的函数, 也得以当作 Elvis 表达式的相同组成部分:

val nothingInt/*: Nothing?*/ = null
val list: List<Nothing?> = listOf(null)
fun fail(message: String): Nothing {
    throw IllegalArgumentException(message)
}
fail("fail")
//作为 Elvis 表达式的一部分 
var exception = null ?: throw RuntimeException("throw")

解构声明

解构声明是创立多个变量和对象componentN
函数对应起来。例如当上面的数据类中

val (name, age) = KotlinDataClass.User("Lisa", 18)

NOTE: componentN() 函数需要为此 operator
关键字记,以允许在解构声明中运用它。它可以为此for-循环、

map-映射, 以及 lambda 表达式中。

fun main(args: Array<String>) {
    val (name, age) = KotlinDeconstruction.Person("jack", 32)
    println("$name $age")

    val request = KotlinDeconstruction.request()
    val (rs, code) = request
    println("result = $rs , code = $code")
    //下划线用于未使用的变量
    val (_, responseCode) = request
    println(responseCode)
    println(request.component1())
    println(request.component2())

    //解构声明和Map
    val map = mutableMapOf<String, String>()
    for (it in 1..10) {
        map.put(it.toString(), it.toString())
    }
    for ((k, v) in map) {
        println("map key = $k, value = $v")
    }
    map.mapValues { entry -> println("key = ${entry.key}, value = ${entry.value}!") }
    map.mapValues { (key, value) -> println("key = $key, value = $value!") }
}


private class KotlinDeconstruction {
    class Person(val name: String, val age: Int) {
        operator fun component1(): Any = name
        operator fun component2(): Any = age
    }

    data class Response(val result: String, val code: Int)

    companion object {
        fun request(): Response {
            //request network
            return Response("ok", 200)
        }
    }
}

解构声明的补益, 如request
函数时若回去两只东西常常,用它们爽爆了。因为编译器始终会创造多独变量接收,效率并无可比前用对象的赛。但其实并不需要解析一个对象里的大方变量,否则通过对象
.属性获取值。

val (name, age) = person //编译器会生成如下两句代码
val name = person.component1()
val age = person.component2()

相等性

Kotlin 中来一定量栽档次的相等性:

  • 援相等(两只援指向同一对象)
  • 布局等(用 equals() 检查)

引用相等

援相等由 ===(以及那个否定形式 !==)操作判断。a === b 当还仅当 a
b 指向与一个目标时求值为 true。

结构相当

组织等由 ==(以及那个否定形式 !=)操作判断。按照老,像 a == b
这样的表达式会翻成

a?.equals(b) ?: (b === null)

也就是说要 a 不是 null 则调用 equals(Any?) 函数,否则(即 a
null)检查 b 是否与 null 引用相等。

请注意,当与 null 显式比较时全没有必要优化你的代码:a == null
会被电动转换为 a=== null

浮点数相等性

当相等性检测的星星独操作数都是静态已知道之(可空或非空的)Float
Double 类型时,该检测仍 IEEE 754 浮点数运算标准。

要不会动用无适合该规范的结构相等性检测,这会促成 NaN 等于其自身,而
-0.0 不等于 0.0

异常

异常类

Kotlin 中具备特别类都是 Throwable 类的子孙类。
每个异常都出信息、堆栈回溯信息及可选的案由。

使用 throw-表达式来废弃来很:

throw MyException("Hi There!")

使用 try-表达式来捕获异常:

try {
    // 一些代码
}
catch (e: SomeException) {
    // 处理程序
}
finally {
    // 可选的 finally 块
}

可生散装到多单 catch 块。finally 块可以简单。 但是 catch
finally 块至少应该存在一个。

Try 是一个表达式

try 是一个表达式,即其可以产生一个返回值:

val a: Int? = try { parseInt(input) } catch (e: NumberFormatException) { null }

try-表达式的返回值是 try 块中的最终一个表达式或者是(所有)catch
块中之末梢一个表达式。 finally 块中的情节未会见影响表达式的结果。

受检的不胜

Kotlin 没有受检的老大。这间有多由,但咱见面提供一个粗略的事例。

以下是 JDK 中 StringBuilder 类实现的一个演示接口:

Appendable append(CharSequence csq) throws IOException;

本条签名是啊意思? 它是说,每次自我增加一个字符串到部分事物(一个
StringBuilder、某种日志、一个控制台等)上经常自己就是务须捕获那些
IOException。 为什么?因为它恐怕在推行 IO 操作(Writer 也实现了
Appendable)…… 所以它导致这种代码随处可见的起:

try {
    log.append(message)
}
catch (IOException e) {
    // 必须要安全
}

即时并无好,参见《Effective
Java》
第 65 条:无须忽略异常

Bruce Eckel 在《Java 是否要受检的大?》(Does Java need Checked
Exceptions?)
中指出:

经一些略带序测试得出的定论是异常规范会同时增长开发者的生产力与代码质量,但是大型软件项目之经验表明一个不等之定论——生产力降低、代码质量非常少或者无增长。

其他有关引证:

  • 《Java 的受检异常是一个荒唐》(Java’s checked exceptions were a
    mistake)(Rod
    Waldhoff)
  • 《受检异常的苦恼》(The Trouble with Checked
    Exceptions)(Anders
    Hejlsberg)

注意:throw 表达式的项目是特类别
Nothing。参见Nothing类型

反射

照是这么的均等组语言和货栈功能,它同意以运作时自省你的先后的结构。 Kotlin
让语言中的函数和性质做吗同样当百姓、并针对性该反省(即在运作时得知一个称呼或一个特性或函数的项目)与简短地采用函数式或响应式风格严谨有关。

以 Java 平台上,使用反射作用所需要的周转时组件作为单身的 JAR
文件(kotlin-reflect.jar)分发。这样做是以削减非动反射作用的应用程序所欲的运转时库的大大小小。如果您待使用反射,请确保该
.jar文件添加至路的 classpath 中。

类引用

不过核心的反光功能是获取 Kotlin 类的周转时引用。要获得对静态已解之 Kotlin
类的援,可以行使 类字面值 语法:

val c = MyClass::class

该引用是
KClass
类型的价值。

告留意,Kotlin 类引用和 Java 类引用不同。要博得 Java 类引用, 请在
KClass 实例上运 .java 属性。

绑定的接近援(自 1.1 起)

由此动用对象作为接收者,可以为此同样之 ::class
语法获取指定对象的切近的援:

val widget: Widget = ……
assert(widget is GoodWidget) { "Bad widget: ${widget::class.qualifiedName}" }

而可以得对象的精确类的援,例如 GoodWidget
BadWidget,尽管接收者表达式的档次是 Widget

函数引用

当我们发一个命名函数声明如下:

fun isOdd(x: Int) = x % 2 != 0

咱们好死轻地直接调用它(isOdd(5)),但是咱也可以管其当作一个值传递。例如传为另外一个函数。
为夫,我们采用 :: 操作符:

val numbers = listOf(1, 2, 3)
println(numbers.filter(::isOdd)) // 输出 [1, 3]

这里 ::isOdd 是函数类型 (Int) -> Boolean 的一个价。

当上下文中曾经了解函数期望之类别时,:: 可以用于重载函数。 例如:

fun isOdd(x: Int) = x % 2 != 0
fun isOdd(s: String) = s == "brillig" || s == "slithy" || s == "tove"

val numbers = listOf(1, 2, 3)
println(numbers.filter(::isOdd)) // 引用到 isOdd(x: Int)

还是,你得透过将方引用存储于具备显式指定项目的变量中来提供必需之上下文:

val predicate: (String) -> Boolean = ::isOdd   // 引用到 isOdd(x: String)

一旦我们得运用类似的分子函数或扩大函数,它用是限制的。 例如
String::toCharArray 为类型
String提供了一个扩张函数:String.() -> CharArray

以身作则:函数组合

考虑以下函数:

fun <A, B, C> compose(f: (B) -> C, g: (A) -> B): (A) -> C {
    return { x -> f(g(x)) }
}

她回到一个污染给它的个别只函数的重组:compose(f, g) = f(g(*))
现在,你得用其以被可调用引用:

fun length(s: String) = s.length

val oddLength = compose(::isOdd, ::length)
val strings = listOf("a", "ab", "abc")

println(strings.filter(oddLength)) // 输出 "[a, abc]"

属性引用

只要把性能作为 Kotlin中 的甲级对象来聘,我们吧得以应用 :: 运算符:

var x = 1

fun main(args: Array<String>) {
    println(::x.get()) // 输出 "1"
    ::x.set(2)
    println(x)         // 输出 "2"
}

表达式 ::x 求值为 KProperty<Int> 类型的属性对象,它同意我们用
get() 读博其的值,或者使用 name
属性来抱属性名。更多信息要参见关于 KProperty
类的文档。

对于可变属性,例如 var y = 1::y 返回
KMutableProperty
类型的一个值, 该种有一个 set() 方法。

特性引用得据此当匪欲参数的函数处:

val strs = listOf("a", "bc", "def")
println(strs.map(String::length)) // 输出 [1, 2, 3]

假定拜访属于类的分子的特性,我们这样界定它:

class A(val p: Int)

fun main(args: Array<String>) {
    val prop = A::p
    println(prop.get(A(1))) // 输出 "1"
}

于扩大属性:

val String.lastChar: Char
    get() = this[length - 1]

fun main(args: Array<String>) {
    println(String::lastChar.get("abc")) // 输出 "c"
}

暨 Java 反射的互操作性

在Java平台及,标准库包含反射类的扩展,它提供了和 Java
反射对象中映射(参见 kotlin.reflect.jvm保)。 例如,要物色一个就此作
Kotlin 属性 getter 的 幕后字段或 Java方法,可以这么描绘:

import kotlin.reflect.jvm.*

class A(val p: Int)

fun main(args: Array<String>) {
    println(A::p.javaGetter) // 输出 "public final int A.getP()"
    println(A::p.javaField)  // 输出 "private final int A.p"
}

要是博相应为 Java 类的 Kotlin 类,请以 .kotlin 扩展属性:

fun getKClass(o: Any): KClass<Any> = o.javaClass.kotlin

构造函数引用

构造函数可以像方法和性能那样引用。他们得以用来希望这样的函数类型对象的其他地方:它和该构造函数接受平等参数并且返回相应项目的靶子。
通过以 :: 操作符并补充加类名来引用构造函数。考虑下的函数,
它想一个不论是参并返回 Foo 类型的函数参数:

class Foo

fun function(factory: () -> Foo) {
    val x: Foo = factory()
}

使用 ::Foo,类 Foo 的零参数构造函数,我们可以这么概括地调用它:

function(::Foo)

绑定的函数和特性引用(自 1.1 起)

汝可以引用特定对象的实例方法。

val numberRegex = "\\d+".toRegex()
println(numberRegex.matches("29")) // 输出“true”

val isNumber = numberRegex::matches
println(isNumber("29")) // 输出“true”

替直接调用方法 matches 的是咱囤其引述。
这样的引用会绑定到该接收者上。
它可以直接调用(如上例所示)或者用于其它期待一个函数类型表达式的时候:

val strings = listOf("abc", "124", "a70")
println(strings.filter(numberRegex::matches)) // 输出“[124]”

于绑定的型和呼应的不绑定类型的援。
绑定的可调用引用有那个接收者“附加”到那个达到,因此接收者的色不再是参数:

val isNumber: (CharSequence) -> Boolean = numberRegex::matches

val matches: (Regex, CharSequence) -> Boolean = Regex::matches

特性引用也堪绑定:

val prop = "abc"::length
println(prop.get())   // 输出“3”

型别名

品种别名为现有项目提供替代名称。
如果类型名称最为丰富,你可以另外引入较短的名号,并行使初的称呼替代原类型名。可以吗函数类型提供另外的别名,也得以为其中类和嵌套类创建新名称

品类别名不见面引入新品类。 它们当效于相应的脚类型。 当你以代码中补充加
typealias Predicate<T> 并使用 Predicate<Int> 时,Kotlin
编译器总是拿它们扩展为 (Int) -> Boolean

fun main(args: Array<String>) {
    val net: Net = Network()

    val enable = enable(net) {
        netStatus() == 0
    }
    println(enable)

    val p: Predicate<Int> = { it > 0 }
    println(listOf(1, -2).filter(p)) // 输出 "[1]"
}


typealias Net = Network
typealias Node = Network.Node

typealias NodeSet = Set<Network.Node>
typealias FileTable<N> = MutableMap<N, MutableList<File>>

typealias MyHandler = (Int, String, Any) -> Unit
typealias Predicate<T> = (T) -> Boolean

fun netStatus(): Int = 0

class Network {
    inner class Node
}

fun <T> enable(t: T, p: Predicate<T>): Boolean {
    return p(t)
}