Kotlin语法基础,函数与闭包
来源:互联网 发布:nginx 多个二级域名 编辑:程序博客网 时间:2024/06/05 03:39
函数
为了是程序简洁明了,更具有逻辑性,我们通常的做法就是把相似的功能模块整合到一起,并设计成函数。函数是执行特定任务模块的代码,每个函数都有一个类型,你可以像使用Kotlin语言中其他类型一样使用函数类型,将函数作为参数传递给其他函数,或者将函数类型当做返回类型。你可以通过给定一个函数名称来标识它是什么,并在需要的时候使用该名称来调用函数以执行任务。
在Kotlin语言中,函数可以分为两类:一种是库和框架中的函数,要调用库函数或者使用框架中的函数,需要在单元顶部引用库或者框架的接口单元;另一种则是自定义函数,这类函数通常都是为了解决某一特别的问题而编写的,编写好函数之后,我们可以在程序的任意位置引用并调用它。学会编写和使用函数会使你的代码复杂度、可读性、可维护性都提高。
函数的定义和调用
要使用函数,必须先定义后调用。每一个函数都有一个函数名,用来表述函数执行的任务。要使用函数是,可以使用它的名称进行“调用”,并通过函数名来传递参数。函数形参是指函数定义时设定的参数名,函数实参是指调用函数时实际传递的参数,一个函数的实参必须始终和函数的形参顺序一致。函数定义需要使用关键字fun,其一般格式为:
fun <函数名>(<参数名1> : <参数1类型>, <参数名2> : <参数2类型>...) : <函数返回类型> { 函数体... return <返回值>}
例如下面的例子,我们定义了一个函数getPercent,从函数的名字我们可以看得出来,它的功能是获取一个数字的百分比显示内容。即,我给它一个浮点数number,它返回给我一个百分比显示的字符串,0.98 ——> 98%。我们需要一个参数来支出这个数字具体是什么,即设计形参number。参数通过函数名传进函数体李,通过函数处理之后需要一个返回处理结果,以表示百分显示的效果。所以有了一个返回值,这里是String类型。函数定义如下:
/** * 获取百分比显示的数字 */fun getPercent(number: Double): String { val percentNumber = number * 100 return "$percentNumber %"}
我们可以直接通过使用函数名来调用这个函数,并且在调用的时候把参数传进去:
println(getPercent(0.98))
函数getPercent将数字0.98传入到函数体内部,函数体内部的程序语句对数字进行处理判断,然后将处理后的的显示效果赋值给percentNumber变量,在通过return关键字将percentNumber的值返回给函数主体。
函数的形参
在Kotlin语言中,函数的形参和返回值是非常具有灵活性的,在需要的时候,你可以定义一个或者多个甚至选择性的省略。
在定义的时候可以忽略返回值,但一个定义了返回值的函数必须在函数体中设定返回值。对于一个定义了返回类型的函数来说,如果没有返回值,相当于没有指定函数的出口,这种情况下,编译器会报错的。事实上,在定义的时候忽略返回值等于是隐式声明函数的返回值是空。
如之前的查找“X”字符的例子:
/** * 查找x字符串 */fun findX(xArray : Array<String>) { for (word in xArray) { if (word == "x") { println("find the 'x' word!") return // 中断整个循环执行,退出函数 } } println("Can not find 'x' !")}
我们在查找到“X”之后,return一个空值,这里的return并没有给函数返回任何值。它的作用仅仅是告诉这个函数,在这个情况下return函数就该结束了。
参数默认值
当然,对于任何形参的定义来说,我们可以给其设定一个默认值。如果已经定义了默认值,那么调用函数时就可以省略该形参。和其他高级语言一样,为了避免遗漏参数或者参数传递二义性,请在函数形参列表的末尾放置带默认值的形参,不要在默认值的形参前放置。
/** * 给一个字符串拼接前缀和后缀 * 前缀默认值:*** * 后缀默认值:### */fun catString(myString: String, prefix: String = "***", suffix: String = "###"): String{ return prefix + myString + suffix}// 使用的时候,可以忽略带有默认值的参数不传catString("hello")
在有定义默认值得情况下,当你没有指定外部名称时,Kotlin语言将为你定义的任何默认形参提供一个自动外部名,这个自动外部名和本地名类似。
可变个数参数(vararg)
可变个数形参是指可接受零个或者多个指定类型值得形参,你可以用它来传递任意数量的输入参数。声明可变个数形参需要用到vararg关键字,当参数传递进入函数体之后,参数在函数体内可以通过集合的形式访问。还有一点需要注意的是,函数最多可以有一个可变个数的形参,而且它必须出现在参数列表的最后。如:
/** * 求多个数字的和 */fun sumNumbers(vararg numbers : Double) : Double{ var result : Double = 0.0 for (number in numbers) { result += number } return result}// 使用的时候,则可以传任意多个参数sumNumbers(1.2,2.56,3.14)
命名参数
如果别人第一次阅读你的代码,可能会不知道你使用的这个函数的形参的目的,那么使用外部形参名称就可以是你要表达的亿图更加的明确,上下文更加清晰。当然,如果每个形参名 的目的已经足够的简单清晰明确的话,那就无需在制定外部形参名。命名参数的方式,是在调用函数的时候带上这个形参的名称,并做赋值语句传入。
如,我们定义了一个格式化字符串的函数
fun reformat(str: String, normalizeCase: Boolean = true, upperCaseFirstLetter: Boolean = true, divideByCamelHumps: Boolean = false, wordSeparator: Char = ' ') { //...}
然而,当使用非默认参数调用它时,该调用看起来就像
reformat(str, true, true, false, '_')
阅读起来相当的费劲,而且对于多个设置有默认值类型相同形参的函数,在调用使用上还很容易将参数传错。如果我们使用外部形参命名的方式,我们就可以写为:
reformat(str, normalizeCase = true, upperCaseFirstLetter = true, divideByCamelHumps = false, wordSeparator = '_')
使用上直观,阅读上清晰易懂,避免了传错参数导致的低级Bug。
返回 Unit 的函数
在函数中,所有定义了返回值的函数,我们都称之为显示函数。没有返回类型的函数,我们称之为隐式函数。
上文中我们又向大家介绍,在定义的时候忽略返回值等于是隐式声明函数的返回值是空。在Kotlin中,这种隐式返回的类型称之为:Unit。这个Unit类型的作用类似Java语言中的void类型。Unit是一种只有一个值——Unit的类型。这个值不需要显式返回。
即定义函数printHello具有Unit返回类型:
fun printHello(name: String): Unit { // ...}
和不写Unit返回类型的作用是一样的。
fun printHello(name: String) { // ...}
当然,在返回值return上,写不写Unit效果都一样。
fun printHello(name: String): Unit { return Unit// 和不写return // 和return Unit作用一样}
单表达式函数
单表达式函数,即只有一个表单式的函数。当函数返回单个表达式时,可以省略花括号并且在 = 符号之后指定代码体即可
fun doubleValue(x: Int): Int = x * 2
当返回值类型可由编译器推断时,显式声明返回类型是可选的
fun doubleValue(x: Int) = x * 2
嵌套函数
在结构化编程盛行的年代,嵌套函数被广泛使用,在一个函数体中定义另外一个函数体就为嵌套函数。嵌套函数默认对外界是隐藏的,但仍然可以通过它们包裹的函数调用和使用它,举个例子:
/** * 嵌套函数demo * * 比较数字numberA和数字numberB的二次幂谁大 */fun compare(numberA: Int, numberB: Int) : Int{ var powerB = 0 // 嵌套函数,求一个数字的二次幂 fun power(num : Int) : Int{ return num * num } powerB = power(numberB) if (numberA > powerB) { return numberA } else { return powerB }}fun main(args: Array<String>) { // 报错!!! // 无法直接调用内部嵌套的函数 power()}
闭包
闭包是Kotlin语言的众多特性之一,对多数习惯使用了Java语言的开发者来说是一个很难理解的东西(实际上Java8也开始支持闭包特性),Kotlin中的闭包是一个功能性自包含模块,可以再代码中被当做参数传递或者直接使用。这个描述可能不太直观,你可能还是想问:“那么到底什么是闭包呢?闭包在函数中是以什么形式出现和使用的呢?”下面向大家介绍。
什么是闭包
一段程序代码通常由常量、变量和表达式组成,然后使用一对花括号“{}”来表示闭合,并包裹着这些代码,由这对花括号包裹着的代码块就是一个闭包。其实在签名我们也介绍了全局和嵌套函数就是一种特殊的闭包。这里,我们总结了一下,Kotlin语言中有三种闭包形式:全局函数、自嵌套函数、匿名函数体。
听着名词解释是挺让人费解,下面我们举个例子:
fun main(args: Array<String>) { // 执行test闭包的内容 test}// 定义一个比较测试闭包val test = if (5 > 3) { println("yes")} else { println("no")}
先不说闭包的结构,从代码层面来看,上述逻辑我们都知道这段代码永远都只会输出“yes”。那么你可能会问:
- 为什么能够将一个if逻辑语句赋值给test呢?
- 为什么在main函数中单独写一个test就能执行test所指向的if逻辑呢?
- 如果一个if逻辑快块是一个闭包?那么还有什么逻辑块可以是闭包呢?
下面,我们会一一给你解答。
为什么会设计闭包这种结构?
从上述的例子来说,我们可以看出来,其实定义一个函数就好了,为什么设计编程语言的人要设计闭包这么一个结构呢?这就得从作用域开始说起。变量的作用域无非就是两种:全局变量和局部变量。
- 全局变量
就Kotlin语言而言,函数内部可以直接读取全局变量。
var n = 999;fun f1() { println(n) // 打印999}f1()
- 局部变量
另一方面,在函数外部自然无法读取函数内的局部变量。
fun f1(){ var n=999}println(n) // 报错!!! n是函数内局部变量,外部无法调用
那么,如何在外部调取局部的变量呢?答案就是——闭包。
这里,我们给闭包下一个定义:闭包就是能够读取其他函数内部变量的函数。
闭包的用途在哪里?
闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。什么意思,没有听懂?下面,我们具体跑个例子试试,如:
/** * 计数统计 */fun justCount():() -> Unit{ var count = 0 return { println(count++) }}fun main(args: Array<String>) { val count = justCount() count() // 输出结果:0 count() // 输出结果:1 count() // 输出结果:2}
有没有发现闭包这点的好处,闭包就是在函数被创建的时候,存在的一个私有作用域,并且能够访问所有的父级作用域。每个功能模块我们都能够拆解到不同fun里,不同fun里的变量保持相互调用的可能性,相互独立还彼此不影响。我们可以函数式编程了!
广义上来说,在Kotlin语言之中,函数、条件语句、控制流语句、花括号逻辑块、Lambda表达式都可以称之为闭包,但通常情况下,我们所指的闭包都是在说Lambda表达式。
自执行闭包
自执行闭包就是在定义闭包的同时直接执行闭包,一般用于初始化上下文环境。 例如:
{ x: Int, y: Int -> println("${x + y}")}(1, 3)
- Kotlin语法基础,函数与闭包
- Kotlin语法基础,包引入
- Kotlin Primer·函数与闭包
- Android kotlin入门与基础语法一
- Android kotlin入门与基础语法二
- Kotlin Primer·第五章·函数与闭包
- Kotlin开发语言学习(5) 函数与闭包
- Kotlin基础语法
- Kotlin 基础语法学习
- Kotlin基础语法
- Kotlin 基础语法
- Kotlin基础教程-基础语法
- 【Kotlin】基础语法学习
- kotlin基础语法
- kotlin基础语法学习
- Kotlin基础语法详解
- Kotlin基础语法
- Kotlin语法基础
- Python 循环终止语句
- PHP中查看PHP基本信息
- C Primer Plus(第6版)第四章答案
- 部分手机无法显示Dialog问题
- java实现俄罗斯方块
- Kotlin语法基础,函数与闭包
- WUST 1904 饥饿的牛(dp)
- Mac问题积累
- POJ 1012--Joseph
- 数据库导入数据
- Recyclerview和CheckBox的完美
- 错误解决:Unable to add module to the current project as it is not of package
- 一步一步理解Java NIO(上)
- 理解EJB,EJB到底是什么?