这篇文章为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
如果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
. 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 var
s).
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)
常用的属性代理有lazy
和observable
// 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("") {
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而无需知道它的名字
具体的作用域函数主要有:let
,run
,with
,apply
和 also
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)
run
,with
和 apply
使用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)
let
,also
使用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(主要适用于进行对象的链式处理)let
,run
和with
返回的是 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)