Kotlin笔记小结(For|Kotlin笔记小结(For Java Developer)

这篇文章为kotlin学习记录,主要针对的是自己的知识盲区,不适用于新手。
文中所有demo均来自于kotlin官网
类型 整形

Type Size (bits) Min value Max value
Byte 8 -128 127
Short 16 -32768 32767
Int 32 -2,147,483,648 (-231) 2,147,483,647 (231 - 1)
Long 64 -9,223,372,036,854,775,808 (-263) 9,223,372,036,854,775,807 (263 - 1)
浮点型
Type Size (bits) Significant bits Exponent bits Decimal digits
Float 32 24 8 6-7
Double 64 53 11 15-16
变量 只读变量使用val,可以被重新赋值的变量使用关键字var
// example of val val a: Int = 1// immediate assignment val b = 2// `Int` type is inferred val c: Int// Type required when no initializer is provided c = 3// deferred assignment// example of var var x = 5 // `Int` type is inferred x += 1

函数 默认参数
fun foo(a: Int = 0, b: String = "") { ... }

函数扩展
fun String.spaceToCamelCase() { ... }"Convert this to camelcase".spaceToCamelCase()

单行表达式函数
fun theAnswer() = 42// 同样适用于when fun transform(color: String): Int = when (color) { "Red" -> 0 "Green" -> 1 "Blue" -> 2 else -> throw IllegalArgumentException("Invalid color param value") }

Lambda表达式
匿名函数
带receiver的函数字面量
类似于函数扩展,使得你能够直接在函数体中直接返回到receiver object. 在函数体重,receiver object使用this访问到,你可以通过this来获取到reciver object中的属性和方法。
val sum: Int.(Int) -> Int = { other -> plus(other)// 匿名函数 val sum = fun Int.(other: Int): Int = this + other

Lambda表达式 基本语法
val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }

  • lambda 使用 {} 包围
  • 后面的为函数体
  • 如果lambda的返回值不为空(Unit),那么最后一个表达式即为返回值,上面的例子中,x + y 即为返回值
类型声明可以省略,简略写法为:
val sum = { x: Int, y: Int -> x + y }

lambda为函数的最后一个参数
如果lambda为函数的最后一个参数,那么可以直接简写在函数外面,例如
val product = items.fold(1) { acc, e -> acc * e }// 如果lambda作为函数的最后一个参数,那么,函数()可以省略 run { println("...") }

隐式的it
【Kotlin笔记小结(For|Kotlin笔记小结(For Java Developer)】如果lambda只有一个参数,那么这个参数可以不必要声明,参数会隐式被it代替
ints.filter { it > 0 } // this literal is of type '(it: Int) -> Boolean'// 等价于 ints.filter { intVal -> intVal > 0 }

lambda返回值
默认如果lambda的返回值不为空(Unit),那么最后一个表达式即为返回值,但是也可以显示地使用return来进行值的返回,但是要遵循qualified return语法
ints.filter { val shouldFilter = it > 0 shouldFilter }ints.filter { val shouldFilter = it > 0 return@filter shouldFilter }

跳过lambda的返回值
如果不需要使用到lambda的参数,那么可以使用_代替
map.forEach { _, value -> println("$value!") }

匿名函数 匿名函数和lambda表达式一样,都是函数字面量。
Function literals are functions that are not declared but are passed immediately as an expression.
fun(x: Int, y: Int): Int = x + y

和lambda相比,匿名函数主要有两点不同
  • 匿名函数可以指定返回类型,而lambda不同
  • 如果没有定义返回标签,lambda的返回会直接返回到最近定义了fun关键字的函数,而匿名函数会返回到函数本身
类 类的继承使用:,类默认是final类型的(即不可修改),如果要让类可继承,使用open关键字
open class Shapeclass Rectangle(var height: Double, var length: Double): Shape() { var perimeter = (height + length) * 2 }

DTOs(POJOs/POCOs)
data class主要用作承载数据的对象,可以用作两个系统之间数据的交换
data class Customer(val name: String, val email: String)

  • getters (and setters in case of var s) for all properties
  • equals()
  • hashCode()
  • toString()
  • copy()
  • component1(), component2() 用作数据解构
实例化一个抽象类
abstract class MyAbstractClass { abstract fun doSomething() abstract fun sleep() }fun main() { val myObject = object : MyAbstractClass() { override fun doSomething() { // ... }override fun sleep() { // ... } } myObject.doSomething() }

匿名类
Object表达式 主要用来创建匿名类
val helloWorld = object { val hello = "Hello" val world = "World" // object expressions extend Any, so `override` is required on `toString()` override fun toString() = "$hello $world" }

匿名内部类
匿名内部类也叫伴生类,使用关键字companion,通常的作用是为类提供静态属性和方法,类似于java的static关键字
// 一个类中只允许声明一个伴生类 // 下面的例子中,如果同时存在两个伴生类,会报类型错误 class MyClass { // 也可以声明名字 companion object Factory { fun create(): MyClass = MyClass() }// 匿名, Companion companion object { fun create(): MyClass = MyClass() } }fun main() { // 调用可以省略掉伴生类名 println(MyClass.create()) println(MyClass.Factory.create()) println(MyClass.Companion.create()) }

字符串模板
var a = 1 // simple name in template: val s1 = "a is $a" a = 2 // 如果需要计算,则使用${xxx}来表示 // arbitrary expression in template: val s2 = "${s1.replace("is", "was")}, but now is $a"

条件判断 条件判断采用关键字if / else if / else,需要注意一点的是if也可以作为表达式使用
fun maxOf(a: Int, b: Int) = if (a > b) a else b

if-not-null-else 简写
val files = File("Test").listFiles()println(files?.size ?: "empty") // if files is null, this prints "empty"// To calculate the fallback value in a code block, use `run` val filesSize = files?.size ?: run { return someSize } println(filesSize)

if not null 执行 (let)
val value = https://www.it610.com/article/...value?.let { ... // execute this block if not null }

Break and continue 标签
loop@ for (i in 1..100) { for (j in 1..100) { if (...) break@loop } }

Return to 自定义标签
the return -expression returns from the nearest enclosing function
fun foo() { listOf(1, 2, 3, 4, 5).forEach { if (it == 3) return // non-local return directly to the caller of foo() print(it) } println("this point is unreachable") }// To return from a lambda expression, label it and qualify the return: fun foo() { listOf(1, 2, 3, 4, 5).forEach lit@{ if (it == 3) return@lit // local return to the caller of the lambda - the forEach loop print(it) } print(" done with explicit label") }// Often it is more convenient to use implicit labels fun foo() { listOf(1, 2, 3, 4, 5).forEach { if (it == 3) return@forEach // local return to the caller of the lambda - the forEach loop print(it) } print(" done with implicit label") }// Return to anonymous function fun foo() { listOf(1, 2, 3, 4, 5).forEach(fun(value: Int) { if (value =https://www.it610.com/article/= 3) return// local return to the caller of the anonymous function - the forEach loop print(value) }) print(" done with anonymous function") }

When(更强大的Switch)
fun describe(obj: Any): String = when (obj) { 1-> "One" "Hello"-> "Greeting" is Long-> "Long" !is String -> "Not a string" else-> "Unknown" }

Ranges 使用in关键字来判断一个变量是否处在这个区间中
val x = 10 val y = 9 if (x in 1..y+1) { println("fits in range") }

类型检查和自动转换
fun getStringLength(obj: Any): Int? { if (obj is String) { // `obj` is automatically cast to `String` in this branch return obj.length }// `obj` is still of type `Any` outside of the type-checked branch return null }// 或者 fun getStringLength(obj: Any): Int? { if (obj !is String) return null// `obj` is automatically cast to `String` in this branch return obj.length }

不安全转换
val x: String = y as String

安全转换
val x: String? = y as? String

代理 代理使用关键字by,代表的是将原本应该自己职责代理给其他的类或者属性
类代理
interface Base { fun printMessage() fun printMessageLine() }class BaseImpl(val x: Int) : Base { override fun printMessage() { print(x) } override fun printMessageLine() { println(x) } }// 交给代理类Base去做 class Derived(b: Base) : Base by b { // 也可以重新override override fun printMessage() { print("abc") } }fun main() { val b = BaseImpl(10) Derived(b).printMessage() Derived(b).printMessageLine() }

属性代理
一个规划化的栗子
class Example { // set p, get p will delegate by Delegate getValue/setValue var p: String by Delegate() }

The syntax is: val/var : by . The expression after by is a delegate, because the get() (and set()) that correspond to the property will be delegated to its getValue() and setValue() methods. Property delegates don’t have to implement an interface, but they have to provide a getValue() function (and setValue() for vars).
import kotlin.reflect.KPropertyclass 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.") } }val e = Example() println(e.p)

常用的属性代理有lazyobservable
// The first call to get() executes the lambda passed to lazy() and remembers the result. // Subsequent calls to get() simply return the remembered result. val lazyValue: String by lazy { println("computed!") "Hello" }fun main() { // computed! // Hello println(lazyValue)// Hello println(lazyValue) }// observable import kotlin.properties.Delegatesclass User { var name: String by Delegates.observable("") { prop, old, new -> println("$old -> $new") } }fun main() { val user = User() // -> first user.name = "first"// first -> second user.name = "second" }

运算符重载 运算符重载允许你重新自定义已经定义好的操作,比如”+”, “-”等等。实现运算符重载,需要使用到operator关键字
member方式
interface IndexedContainer { operator fun get(index: Int) }// 重写时可以忽略operator关键字 class OrdersList: IndexedContainer { override fun get(index: Int) { /*...*/ } }

函数扩展方式
data class Point(val x: Int, val y: Int)operator fun Point.unaryMinus() = Point(-x, -y)val point = Point(10, 20)fun main() { println(-point)// prints "Point(x=-10, y=-20)" }

Scope functions 作用域函数的目的就是让你在一个特定的object context中执行你的函数体,在这个作用域中,你能获取到这个object而无需知道它的名字
具体的作用域函数主要有:letrunwithapplyalso
Function Object reference Return value Is extension function
let it Lambda result Yes
run this Lambda result Yes
run - Lambda result No: called without the context object
with this Lambda result No: takes the context object as an argument.
apply this Context object Yes
also it Context object Yes
使用this还是it
在作用域函数中,你有两种方式获取到这个context object. lambda receiver(this) 或者 lambda argument(it)
runwithapply使用this指向这个context object,一般情况下,this可以省略。推荐使用this的情况是,需要访问或者设置这个对象的一些属性或者方法。
val adam = Person("Adam").apply { age = 20// same as this.age = 20 or adam.age = 20 city = "London" } println(adam)

letalso使用it指向这个object. it适用的场景是,这个object作为变量或者参数使用。
fun getRandomInt(): Int { return Random.nextInt(100).also { writeToLog("getRandomInt() generated value $it") } }val i = getRandomInt() println(i)

返回值
  • applyalso 返回context object(主要适用于进行对象的链式处理)
  • let, runwith 返回的是 lambda的执行结果
run
// this, Lambda result@kotlin.internal.InlineOnly public inline fun T.run(block: T.() -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return block() }

run的使用场景是:当这个lambda中同时包含对象初始化以及返回值计算
val service = MultiportService("https://example.kotlinlang.org", 80)val result = service.run { port = 8080 query(prepareRequest() + " to port $port") }// the same code written with let() function: val letResult = service.let { it.port = 8080 it.query(it.prepareRequest() + " to port ${it.port}") }

另外,run也可以作为非扩展函数使用,作为非扩展函数时,主要用来通过计算一系列语句声明来得到返回表达式。
@kotlin.internal.InlineOnly public inline fun run(block: () -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return block() }// example val hexNumberRegex = run { val digits = "0-9" val hexDigits = "A-Fa-f" val sign = "+-"Regex("[$sign]?[$digits$hexDigits]+") }for (match in hexNumberRegex.findAll("+123 -FFFF !%*& 88 XYZ")) { println(match.value) }

let
// it, Lambda result@kotlin.internal.InlineOnly public inline fun T.let(block: (T) -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return block(this) }

  • 用来执行一个或者多个函数链式调用后的结果
    val numbers = mutableListOf("one", "two", "three", "four", "five") numbers.map { it.length }.filter { it > 3 }.let { println(it) // and more function calls if needed }// 如果一个lambda块中,只有一个函数并且使用it作为它的参数 // 则可以使用函数引用(::)来代替这个lambda numbers.map { it.length }.filter { it > 3 }.let(::println)

  • 用作判空检查
    val str: String? = "Hello" //processNonNullString(str)// compilation error: str can be null val length = str?.let { println("let() called on $it") processNonNullString(it)// OK: 'it' is not null inside '?.let { }' it.length }

  • 提升代码的可阅读性
    val numbers = listOf("one", "two", "three", "four") val modifiedFirstItem = numbers.first().let { firstItem -> println("The first item of the list is '$firstItem'") if (firstItem.length >= 5) firstItem else "!" + firstItem + "!" }.uppercase() println("First item after modifications: '$modifiedFirstItem'")

apply
// this, Context Object@kotlin.internal.InlineOnly public inline fun T.apply(block: T.() -> Unit): T { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } block() return this }

apply常用作对象的配置,例如
val adam = Person("Adam").apply { age = 32 city = "London" } println(adam)

also
// it, Context Object@kotlin.internal.InlineOnly @SinceKotlin("1.1") public inline fun T.also(block: (T) -> Unit): T { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } block(this) return this }

also主要用来执行一些需要将object作为参数的操作
val numbers = mutableListOf("one", "two", "three") numbers .also { println("The list elements before adding new one: $it") } .add("four")

with
// this, Lambda result@kotlin.internal.InlineOnly public inline fun with(receiver: T, block: T.() -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return receiver.block() }

  • 持有object引用,执行函数,无需返回
    val numbers = mutableListOf("one", "two", "three") with(numbers) { println("'with' is called with argument $this") println("It contains $size elements") }

  • object的属性或者方法参与值的计算
    val numbers = mutableListOf("one", "two", "three") val firstAndLast = with(numbers) { "The first element is ${first()}," + " the last element is ${last()}" } println(firstAndLast)

    推荐阅读