Kotlin
函数在设计上与Java
相比有非常多的改动,并有独特性,尤其是函数可以当参数的形式传递,提供了更大的发挥空间,并且巧妙的运用Lambda
表达式
函数的定义
普通函数
/**
* 定义函数
*/
fun function(){
println("Function 函数~")
}
@Test
fun main() {
//调用
function()
}
形参&返回值
/**
* 定义函数
*/
fun function(code: Int, message: String): Boolean {
println("Function 函数~ 接受到参数 code:${code} message:${message}")
return true
}
@Test
fun main() {
//调用
val isCall = function(200,"ok")
}
带默认值
可以看出带默认值的解决JAVA
中的多态的定义,以及一些参数的常量的初始值
/**
* 定义函数
*/
fun function(code: Int = 100, message: String = "未知"): Boolean {
println("Function 函数~ 接受到参数 code:${code} message:${message}")
return true
}
@Test
fun main() {
//调用
val isCall1 = function()
val isCall2 = function(200)
val isCall3 = function(200, "ok")
}
具名参数
调用时可以有选择性的参数传值 参数昵称 = 参数值
/**
* 定义函数
*/
fun function(code: Int = 100, message: String = "未知"): Boolean {
println("Function 函数~ 接受到参数 code:${code} message:${message}")
return true
}
@Test
fun main() {
//调用
val isCall = function(message = "ok")
}
可变参数
怎么理解可变参数,在JAVA
中有funcation(String... args)
可以接受N个String
参数,而在Kotlin
也可以这么定义,是不过是使用关键字vararg
来定义
/**
* 定义函数
*/
fun function(vararg rarg: String): Unit {
println("Function 函数~ 接受到参数 rarg 长度:${rarg.size} ")
}
@Test
fun main() {
//调用
val isCall = function("1", "2", "3")
}
通过 show Java 查看
public final void function(@NotNull String... rarg) {
Intrinsics.checkNotNullParameter(rarg, "rarg");
String var2 = "Function 函数~ 接受到参数 rarg 长度:" + rarg.length + " ";
boolean var3 = false;
System.out.println(var2);
}
函数嵌套
在Kotlin
中可以函数嵌套定义函数,这是一个非常重要的特性,在JAVA
中不可能实现的
/**
* 定义函数
*/
fun function(message: String) {
println("function")
fun test(code: Int) {
println("test code:${code}")
}
println(message)
test(200)
}
@Test
fun main() {
//调用
val isCall = function("一个参数Mesage")
}
打印:
function
一个参数Mesage
test code:200
先看看编译器做了什么?
public final void function(@NotNull String message) {
Intrinsics.checkNotNullParameter(message, "message");
String var2 = "function";
boolean var3 = false;
System.out.println(var2);
<undefinedtype> $fun$test$1 = null.INSTANCE; //函数为静态对象
var3 = false;
System.out.println(message);
$fun$test$1.invoke(200); //通过invoke函数调用
}
函数内部函数的定义:
<undefinedtype> $fun$test$1 = null.INSTANCE;
函数内部的调用:
$fun$test$1.invoke(200);
可以看出编译器会将函数内部函数,编译生成一个实现内,FunctionN
类,并且有一个重写incoke
方法,创建一个静态对象,然后invoke
方法就是嵌套函数的实现,在Lambda表达式
、函数编程
、函数当参数传递
、DSL
都与函数嵌套有关。
public interface Function<out R>
out R
:表示泛型
可以看到 ***$1 代表Function1
继承interface Function<out R> Function
接口,在Kotlin
一个有Functions.kt
都是定义23个新接口,从0~22
package kotlin.jvm.functions
/** A function that takes 0 arguments. */
public interface Function0<out R> : Function<R> {
/** Invokes the function. */
public operator fun invoke(): R
}
/** A function that takes 1 argument. */
public interface Function1<in P1, out R> : Function<R> {
/** Invokes the function with the specified argument. */
public operator fun invoke(p1: P1): R
}
/** A function that takes 2 arguments. */
public interface Function2<in P1, in P2, out R> : Function<R> {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2): R
}
//.....省略
/** 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
}
可以看到 out R
, 最后一个参数进行返回值的类型,如果没有用Unit
代替
函数当参数传递
假设一个函数当参数传递应该怎么办?首先就应该想到C、C++
中的指针(指正函数),把函数当成指针,获取到指针所存在的位置,把这个指针当成变量传入其他函数的变量即可
Kotlin
可以使用::
关键词取出fun
的函数指针
/**
* 定义一个函数
* 这里用类一个缩写,只要函数只有执行一句代码,就可以这样写
*/
fun funPrint(msg: String) = println(msg)
/**
* 定义函数
* funParameter: Function1<String, Unit>【具体的函数】 为该函数的参数
*/
fun functionTest(message: String, funParameter: Function1<String, Unit>) {
funParameter(message) //通过 Function1<String, Unit> 传参默认调用,也会默认调用 invoke
funParameter.invoke(message) //通过invoke调用
}
@Test
fun main() {
//通过 ::+函数名 获取地址, 这是没有传参
val funAddress = ::funPrint
functionTest("this Message", funAddress)
}
看看编译后的代码:
public final void funPrint(@NotNull String msg) {
Intrinsics.checkNotNullParameter(msg, "msg");
boolean var2 = false;
System.out.println(msg);
}
public final void functionTest(@NotNull String message, @NotNull Function1 funParameter) {
Intrinsics.checkNotNullParameter(message, "message");
Intrinsics.checkNotNullParameter(funParameter, "funParameter");
funParameter.invoke(message);
funParameter.invoke(message);
}
@Test
public final void main() {
KFunction funAddress = new Function1((ExampleUnitTest)this) {
// $FF: synthetic method
// $FF: bridge method
public Object invoke(Object var1) {
this.invoke((String)var1);
return Unit.INSTANCE;
}
public final void invoke(@NotNull String p1) {
Intrinsics.checkNotNullParameter(p1, "p1");
((ExampleUnitTest)this.receiver).funPrint(p1);
}
};
this.functionTest("this Message", (Function1)funAddress);
}
- 如果当参数其实都是定义
FunctionN
接口 - 具体函数用
invoke(vararg...)
-
::funPrint函数名
通过接口FuncationN
实现,获取实体对象,在invoke
函数调用函数指针
Unit
可以理解Unit
与 Java中的void
类似,当一个函数没有返回值的时候,可以用Unit
代替,也可以省略,编译器会隐式的加上去