kotlinMethod

method

函数声明

1
2
3
[public] fun methodName([param: ClassName [,param: ClassName]*]*)[: ReturnClassName]{
}
fun add(second : Int) : Int{}

函数使用

1
[var varName] [: ClassName] = methodName([param [,param]*])

中缀标记法(Infix notation)

1
2
3
4
使用条件
是成员函数, 或者是扩展函数
只有单个参数
使用 infix 关键字标记

Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
fun main(args: Array<String>) {
var str = "1" add "2" sub 1
//输出2
println(str)
println("1".add("3"))
}
infix fun String.add(append : String): String{
return this.plus(append)
}
infix fun String.sub(index : Int): String{
return this.subSequence(index,this.length).toString()
}

默认参数

1
2
3
4
5
6
7
8
9
10
11
class DefaultParam{
fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size()) {}
}
// 6 = 0b110
//DefaultParam.read$default(new DefaultParam(), b, 0, 0, 6, (Object)null);
//以下是伪代码
DefaultParam param = DefaultParam();
param.read(b[]);
// 以read函数为例,有多个默认参数时可以通过参数名指定某个参数值
param.read(b[],off = 1)
param.read(b[],len = 3)

实际java代码

1
2
3
4
5
6
7
8
9
10
11
12
13
public final void read(@NotNull Byte[] b, int off, int len) {}
// $FF: synthetic method
// $FF: bridge method
public static void read$default(DefaultParam var0, Byte[] var1, int var2, int var3, int var4, Object var5) {
if((var4 & 2) != 0) {
var2 = 0;
}
if((var4 & 4) != 0) {
var3 = ((Object[])var1).length;
}
var0.read(var1, var2, var3);
}

思考

1
默认参数是通过添加static函数,实际调用static再桥接实际函数来实现,具体分两个static和非static

static

1
2
3
4
5
6
7
fun methodName(param1,param2..){}
生成,会在参数后面加入2个参数,一个flag,一个obj,obj一般为null,如果有遇到可以联系我
public final void methodNameread$default(param1,param2,..,int flag,Object obj)
以fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size())为例
调用read(b[]) --> read$default(b[],0,0,6,null),6 = 0b110,表示第一个参数有值,其他无值
通过flat可以设置指定位置的值,比如read(b[],off = 1) -->read$default(b[],0,0,4,null)

b[] off len
0 1 1
1
2
3
4
5
6
7
8
9
public static void read$default( Byte[] var1, int var2, int var3, int var4, Object var5) {
if((var4 & 2) != 0) {//-->var4 & 0b10
var2 = 0;
}
if((var4 & 4) != 0) {//-->var4 & 0b100
var3 = ((Object[])var1).length;
}
read(var1, var2, var3);
}

非static

1
2
3
4
5
6
和static几乎一样,差别在第一个参数变成对象,具体看demo
class DefaultParam{
fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size()) {}
}
DefaultParam().read() -->实际调用如下
public static void read$default(DefaultParam var0, Byte[] var1, int var2, int var3, int var4, Object var5)

单表达式函数(Single-Expression function)

1
fun double(x: Int) = x * 2

局部函数

1
2
3
4
5
6
7
8
9
fun move(x: Int) {
fun move(x: Int, y: Int) {
println("$x : $y")
}
//建议参数都通过函数传递
move(x, 10)
}
//实际会生成一个extends kotlin.jvm.internal.extends kotlin.jvm.internal.Lambda implements kotlin.jvm.functions.Function2 implements kotlin.jvm.functions.Function2的class,我叫他$1并带有一个INSTANCE对象
//那么可以理解成,实际是使用了object :Lambda Function2

返回Unit

1
2
3
4
5
6
//Unit 约等于 Void
fun returnNull() :Unit {}
fun returnNull() :Unit {
return Unit
}

可变参数vararg

1
2
3
4
5
6
7
8
//vararg后面不能带参数,否则定的函数无法使用
//和java可变参数一致
fun <T> asList(vararg ts: T): List<T> {
val result = ArrayList<T>()
for (t in ts) // ts 是一个 Array
result.add(t)
return result
}

