程序结构
变量和常量
常量
在Kotlin中,val相当于常量,类似于Java中的final,例如:
val HELLO_WORLD = "HELLO WORLD"//可以自己进行推导类型
val在Kotlin中是编译期常量,常量只有get方法,没有对应的set方法,值必须初始化,且不可被修改,但是实际工作中并不一定见得就会初始化为想要的值,所以应用的时候,应该加一个lateinit,延迟初始化
变量
变量使用var来表示,var标注的值是可以修改的。对应的set get方法都会有,但是如果没有初始值,且不能为null的话,还是需要使用来进行初始化
lateinit var name : String
####函数
函数定义
函数使用fun
进行声明,如果函数不是成员函数的话,可以直接进行调用,若为成员函数,则需要使用实例进行调用,Kotlin中,函数的参数必须有显式类型,采用Pascal表示法定义,例如:
fun powerOf(number:Int,exponent:Int){
//...
}
默认值
函数可以拥有默认值,而且覆盖一个带有默认值的方法的时候,必须从签名中省略默认参数值,例如:
open class A{
open fun foo(i:Int = 10){...}
}
open class B : A(){
override fun foo(i:Int){...}//不能有默认值
}
命名参数调用函数
Kotlin中可以使用命名参数的方式调用函数,而且如果函数中有默认参数,且一个默认参数在一个无默认值的参数之前,那么该默认值只能通过使用命名参数调用该函数来使用,但是如果最后一个是_lambda_表达式的话,则允许默认参数不传值
fun foo(bar:Int = 0 , baz : Int,bun : Int){...}
foo(baz = 1,bun = 2) //默认值bar = 0
可变参数
不同于Java中的String ... str
,在Kotlin中,使用_vararg_来表示:
fun foo(vararg strings : String){...}
foo(strings=*arrayOf("a","b","c"))//多个值的话,可以使用*操作符将vararg变量以命名形式传入
foo(strings = "a")//单个值的话则不需要*操作符
void
Kotlin中没有void返回值,每个函数都由返回类型,但是如果需要一个类似于Java中void的函数的时候,可以使用Unit返回值类型,这种类型不需要进行显示声明
单表达式函数
Kotlin中更多的是使用表达式,以使代码简约的同时保持可读性:
fun double(x:Int) = x * 2//单表达式可以省略函数,如果返回值类型可以由编译器进行推断的话,返回值类型也可以省略
中缀表示法
只能是成员函数或者扩展函数;只能有一个参数,使用_infix_进行标注。例如:
infix fun Int.shl(x:Int):Int{
//do something
}
1 shl 2
//等同于
1.shl(2)
局部函数
后续补充…
泛型函数
后续补充…
内联函数
高阶函数会带来一些效率上的损失,这时使用内联函数的话,就可以消除这类的开销,类似于C语言的函数指针,例如:
inline fun<T> lock(lock:Lock,body: () -> T):T{
//...
}
//lock()函数会被转换成下面的形式
l.lock()
try{
foo()
}
finally{
l.unlock()
}
若只想一部分内联,一部分不内联的话,则使用noinline来标记.例如:
inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) {
// ……
}
后续补充…
扩展函数
后续补充…
高阶函数和 Lambda 表达式
其实就是_函数式编程_,如下:
fun <T> lock(lock : Lock,body:() -> T):T{
lock.lock()
try{
return body()
}finally{
lock.unlock()
}
}
//传一个函数进行调用
fun toBeSynchronized() = shareResource.operation()
val result = lock(lock,::toBeSynchronized)
//使用lambda表达式
val result = lock(lock,{ sharedResource.operation() })
lambda简短描述:
- ambda 表达式总是被大括号括着;
- 其参数(如果有的话)在 -> 之前声明(参数类型可以省略);
- 函数体(如果存在的话)在 -> 后面
Lambda表达式或匿名函数可以访问其闭包,即在外部作用域中声明的变量。与 Java 不同的是可以修改闭包中捕获的变量:
var sum = 0
ints.filter{ it > 0 }.forEach{
sum += it
}
println(sum)
后续补充…
尾递归函数
使用_tailrec_修饰符来标记,编译器会优化该递归,留下一个快速而高效的基于循环的版本:
tailrec fun findFixPoint(x: Double = 1.0):Double= if(x == Math.cos(x)) x else findFixPoint(Math.cos(x))
编译器优化过后的代码:
private fun findFixPoint():Double{
var x = 1.0
while(true){
val y = Math.cos(x)
if(x == y) return y
x = y
}
}
要符合 tailrec 修饰符的条件的话,函数必须将其自身调用作为它执行的最后一个操作。在递归调用后有更多代码时,不能使用尾递归,并且不能用在try/catch/finally 块中。目前尾部递归只在 JVM 后端中支持。