0%

kotlin学习日志2-语法学习


定义函数

入参类型写在变量的后面,返回参数类型写在最后,函数关键字fun。

语法格式fun 函数名(参数名1:参数类型1,参数名2:参数类型2....):返回类型

1
2
3
fun sum(a: Int, b: Int): Int {
return a + b
}

可以是表达式的函数体,可推断返回值类型

1
fun sum(a: Int, b: Int) = a + b

没有指定它的返回值(类似于java空返回void),返回 Unit

1
2
3
fun printSum(a: Int, b: Int): Unit {
println("sum of $a and $b is ${a + b}")
}

Unit的返回类型可以被缺省

1
2
3
fun printSum(a: Int, b: Int) {
println("sum of $a and $b is ${a + b}")
}

中缀表达式

  1. 使用infix修饰
  2. 只有一个参数
  3. 参数不能为变长且不能有默认值
1
2
3
4
5
infix fun Int.add(i:Int):Int = this + i
infix fun Int.加(i:Int):Int = this + i

println(1 add 2)
println(12)

默认参数

函数参数可以设置默认值,当参数被忽略时会使用默认值。比起java来,少了重载

1
2
3
fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size() ) {
...
}

调用默认

1
reformat(str)

调用非默认

1
reformat(str, true, true, false, '_')

使用命名参数:

1
2
3
4
5
6
reformat(str,
normalizeCase = true,
uppercaseFirstLetter = true,
divideByCamelHumps = false,
wordSeparator = '_'
)

调用部分非默认:

1
reformat(str, wordSeparator = '_')

变长函数

函数的参数(通常是最后一个参数)可以用 vararg修饰符进行标记一个变长入参,相当于传入数组:

1
2
3
4
5
6
fun asList<T>(vararg ts: T): List<T> {
val result = ArrayList<T>()
for (t in ts)
result.add(t)
return result
}

标记后,允许给函数传递可变长度的参数:

1
val list = asList(1, 2, 3)

如果变长入参不是最后一个参数,后面的参数需要通过命名参数语法进行传值。
当调用变长参数的函数时,如果需要传递一个 array 的内容给函数,我们就可以使用 * 前缀操作符:

1
2
val a = array(1, 2, 3)
val list = asList(-1, 0, *a, 4)

函数范围

Kotlin 中可以在文件顶级声明函数,不用像java一样创建一个类来持有函数,除了顶级函数,Kotlin 函数可以声明为局部的,作为成员函数或扩展函数。

一个函数包含另一函数

1
2
3
4
5
6
7
8
9
fun dfs(graph: Graph) {
fun dfs(current: Vertex, visited: Set<Vertex>) {
if (!visited.add(current)) return
for (v in current.neighbors)
dfs(v, visited)
}

dfs(graph.vertices[0], HashSet())
}

局部函数可以访问外部函数的局部变量,局部函数甚至可以返回到外部函数,通过return@标签名的方式

1
2
3
4
5
6
7
8
9
10
11
fun reachable(from: Vertex, to: Vertex): Boolean {
val visited = HashSet<Vertex>()
fun dfs(current: Vertex) {
if (current == to) return@reachable true
if (!visited.add(current)) return
for (v in current.neighbors)
dfs(v)
}
dfs(from)
return false
}

成员函数

定义在一个类里面

1
2
3
class Sample() {
fun foo() { print("Foo") }
}

成员函数的调用

1
Sample.foo()

泛型函数

1
2
3
fun sigletonArray<T>(item: T): Array<T> {
return Array<T>(1, {item})
}

高阶函数与 lambda 表达式

kotlin的高阶函数可以接受函数作为参数,并返回一个函数。接收一个 lock 对象和一个函数,运行函数并释放 lock

1
2
3
4
5
6
7
8
9
fun <T> lock(lock: Lock, body: () -> T ) : T {
lock.lock()
try {
return body()
}
finally {
lock.unlock()
}
}

body 这里代表函数参的引用,() -> T,是一个没有参数并返回 T 类型的函数

调用lock() 函数,我们可以传给它另一个函数做参数,两种调用方式,一种是通过::函数名,另一种是通过字面函数