尾递归函数(Tail recursive function)–>不存在栈溢出(stack overflow)的风险

1
2
3
4
//使用 tailrec 修饰符必须在最后一个操作中调用自己。在递归调用代码后面是不允许有其它代码的,并且也不可以在 try/catch/finall 块中进行使用。当前的尾递归只在 JVM 的后端中可以用
//意味着很多操作都用不了,使用场景不多
tailrec fun findFixPoint(x: Double = 1.0): Double
= if (x == Math.cos(x)) x else findFixPoint(Math.cos(x))

Lambda 表达式

1
2
3
4
5
6
7
8
9
10
11
12
//不带返回值的
var list = arrayListOf<Int>(1,2,3)
//单个参数默认名字是it
list.forEach { it -> println(it) }
//实际是生成一个Function对象,调用invote函数,使用inline的话不会生成对象,底层代码做了优化
//带返回值的
fun Int.highAction(name:(Int) -> Int) {
println(name(this))
}
//这里注意,多个操作时,return 之前有其他操作需要加 ; ,return@函数名称
//如果是单个操作,如1.highAction { 1 } 就不需要return
1.highAction { println(it); return@highAction 1 }

高阶函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//body 名称可以自己定,格式为name:(params) -> ReturnType
// 不需要返回则用Uint
//body只有一个参数时默认用it
fun <T> lock(lock: Lock?, body: () -> T): T {
lock?.lock()
try {
return body()
}
finally {
lock?.unlock()
}
}
// 调用
fun main(args: Array<String>) {
//调用lambda.实际是生成一个Function对象,调用invote函数
lock(null,{ println("action in Lock")})
// ::staticMethodName,直接将函数名传入调用函数,原理同上
lock(null, ::actionInLock)
lock(null, fun(){
println("action in Anonymous ")
})
}
fun actionInLock(){
println("action in Method")
}
fun Int.highAction(name:(Int) -> Unit){
name(this);
}
1
思考,有这个函数可以做很多事情,比如资源的释放,还有时间统计等等,都可以在这里轻松解决,而不需要添加太多代码

匿名函数(Anonymous Function)

1
2
3
4
5
6
7
8
9
10
11
12
13
//配合高级函数使用
fun(x: Int, y: Int): Int = x + y
```
### 闭包
```kotlin
//实际都是生成了对象,然后使用实际对象操作
var ints = arrayOf(1,2,3)
var sum = 0
ints.filter { it > 0 }.forEach {
sum += it
}
print(sum)

带有接受者的函数字面值

1
2
3
// 通过生成Function class,调用invoke
val sum = fun Int.(other: Int): Int = this + other
1.sum(2);

内联函数 inline

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//内联函数可以讲函数内代码提到执行地方,从而避免产生太多的函数,顺带提升运行效率,但是内联使用有限制条件
//函数内联也许会导致编译产生的代码尺寸变大, 但如果我们使用合理(不要内联太大的函数), 可以换来性能的提高,
//以inline fun <T> lock(lock: Lock?, body: () -> T): T {}为例,加了inline之后反编译的代码中就没有lock这个函数,而是将lock的整个代码放到调用地方
//如果 inline函数调用包含inline则外层inline失效,举个栗子
inline fun inlineAction(){
lock(null,{ println("action in inlineAction")})
}
inline fun <T> lock(lock: Lock?, body: () -> T): T {//伪代码}
//这时候调用inlineAction(),这个方法并不多消失
//在局部函数中,非内联函数为外层函数return,而内联可以
fun foo() {
ordinaryFunction {
return // 错误: 这里不允许让 `foo` 函数返回
}
}
fun foo() {
inlineFunction {
return // OK: 这里的 Lambda 表达式是内联的
}
}

具体化的类型参数

1
2
3
4
5
6
//reified 必须配合inline使用
//不知道干啥用的,目前就看到reified之后可以使用::class,::class.java,如果不加,没法使用
//::class可以获取T的真实类型,
inline fun <reified T> getRealType( t : T) : Unit{
println(T::class)
}

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器