定义
fun 函数名(形参列表)[:返回值类型]{
函数体
}
如果没有返回值
kotlin.Unit
fun min(a: Int, b: Int) {
}
fun min2(a: Int, b: Int): Unit {
}
fun min3(a: Int, b: Int): Unit {
return Unit
}
fun main() {
/**
* 输出
* 1
* kotlin.Unit
*/
var value = run(1)
var value1 = run1(1)
var value2 = run2(1)
println(value)
println(value1)
println(value2)
}
fun run(a: Int) {
println(a)
}
fun run1(a: Int): Unit {
println(a)
}
fun run2(a: Int): Unit {
println(a)
return Unit
}
Unit
源码,从源码可以看出Unit
是一个真正的类
public object Unit {
override fun toString() = "kotlin.Unit"
}
在Kotlin中由于有Unit类型,对于不想实现的接口方法,可以更简洁
interface Base {
fun test(): Unit
}
class Child : Base {
override fun test() = Unit
}
函数定义实例,return语句可以返回常量、变量或者表达式
/**
* 定义一个函数,有两个形参,返回Int
*/
fun max(x: Int, y: Int): Int {
val result = if (x > y) x else y
return result
}
fun max2(x: Int, y: Int): Int {
return if (x > y) x else y
}
单表达式函数:函数只有一条表达式,可以省略括号,使用=代替。对于单表达式函数,编译器可以推断出返回值(即可以省略返回值类型)
fun area0(x: Double, y: Double): Double {
return x * y
}
以下两种方式等价上面
fun area1(x: Double, y: Double): Double = x * y
fun area2(x: Double, y: Double) = x * y
在java中没有顶级函数和顶级变量,java的所有变量和方法都必须被封装在类中。kotlin中函数必须有fun关键字,因为Kotlin并不是一个完全的面向对象的编程语言,并不是所有的元素都必须被封装在类型中,如果没有fun,编译器无法确定所声明的是不是函数。
命名参数:Kotlin允许调用函数时通过名字来传入参数值
fun max(x: Int, y: Int): Int {
val result = if (x > y) x else y
return result
}
fun main(args: Array<String>) {
//传统调用函数的方式,根据参数位置传入参数值
val max = max(5, 6)
//根据参数名传递参数,并且不需要按照函数原有形参的顺序
val max2 = max(x = 5, y = 6)
val max3 = max(y = 6, x = 5)
//部分使用命名参数, 在命名参数后面的只能是命名参数,不能这样写max(x=5,6)
val max4 = max(5, y = 6)
println(max)
println(max2)
println(max3)
}
形参默认值:Kotlin中,在定义函数时可以为一个或多个形参指定默认值。Kotlin建议将带默认值的参数放在函数形参列表的后面
fun sayHello(name: String = "jannal", message: String = "欢迎") {
println("hello ${name},${message}")
}
fun main(args: Array<String>) {
//全部使用默认形参,输出:hello jannal,欢迎
sayHello();
//message使用默认值,输出:hello jack,欢迎
sayHello("jack");
//都不使用默认值,输出:hello jack,welcome
sayHello("jack", "welcome");
//指定参数不使用默认值,没指定的使用默认值,输出:hello jannal,welcome
sayHello(message = "welcome")
}
尾递归函数:当函数将调用自身作为它执行的最后一行代码,且递归调用后没有更多代码时,可使用尾递归语法。尾递归不能在异常处理的try、catch、finally块中使用,尾递归函数需要使用tailrec修饰。与普通递归相比,编译器会对尾递归进行修改,将其优化成一个快速而高效的基于循环的版本,这样就可以减少可能对内存的消耗
fun fact(n: Int): Int {
if (n == 1) {
return 1
} else {
return n * fact(n - 1)
}
}
tailrec fun factRec(n: Int, total: Int = 1): Int = if (n == 1) total else factRec(n - 1, total * n)
可变形参: kotlin 允许个数可变的形参可以处于形参列表的任意位置,但是kotlin要求一个函数最多只带一个个数可变的形参。
"*"
运算符fun testVarArgs(a: Int, vararg books: String) {
for (book in books) {
println(book)
}
println(a)
}
fun main(args: Array<String>) {
testVarArgs(3, "java", "C", "C++")
//对于已有数组使用* 运算符
var arr = arrayOf("java", "C", "C++")
testVarArgs(3, *arr)
}
重载:与java一样,Kotlin的函数重载也只能通过形参列表进行区分,即形参个数不同、类型不同的都可以算函数重载。如果重载的函数中包含了可变参数,Kotlin会尽量执行最精确的匹配,大部分时候不建议重载形参个数可变的函数,容易出错。
fun testOverride(msg: String) {
println("一个参数")
}
fun testOverride(vararg msg: String) {
println("可变参数")
}
fun main(args: Array<String>) {
//输出:可变参数
testOverride()
//输出:一个参数
testOverride("a")
//输出:可变参数
testOverride("a", "b")
}
局部函数: 在函数体内部定义的函数称为局部函数。默认情况下,局部函数对外部是隐藏的,局部函数只能在其封闭函数内有效,其封闭函数可以返回局部函数,以便在其他作用域中使用局部函数
/**
* 局部函数:在函数体内定义函数
*/
fun operator(func: String, x: Int, y: Int): Int {
fun add(x: Int, y: Int): Int {
return x + y
}
fun sub(x: Int, y: Int) = x - y
fun multi(x: Int, y: Int) = x * y
var result: Int = 0
when (func) {
//调用局部函数
"add" -> {
result = add(x, y)
}
"sub" -> {
result = sub(x, y)
}
"multi" -> {
result = multi(x, y)
}
}
return result;
}
fun main(args: Array<String>) {
println(operator("add", 1, 2))
println(operator("sub", 1, 2))
println(operator("multi", 1, 2))
}
高阶函数: Kotlin中函数本身有自己的类型。函数类型与数据类型一样,即可用于定义变量,也可用于作函数的形参类型,还可以作为函数的返回值类型
函数类型的定义: 【函数的形参列表 -> 返回值类型组成】
//定义函数
fun add(x: Int, y: Int): Int {
return x + y
}
//定义一个变量,其类型为(Int,Int)->Int
var myfunc: (Int, Int) -> Int
//将函数add赋值给myfunc,则myfunc可以当做add使用
myfunc = ::add
/**
* 只要被赋值的函数类型与myfunc的变量类型一致,就可以被赋值成功
* 通过使用函数类型的变量,可以让myfunc在不同的时间指向不同的函数,从而使程序更加灵活
*/
myfunc = ::sub
println(myfunc(2, 3))
如果要在函数中传入函数,需要在函数中定义函数类型的形参。Kotlin支持像使用其他类型一样使用函数类型
/**
* 在函数参数上使用函数类型
*/
fun add(x:Int,y: Int,fn:(Int,Int)->Unit):Int{
fn(x,y);
return x + y
}
fun myprint(x:Int,y:Int){
println("x:${x},y:${y}")
}
//x:2,y:3
add(2,3,::myprint)
使用函数类型作为返回值
fun myprint(x:Int,y:Int){
println("x:${x},y:${y}")
}
//使用函数类型作为返回值
fun add(x:Int,y:Int,z:Int):(Int,Int)->Unit{
val result = x+y+z;
return ::myprint
}
var printFunc = add(2,3,4)
//x:2,y:3
printFunc(2,3)
函数类型声明的箭头表达式有时过于冗长,显得不够简洁,此时可以通过Kotlin类型别名(typealias )来简化
val sourceFunc = fun(a: (String) -> Int, b: (Int) -> Boolean): (String) -> Boolean {
return { b(a(it)) }
}
typealias A = (String) -> Int
typealias B = (Int) -> Boolean
typealias C = (String) -> Boolean
//简化后的,有点像数学公式
val simpleFunc = fun(a: A, b: B): C {
return { b(a(it)) }
}
使用Lambda表达式代替局部函数
fun operator2(func: String): (Int, Int) -> Int {
when (func) {
//使用lambda代替局部函数,返回lambda表达式
"add" -> return { m: Int, n: Int ->
m + n
}
"sub" -> return { m: Int, n: Int ->
m - n
}
"multi" -> return { m: Int, n: Int ->
m * n
}
else -> return { m: Int, n: Int ->
m / n
}
}
}
lambda表达式与局部函数的区别
Lambda表达式总是被大括号括着
定义lambda表达式不需要fun关键字,无需指定函数名
形参列表(如果有的话)在->之前声明,参数类型可以省略
函数体(lambda表达式执行体)放在->之后
函数的最后一个表达式自动被作为lamdba表达式的返回值,无需使用return
lambda的语法
{(形参列表) ->
//执行语句
}
lambda表达式的本质是功能更灵活的代码块,即完全可以将lambda表达式赋值给变量或直接调用Lambda表达式。
//省略形参名,使用it代表形参
var square: (Int) -> Int = { it * it }
//16
println(square(4))
//多个形参无法省略
var result = { x: Int, y: Int ->
var result = x + y
result
}(4, 3)
println(result)
如果函数的最后一个参数是函数类型,而且如果要传入一个Lambda表达式作为相应的参数,那么就允许在圆括号之外指定Lambda表达式(尾随闭包 Tail Closure)。如果Lambda表达式是函数调用的唯一参数,则调用方法时的圆括号完全可以省略,通常建议将函数类型的形参放在形参列表的最后,这样就可以方便传入lambda表示作为参数。
var list = listOf("java", "Kotlin", "GO")
var dropWhile = list.dropWhile() { it.length > 3 }
//[GO]
println(dropWhile)
dropWhile = list.dropWhile { it.length > 3 }
//[GO]
println(dropWhile)
如果一个函数既包含个数可变的形参,也包含函数类型的形参,那么就应该将函数类型的形参放在最后
//定义匿名函数,并赋值给变量
var test = fun(x: Int, y: Int): Int {
return x + y
}
println(test(2, 4))
如果系统可以推断出匿名函数的形参类型,那么匿名函数允许省略形参类型
//使用匿名函数作为filter的参数
var filterList = listOf(3, 4, 5, 6, 30, 40, 50).filter(
fun(el): Boolean {
return el > 10
}
)
println(filterList)
匿名函数使用单表达式作为函数体,无须指定返回值类型
//定义匿名函数,函数体是单表达式,所以可以省略函数的返回值类型
var sub = fun(x: Int, y: Int) = x + y
匿名函数的本质是函数,即匿名函数的return返回该函数本身,而Lambda表达式的return用于返回它所在的函数,而不是lambda表达式
//匿名函数返回该函数本身,所以对外层函数a()没有影响
fun a() {
var list = listOf(2, 3, 4, 5)
list.forEach(fun(n) {
print("${n}")
return
})
}
//2345
a()
//lambda表达式返回它所在的函数,对b()有影响
fun b() {
var list = listOf(2, 3, 4, 5)
list.forEach({ n ->
print("${n}")
return
})
}
//2
b()
fun c() {
var list = listOf(2, 3, 4, 5)
list.forEach({ n ->
print("${n}")
//使用限定返回,此时return返回lambda表达式
return@forEach
})
}
//2345
c()
Lambda表达式或匿名函数(以及局部函数、对象表达式)可以访问或修改其所在上下文(闭包)中的变量或常量,这个过程被称为捕获。即使定义这些变量和常量的作用域已经不存在了,Lambda表达式或匿名函数也依然可以访问或者修改他们
fun makeList(ele: String): () -> List<String> {
var list: MutableList<String> = mutableListOf()
/**
* 匿名函数会持有一个其所捕获的变量的副本
* 1. 从表面上看 addElement访问的是makeList函数的list集合变量
* 但是程序返回一个新的addElement函数,addElement函数就会自己持有一个新的
* list的副本。
*/
fun addElement(): List<String> {
list.add(ele)
return list
}
return ::addElement
}
var add1 = makeList("jannal")
//[jannal]
println(add1())
//[jannal, jannal]
println(add1())
var add2 = makeList("jack")
//[jack]
println(add2())
//[jack, jack]
println(add2())
调用lambda表达式或函数的过程是:程序要将执行顺序转移到被调用表达式或函数所在的内存地址,当被调用表达式或函数执行完后,再返回到原函数执行的地方。在这个转移过程中,系统要处理如下的事情
函数调用会产生一定的时间和空间开销,如果被调用的表达式或函数的代码量本身不大,而且该表达式或函数经常被调用,那么这个时间和空间的开销就很大
为了避免产生函数调用的过程,可以考虑直接把被调用的表达式或函数的代码嵌入到原来的执行流中。即编译器去负责复制和粘贴,此时相当于没有调用任何函数。
inline关键字修饰的函数就是内联函数,noinline用于修改函数中某一个或者几个函数类型的形参不被内联化。
如果被调用的Lambda表达式或函数的代码两非常大,且该Lambda表达式或函数多次被调用,每一次调用,该Lambda表达式或函数的代码就会被复制一次,因此会带来程序代码的急剧增加。即如果被调用的Lambda表达式或函数只包含非常简单的代码(比如单表达式),那么应该使用内联函数,否则不应该不使用。
inline fun map(data: Array<Int>, fn: (Int) -> Int): Array<Int> {
var result = Array<Int>(data.size, { 0 })
for (i in data.indices) {
result[i] = fn(data[i])
}
return result
}
fun main(args: Array<String>) {
var arr = arrayOf(10, 20, 30, 40)
var result = map(arr, { it + 3 })
println(result.contentToString())
}
编译后只会产生一个FunctionInlineKt.class,不会生成其他额度的内部类class文件
map函数被编译后实际变成了
fun map(data: Array<Int>, fn: (Int) -> Int): Array<Int> {
var result = Array<Int>(data.size, { 0 })
for (i in data.indices) {
result[i] = data[i]+3
}
return result
}
在默认情况下,在Lambda表达式中并不允许直接使用return。因为如果是非内联的Lambda表达式,该Lambda表达式会额外生成一个函数对象,因此这种表达式中的return不可能用于返回它所在的函数。由于内联的Lambda表达式会被直接复制、粘贴到调用它的函数中,故此时在该Lambda表达式中可以使用return,该return就像直接写在Lambda表达式的调用函数中一样。因此该内联的Lambda表达式中的return可用于返回它所在的函数,这种返回被称为非局部返回。
inline fun eachPrint(data: Array<Int>, fn: (Int) -> Unit) {
for (el in data) {
fn(el)
}
}
fun main(args: Array<String>) {
var arr = arrayOf(10, 20, 30, 40)
eachPrint(arr, {
print(it)
/**
*1. 如果eachPrint没有inline修饰,此处会编译错误,即非内联的lambda表达式无法使用return
*2. 有inline修饰,则返回的是main函数
*/
return
})
}
kotlin-stdlib-common-1.3.31.jar
中的kotlin/StandardKt
定义:传入代码块,然后返回代码块结果
@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
案例
fun main() {
//输出:打印结果
queryData()
//输出:打印结果
run({ queryData() })
//输出:打印结果
run { queryData() }
}
fun queryData(): String {
println("打印结果")
return "结果"
}
定义: 执行代码块,返回调用的对象
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
案例
fun main() {
val list = mutableListOf("a","b")
list.apply {
add("c")
add("d")
}
//[a, b, c, d]
println(list)
}
定义:将当前调用对象作为参数传入代码块,并返回代码块结果
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
案例
fun main() {
//输出:2
2.let { println(it) }
//输出:A
"A".let { println(it) }
/**
* 输出
* - 打印结果
* - 结果
*/
queryData().let { println(it) }
}
fun queryData(): String {
println("打印结果")
return "结果"
}
定义:将当前调用对象作为参数传入代码块,并返回当前对象
@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
}
案例
//A
//A
var a = "A".also { println(it) }
println(a)
定义:传入一个接收者对象,使用该对象去调用代码块
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return receiver.block()
}
案例
val list1 = mutableListOf("a", "b")
with(list1) {
add("c")
//输出[a, b, c]
println("$this")
}.let {
//输出:kotlin.Unit
println(it)
}