fun main() {
println("Hello World")
}
我们程序的入口点就是main
函数,我们只需要将我们的程序代码编写到主函数中就可以运行了,不过这个函数只是由我们来定义,而不是我们自己来调用。当然,除了主函数之外,我们一直在使用的println
也是一个函数,不过这个函数是标准库中已经实现好了的
fun 函数名称([函数参数...]): 返回值类型 {
//函数体
}
fun main(args: Array<String>) {
sayHello1()
sayHello2()
println(add(10,20))
}
fun sayHello1(){
println("Hello")
}
fun sayHello2(): Unit{
println("Hello")
}
fun add(num1: Int,num2: Int): Int{
return num1 + num2
}
对于上面的函数add
,num1
和num2
就是函数的参数,Kotlin 支持参数声明默认值
fun main(args: Array<String>) {
println(add(10,20)) // 30
println(add(10)) // 20 , 10传给了第一个参数
println(add()) // 11 , 两个都是用默认参数
println(add(num2 = 20)) // 21 , 将 20 指定传给第二个参数
}
fun add(num1: Int = 1,num2: Int = 10): Int{
return num1 + num2
}
可变长参数,使用vararg
关键字将参数标记为可变长参数
注意:可变长参数在函数的形参列表里面只能存在一个
fun main() {
test("zhangsan","lisi","wangwu")
}
fun test(vararg names: String){
for (name in names){
println(name)
}
}
可变长参数的本质是一个数组,但是不同于 Java,可变长参数不可以直接传入一个数组,而需要使用*
来传入
fun main() {
var names = arrayOf("zhangsan","lisi","wangwu")
test("xiaoming",*names,"zhaoliu") // 使用*传入数组,并且前后都可以续写
}
fun test(vararg names: String){
for (name in names){
println(name)
}
}
对于上面的函数sayHello
实际返回是Unit类型,这个类型表示空,类似于Java中的void,默认情况下可以省略
返回值简化,对于一些内容比较简单的函数,比如上面仅仅是计算两个参数的和,我们可以直接省略掉花括号
fun main(args: Array<String>) {
println(add(10,20)) // 30
println(sub(20,10)) // 10
}
fun add(num1: Int,num2: Int): Int = num1 + num2
fun sub(num1: Int,num2: Int) = num1 - num2 // 自动推断返回值类型
函数内部也可以定义函数
fun main() {
myFun1()
}
fun myFun1(){
val a = 1
fun myFun2(){
println(a)
}
myFun2()
}
函数内的函数作用域是受限的,我们只能在函数内部使用;内部函数可以访问外部函数中的变量。
我们不能同时编写多个同名函数,但是如果多个同名函数的参数不一致,是允许的。
注意:返回值类型不同不能作为重载条件!
fun add(num1: Int,num2: Int): Int{
return num1 + num2
}
fun add(num1: Int,num2: Int,num3: Int): Int{
return num1 + num2 + num3
}
在Kotlin中,可以使用tailrec
修饰符来标记尾递归函数。编译器会识别被tailrec
修饰的函数,并进行优化处理,将递归调用转换为循环,从而避免栈溢出问题。
tailrec fun test(n: Int, sum: Int = 0): Int {
if(n <= 0) return sum //到底时返回累加的结果
return test(n - 1, sum + n) //不断累加
}
Kotlin中的函数属于一等公民,它支持很多高级特性,甚至可以被存储在变量中,可以作为参数传递给其他高阶函数并从中返回,就想使用普通变量一样。 为了实现这一特性,Kotlin作为一种静态类型的编程语言,使用了一系列函数类型来表示函数,并提供了一套特殊的语言结构,例如lambda表达式。
所有函数类型都有一个括号,并在括号中填写参数类型列表和一个返回类型,比如:(A, B) -> C
表示一个函数类型,该类型表示接受类型A
和B
的两个参数并返回类型C
的值的函数。参数类型列表可为空的,比如() -> A
,注意,即使是Unit
返回类型也不能省略。
var add: (Int,Int) -> Int
此时add
为变量名,(Int,Int) -> Int
为变量类型
函数变量的赋值
// 使用 Lambda 表达式赋值
var add: (Int,Int) -> Int = { num1, num2 -> num1 + num2}
// 使用匿名函数赋值
var myFunc: (String) -> Unit = fun (str: String){
println(str)
}
赋值已存在的函数
fun test(str: String){
println("hello")
}
var myFunc: (String) -> Unit = ::test
fun main() {
test { str -> print(str) }
}
fun test(handle: (String) -> Unit){
handle("Hello")
}
如果一个方法中声明了多个函数类型的参数,或者是存在函数类型参数嵌套,那么可以使用类型别名来缩短名称
fun main() {
test { str -> print(str) }
}
typealias HandleFun = (String) -> Unit
fun test(handle: HandleFun){
handle("Hello")
}
一个Lambda表达式只需要直接在花括号中编写函数体即可{params -> returnValue}
除了上面的可以使用 Lambda 表达式给一个函数类型变量赋值以外,还可以使用 Lambda 表达式传参
fun main() {
test({msg -> println(msg)})
}
fun test(handle: (String) -> Unit){
handle("Hello")
}
如果函数的最后一个形式参数是一个函数类型,可以直接写在括号后面,就像下面这样
test(){msg -> println(msg)}
如果函数只有一个参数,那么可以省略小括号
test{msg -> println(msg)}
这种语法也被称为尾随lambda表达式,能省的东西都省了,不过只有在最后一个参数是函数类型的情况下才可以,如果不是最后一位,就没办法做到尾随了。
单个参数
var myFunc: (String) -> String = {
println("Hello, $it") // 如果只有一个参数,则使用it来获取
"Hello, $it" // 最后一行作为返回值
}
多个参数
var add :(Int,Int) -> Int = { x,y -> // 我们需要手动添加两个参数这里的形参名称,不然没法用他两
x + y // 最后一行作为返回值
}
Lambda中没有办法直接使用return
语句返回结果,而是默认使用最后一行的值作为返回值。
但是有的时候,我们需要在函数中提前返回,这就需要用到之前我们学习流程控制时用到的标签。
var myFunc: (Boolean) -> String = test@{ b ->
if (b){
return@test "True" // 使用标签进行返回
}
"False" // 最后一行作为返回值
}
如果是函数调用的尾随lambda表达式,默认的标签名字就是函数的名字
使用高阶函数会可能会影响运行时的性能:每个函数都是一个对象,而且函数内可以访问一些局部变量,但是这可能会在内存分配(用于函数对象和类)和虚拟调用时造成额外开销。
为了优化性能,开销可以通过内联Lambda表达式来消除。使用inline
关键字会影响函数本身和传递给它的lambdas,它能够让方法的调用在编译时,直接替换为方法的执行代码。
fun main() {
test()
}
inline fun test(){
println("Hello")
println("Hello")
println("Hello")
}
由于test函数是内联函数,在编译之后,会原封不动地把代码搬过去
fun main() {
println("Hello")
println("Hello")
println("Hello")
}
同样的,如果是一个高阶函数,效果那就更好了,例如如下函数
fun main() {
test { println(it) }
}
inline fun test(handle: (String) -> Unit){
println("test")
handle("Hello")
}
由于test函数是内联的高阶函数,在编译之后,不仅会原封不动地把代码搬过去,还会自动将传入的函数参数贴到调用的位置
fun main(){
println("test")
var it = "Hello"
println(it)
}