1.2 方法与Lambda表达式
1.2.1 方法(或函数)
Kotlin中的方法(或函数)可实现一个特定的功能,或完成一个特定的计算任务。方法的定义使用关键字fun,并采用下列格式进行声明:
fun 方法名称(参数列表): 返回值类型{
执行语句
…
return 返回值
}
方法定义时,参数列表中参数声明的基本格式为“参数名:参数类型”。当方法没有返回值时,上述的“返回值类型”使用Unit,或者省略返回值类型的说明。同时,若方法没有返回值时,方法声明中的return语句需省略。当方法中的程序相对简单,仅包含计算表达式,并将计算结果返回时,可使用以下简化格式:
fun 方法名称(参数列表) =计算表达式
例如:
1 fun add(a: Int, b: Int) = a+b
上述语句定义了一个名为add的方法。该方法包含两个输入参数:a和b。方法add可实现一个加法操作,即a+b,而运算结果为该方法的返回值。方法定义中的参数可指定默认值,例如在下列程序中,b参数被指定了默认值10:
1 fun add(a: Int, b: Int=10) = a+b
若方法中的某个输入参数指定了默认值,方法被调用时,该参数在没有指定具体值的情况下,默认值会被程序自动使用。例如,针对add方法可使用add(2)来获得12的计算结果。
方法定义中的输入参数可定义为变长参数。所谓变长参数是指参数的个数可根据运行情况而动态确定。Kotlin中的一个方法只能包含一个变长参数,而且该变长参数只能位于方法定义中参数列表的末尾。变长参数需使用关键字vararg,例如,vararg vs: Array<Int>表示vs是一个接收多个整型的参数。
另外,当某一方法定义时满足了3个条件:①定义时使用了infix关键字;②方法只有一个输入参数;③方法是类成员(或类的扩展方法),则该方法可以中缀方式使用,基本结构为类实例方法名 参数。例如,下述程序运行的结果会在输出窗口中显示“string-sub”:
1 infix fun String.extends(str: String):String{
2 return this.to String() + str
3 }
4
5 fun main(args: Array<String>){
6 val s = "string"
7 val ss = s extends "-sub"
8 println(ss)
9 }
上述程序中,第1行至第3行在String类中增加了一个方法extends。该方法声明使用了infix关键字,而且,该方法只有一个输入参数str。该方法的声明满足了方法中缀使用的条件,则该方法可以中缀方式被调用。程序第7行展示了extends的中缀使用方法。
1.2.2 方法的声明与使用
一个方法的使用是通过调用方法名来实现的。在使用一个方法时,还需要指定该方法的输入参数,例如,add(2, 3)为add函数的一种使用。此外,Kotlin允许以下多种方法的定义方式。
● 方法可在另一个方法内被声明(即所谓本地方法)并被使用;
● 方法可在类内部声明(即成员函数)并被使用;
● 针对某个特定类声明扩展方法;
● 方法可基于泛型被声明为泛型方法;
● 方法可被声明为递归方法。
下列程序展示了本地方法的声明和使用(程序中,increase是本地方法,程序运行的结果为6):
1 fun add(a:Int, b:Int):Int{
2 fun increase(c:Int):Int{
3 return c+1
4 }
5 return increase(a+b)
6 }
7
8 fun main(args:Array<String>){
9 println(add(2,3))
10 }
1.2.3 Lambda表达式和高阶方法
Lambda表达式是一种匿名方法的表示方式。Lambda表达式一般使用箭头来表示一个运算操作,该操作分为3个部分:箭头,箭头左边,箭头右边。其中,箭头用于表示一个映射,箭头左边是映射的输入参数列表,箭头的右边为映射的输出。例如,{x: Int, y: Int -> x+y},该运算有两个输入整型参数x和y,而运算的结果(输出)为x+y。此外,Lambda表达式在声明时需要使用花括号,即{}。
Lambda可被用于赋值给一个常量(或者变量),例如,val add={x: Float, y: Float -> x+y};这样, add实际上可被看作是一个方法,该方法有两个输入参数x和y,输出x+y,使用时为add(0.1f, 0.2f)。
与Lambda表达式类似,方法类型也可使用箭头操作来表示,同样包含3个部分:箭头,箭头左边,箭头右边。箭头用于表示一个映射,箭头左边是映射的输入参数类型列表,箭头的右边为映射的输出类型。例如,(Int, Float) -> Float,该表达式表示方法的两个输入参数为Int和Float,输出参数类型为Float。
程序中,方法类型不使用花括号。另外,方法类型可看成是一种数据类型,并用于声明常量或变量。例如,val calc: (Int, Float) -> Float ={x: Int, y: Float -> x*y},在这样的示例中,calc实质上是一个类型为(Int, Float) -> Float的运算,而具体的实现则被定义为{x: Int, y: Float -> x*y}。
在Kotlin中,所谓高阶方法是指方法中的参数是方法,或者方法的返回值是方法。例如,在下列程序中,calc1和calc2为高阶方法:
1 fun main(args: Array<String>){
2 fun calc1(n:Int, f: (Int)->Int): Int{ //输入参数为方法
3 return f(n)
4 }
5 println(calc1(10,{it+1}))
6 println(calc1(10,{i -> i-1}))
7
8 fun calc2(n:Int, fn: Float, f: (Int, Float)-> Float): Float{
9 return f(n,fn) //返回值为方法
10 }
11 println(calc2(10, 0.2f, {i: Int, fl: Float -> i*fl}))
12 }
上述程序中,calc1方法使用f: (Int)->Int作为参数(即名称为f,参数类型为(Int) -> Int,这样的结构表示f是一个输入为整型,返回值为整型的函数)。上述程序第5行和第6行中,f被指定为具体的运算方法,而这些方法使用Lambda表达式来说明,即第5行的{it + 1},以及第6行的{i -> i+1}。
上述程序第5行中,使用了关键字it,该关键字指代方法的输入参数(即calc1中f的输入参数)。Kotlin的方法中,当方法的输入参数只有一个时,可使用it来对参数进行访问。上述示例程序中,calc2方法使用了f: (Int, Float) -> Float参数,该参数名称为f,有两个输入参数类型:Int和Float,并返回一个单精小数。
Kotlin的高阶方法还有一种形式,例如,在下列示例程序中,times方法为一个高阶方法:
1 fun main(args: Array<String>){
2 fun times(t:Int) = { x: Double -> x * t }
3 println(times(2)(0.2))
4 }
上述程序中,当 times(t:Int)方法使用参数时,该方法实质上变为函数 times(t)(x:Double):Double。而示例程序运行的结果为0.4。
1.2.4 匿名方法和闭包
匿名方法是没有名字的方法,除了Lambda表达式可用来定义匿名方法外,匿名方法还可直接被定义。例如:
1 fun (x: Int, y: Float): Float = x*y
2
3 fun (x: Int, y: Float): Float{
4 return x * y
5 }
上述声明可作为表达式赋给变量或常量,例如,val m= fun (x: Int, y: Float): Float = x*y。另外,匿名方法和Lambda表达式可访问它们的闭包,也就是说可访问外部范围的变量。下列程序展示一种简单的闭包访问(程序运行的结果为101):
1 fun main(args:Array<String>){
2 var n = 100
3 fun p(){
4 n = n+1
5 }
6 p()
7 println(n)
8 }
上述程序中n可被看成一个main方法范围内的公共变量,而方法p与n在同一个方法内,所以该方法可以直接访问n,并进行计算。基于上述程序,一个闭包还可以这样使用:
1 val print = println("testing")
2
3 fun main(args:Array<String>){
4 print
5 }
上述程序将print定义为一个语句,然后在main中直接调用,程序运行结束,输出窗口会输出“testing”。程序中print和main在同一个文件内,所以print可被视为文件内的公共变量,而main方法可直接使用print。
Kotlin程序可基于Lambda表达式定义“自执行闭包”,例如:
1 fun main(args:Array<String>){
2 {a:String->println(a)}("testing")
3 }
上述程序首先定义一个Lambda表达式,然后通过()运算符号直接调用该表达式,然后运行。所以,程序运行结束,输出窗口会输出“testing”。
再例如下述程序:
1 fun extend():() -> Unit{
2 var content = "string"
3 return {
4 content += ": string"
5 println(content)
6 }
7 }
8
9 fun main(args:Array<String>){
10 val ext = extend()
11 for (i in 1..3){
12 ext()
13 }
14 }
程序第1行定义一个函数为extend,该函数没有输入参数,输出参数是一个方法类型()->Unit,即一个类型为()->Unit的函数;程序第2行定义一个字符串变量content;第3行至第6行返回一个匿名函数(体),该函数对content所包含的字符串进行修改,并打印修改过的字符串。程序第2行到第6行可理解为content是匿名函数的全局变量。程序第10行将匿名函数赋值给ext,使得ext为一个函数(该函数没有输入参数)。程序第11行至第13行执行结束,程序会显示以下内容:
1 string: string
2 string: string: string
3 string: string: string: string