Kotlin笔记小结(For Java Developer)

165 阅读5分钟

这篇文章为kotlin学习记录,主要针对的是自己的知识盲区,不适用于新手。

文中所有demo均来自于kotlin官网

类型

整形

TypeSize (bits)Min valueMax value
Byte8-128127
Short16-3276832767
Int32-2,147,483,648 (-231)2,147,483,647 (231 - 1)
Long64-9,223,372,036,854,775,808 (-263)9,223,372,036,854,775,807 (263 - 1)

浮点型

TypeSize (bits)Significant bitsExponent bitsDecimal digits
Float322486-7
Double64531115-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

如果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 Shape

class 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 = ...

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 == 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 <property name>: <Type> by <expression>. 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.KProperty

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.")
    }
}

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.Delegates

class User {
    var name: String by Delegates.observable("<no name>") {
        prop, old, new ->
        println("$old -> $new")
    }
}

fun main() {
    val user = User()
		// <no name> -> 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

FunctionObject referenceReturn valueIs extension function
letitLambda resultYes
runthisLambda resultYes
run-Lambda resultNo: called without the context object
withthisLambda resultNo: takes the context object as an argument.
applythisContext objectYes
alsoitContext objectYes

使用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)

返回值

  • apply 和 also 返回context object(主要适用于进行对象的链式处理)
  • letrun 和 with 返回的是 lambda的执行结果

run

// this, Lambda result

@kotlin.internal.InlineOnly
public inline fun <T, R> 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 <R> 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, R> 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> 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> 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 <T, R> 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)