基于Kotlin的Android应用程序开发
上QQ阅读APP看书,第一时间看更新

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