1
2
fun toBeSynchroized() = sharedResource.operation()//需要传递的函数
val result = lock(lock, ::toBeSynchroized)

传递一个字面函数(通常是 lambda 表达式)

1
2
val result = lock(lock, {sharedResource.operation()})
lock (lock) {sharedResource.operation()} //最后一个参数是函数,可以省略括号

Lambda表达式:入参->函数主体

对比java的写法:

Java编写的接口

1
2
3
public interface OnClickListener {  
void onClick(View v);
}

调用时java写法

1
2
3
4
5
6
view.setOnClickListener(new OnClickListener() {  
@Override
public void onClick(View v) {
Toast.makeText(v.getContext(), "Click", Toast.LENGTH_SHORT).show();
}
});

kotlin写法,这里用到了对象表达式

1
2
3
4
5
view.setOnClickListener(object : OnClickListener {  
override fun onClick(v: View) {
toast("Click")
}
})

加入lambda写法,入参view->函数体toast(“Click”)

1
view.setOnClickListener({ view -> toast("Click")})

如果没有使用左边的参数,可以省去左边部分

1
view.setOnClickListener({ toast("Click") })

被执行的函数是当前函数的最后一个参数,可以直接放到括号外

1
view.setOnClickListener() { toast("Click") }

被执行的函数是当前函数的唯一参数,可以去掉当前函数的括号

1
view.setOnClickListener { toast("Click") }

内联函数

函数的调用过程:每个函数都是一个对象,在函数体内部访问的那些外层变量, 内存占用(函数对象和类都会占用内存)以及虚方法调用都会带来运行时的消耗,函数实际上将程序执行顺序转移到该函数所存放在内存中某个地址,将函数的程序内容执行完后,再返回到转去执行该函数前的地方。

为了解决使用高阶函数在运行时会带来一些不利,出现了内联函数。在程序编译时,编译器将程序中出现的内联函数的表达式整合到函数被调用的地方去,从而生成的代码内存地址是连续的,减少了压栈/出栈的时间。
内联关键字inline,在方法的最前面添加

1
2
3
4
5
inline fun lock<T>(lock: Lock, body: () -> T): T {
...
body()
...
}

如果一个内联函数的参数中有多个 Lambda 表达式, 而你只希望内联其中的一部分, 你可以对函数的一部分参数添加 noinline 标记:

1
2
3
inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) {
// ...
}

空白的 return 在 lambda 函数中是禁止的,因为 lambda 函数不可以造一个闭合函数返回,但如果 lambda 函数是内联传递的,是可以的

1
2
3
4
5
6
7
8
9
10
fun foo() {
ordinaryFunction {
return // 错误 不可以在这返回
}
}
fun foo() {
inlineFunction {
return //
}
}

返回与跳转

与java支持的跳转类型相同

  • return
  • break 结束最近的闭合循环
  • continue 跳到最近的闭合循环的下一次循环

break 和 continue 标签

标签通过 标签名@ 来定义,如:abc@,fooBar@,调用时,是返过来 @标签名

break 是跳转标签后面的表达式,continue 是跳转到循环的下一次迭代。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
loop@ for (i in 1..100) {
for (j in i..100) {
if (...)
break@loop
}
}

return@a 1 //表示 “在标签 @a 返回 1 ”

//用和传入的 lambda 表达式名字相同的标签
fun foo() {
ints.forEach {
if (it == 0) return@forEach
print(it)
}
}

//用命名函数自动定义的标签
foo outer() {
foo inner() {
return@outer
}
}

this表达式

为了在范围外部访问 this ,使用this@标签名来访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class A { // implicit label @A
inner class B { // implicit label @B
fun Int.foo() { // implicit label @foo
val a = this@A // A's this
val b = this@B // B's this

val c = this // foo()'s receiver, an Int
val c1 = this@foo // foo()'s receiver, an Int

val funLit = @lambda {String.() ->
val d = this // funLit's receiver
val d1 = this@lambda // funLit's receiver
}


val funLit2 = { (s: String) ->
// foo()'s receiver, since enclosing function literal
// doesn't have any receiver
val d1 = this
}
}
}
}