kotlin 基础语法-02-函数-高阶函数
本篇文章 主要介绍kotlin 中的函数、嵌套函数、扩展函数、 lambda 语法 、 高阶函数、内联函数 体会函数式编程的能力。
1.函数
我们已经很熟悉函数的声明了、
基本函数的声明语法如下:
fun funName(arg1:String="默认值"):返回值类型{
/// 函数体
}
其中参数后边的默认值是可以省略的、 如果返回值为空 ,在java中void 、 在kotlin 可以使用Unit来表示, 当然也是可以省略的、 默认返回值为空 。
还有一个特性 就是如果函数体中只有一个语句的话、 那么可以这样来表示
fun sum(num1: Int , num2: Int ) = print("$num1 + $num2= " +( num1+ num2))
这里不再赘述, 我们继续。
2.函数嵌套
个人表示不建议、也不喜欢这样的写法、 在java中类似于内部类。
来一个demo 看一下
fun demo(words:String){
val str = "hello kotlin"
fun sayHello(i :Int = 10 ){
println(str )
if(i > 0){
sayHello(i-1)
}
}
sayHello()
}
打印结果就是:
hello 10
hello 9
hello 8
hello 7
hello 6
hello 5
hello 4
hello 3
hello 2
hello 1
hello 0
个人觉得这种坑逼的写法, 由于比较隐蔽、 内部函数可以访问外部函数变量、 而且外部的其他函数是无法调用改函数, 所以主要用途在于,某些条件下触发递归的函数、或者说根本就不希望外部函数访问到的函数。
当然个人写法,我是不会这样写的, 更不推荐这么写, 搞得跟中介似的。 好了, 我们继续。
3. 扩展函数的静态解析
扩展函数主要用于第三方sdk 或者一些自己无法控制的类的、这时候如果想给这个类添加一些方法或者成员变量。 这时候就可以使用扩展函数 。
来个例子:
fun printFile(fileName:String){
var file = File(fileName)
println(file.readText()
}
这里用的是系统内置的扩展函数、我们看一下 FileReadWrite.kt
/**
* Gets the entire content of this file as a String using UTF-8 or specified [charset].
*
* This method is not recommended on huge files. It has an internal limitation of 2 GB file size.
*
* @param charset character set to use.
* @return the entire content of this file as a String.
*/
public fun File.readText(charset: Charset = Charsets.UTF_8): String = readBytes().toString(charset)
有时候不晓得是怎么回事、 看一下源码就立刻明白了、 是不是很清楚。
大家要注意、Kotlin的扩展函数、是静态的给扩展类添加扩展函数、 也就是说这样的扩展、不具备继承性.
那么Java中如何使用呢?
来个栗子,
public static void main(String args[]){
File file = new File("xxx.txt")
String content = FileKt.readText(file,Charsets.UTF_8)
sysout(content)
}
我们继续,
4.lambda 闭包
Runnable thread = new Runnable(){
public void run(){
Utils.sayMsg("hello test");
}
};
new Thread(thread).start();
java 8 的lambda语法
new Thread(
() -> {Utils.sayMsg("hello test");}
).start();
我们看一下kotlin 的lambda语法
val thread = Thread(Runnable { println("hello world , kotlin !")})
thread.start()
我们再来看几种比较灵活坑逼的语法结构:
默认格式:
fun main(args:Array<String>){
val thread = Thread({-> Unit})
thread.start()
}
如果lambda是没有参数的,可以省略箭头符号:
run 函数没有参数 , 如下
fun main(args:Array<String>){
val thread = Thread({})
thread.start()
}
如果lambda是函数的最后一个参数, 可以将大括号放在小括号的外面, 如下:
fun main(args:Array<String>){
val thread = Thread(){}
thread.start()
}
如果函数只有一个参数并且这个参数是lambda,则可以省略小括号。如下:
fun main(args:Array<String>){
val thread = Thread{ }
thread.start()
}
lambda闭包声明:
val echo = {
name:String, age : Int ->
println("$name , $age")
}
fun main(args:Array<String>){
/// 两种调用方式, 均可以
// echo("samuel", 28)
// echo.invoke("jack",27)
}
5. lambda 的参数上限
如果我们不做特殊处理、我们声明的lambda闭包会直接默认编译成匿名内部对象:
Function1<String,Unit> echo = (Function1) echo.INSTANCE;
当然参数个数的上限是22个,如果超出,就会抛异常。
找到kotlin/jvm/functions/ package目录下的Functions类, 找到最多参数的Function22
/** A function that takes 22 arguments. */
public interface Function22<in P1, in P2, in P3, in P4, in P5, in P6, in P7, in P8, in P9, in P10, in P11, in P12, in P13, in P14, in P15, in P16, in P17, in P18, in P19, in P20, in P21, in P22, out R> : Function<R> {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16, p17: P17, p18: P18, p19: P19, p20: P20, p21: P21, p22: P22): R
}
如果想要传入更多的参数的话,需要我们手动的添加接口。 当然声明的类不能声明和系统包名相同的目录下。由于java和kotlin是可以相互引用的,用java声明也是可以的。
6.高阶函数
高阶的意思就是函数的参数是函数的函数 , 是不是很绕。 来个例子:
fun main(args:Arrays<String>){
///这是第一种写法、
onlyif(true,{
println("hello , world! only if ")
})
//// 第二种写法、 当lambda 闭包 作为函数的最后一个参数传入的时候、是允许写在小括号之外的。
onlyif(true){
println("hello ,kotlin , only if ")
}
}
////第一个参数是boolean 决定block 函数是否被调用,
/// 第二个参数是一个方法块, 返回值是Unit , 这里如果函数作为方法的参数声明的话 是不能省略的, 不能像main函数一样省略Unit关键字
fun onlyif(debug:Boolean ,block:()->Unit){
if(debug) block()
}
函数作为函数的方法传递给函数时的格式为
function::run
如果直接用. 分割传递、 则传递的函数的执行结果。
val runable = Runnable{
println("balabala ")
}
/// 参数为空 , 返回值为Unit 的函数对象
val fuction : () -> Unit
fuction = runable::run
onlyif(true,fuction)
lambda 表达是会被编译成匿名内部类, 如果类中存在大量重复的lambda 表达式则会造成大量无用的匿名内部类。
inline关键字
这时候使用inline 关键字去修饰高阶函数 放在fun 之前, 这样在编译期,编译期就会拆解函数调用为语句调用。 进而减少创建不必要的对象。
inline fun onlyif(debug:Boolean ,block:()->Unit){
if(debug) block()
}
下边是使用inline 关键字编译后生成的代码
public static final void onlyif(boolean debug, Function0<Unit> block){
/// Function0 后边这个数字 代表的是 后边的函数有多少个参数
if(debug){
block.invoke();
}
}
我靠, 这不是静态函数吗、 明白inline关键字的作用了吧 。
过度使用inline 关键字 会增加编译器的负担、 增大代码问题排查难度。
通常我们只会把高阶函数修饰为inline。
7. 总结
多上手敲、多尝试、多思考、 有问题评论区我们共同讨论进步。