Kotlin语法总结(二)

###Kotlin语法梳理(一)
##目录

##函数
Kotlin中的函数很灵活,它可以独立于类或接口之外存在,即顶层函数,也就是全局函数;也可以存在于别的函数中,即局部函数;还可以存在于类或接口之中,即成员函数

fun 函数名(参数列表):返回值类型{
  函数体
  return 返回值
}

fun add(n1:Int,n2:Int) :Int{
    val sum = n1 + n2
    return sum
}

有的函数没有返回值,此时可以将函数的返回值类型声明为Unit,相当于Java的void;或者可以省略不写。

###Nothing类型
Kotlin中提供一种特殊的数据类型NothingNothing只用于函数返回类型声明,不能用于变量声明。Nothing声明的函数永远不会正常的返回,只会抛出异常。

fun read(): Nothing {
    throw  IOException()
}

Nothing意思在于,有些框架,例如Junit单元测试框架,在测试失败时会调用Nothing返回类型的函数,通过它抛出异常使当前测试用例失败。

###采用命名参数调用函数

fun getArea(width:Int,height:Int):Int{
return width * height
}
getArea(width = 3,height = 4)
    或
getArea(height = 4,width = 3)  

可见采用命名参数调用函数,调用者能够清晰地看出传递参数的含义,命名参数对于有多参数函数调用非常有用。另外,采用命名参数函数调用时,参数顺序可以与函数定义时参数顺序不同

注意 在调用函数时,一旦其中一个参数采用了命名参数形式传递,那么其后的所有参数都必须采用命名参数形式传递,除非它是最后一个参数。

###参数默认值
在声明函数的时候可以为参数设置一个默认值,当调用函数的时候可以忽略该参数。

fun getArea(width:Int,height:Int = 4):Int{
    return width * height
}
getArea(3)// 等价于getArea(3,4)

采用参数默认的函数作用,类似java中的方法重载,Kotlin中提倡使用默认值的方式,因为参数默认值只需要声明一个函数就可以,而重载则需要声明多个函数。

###可变参数
可以通过在参数名前面加vararg关键字的方式来表示这是可变参数。

fun sum(vararg numbers:Int) :Int{
    var sum = 0
    for(number in numbers){
        sum += number
    }
    return sum
}

sum(1,2)
sum(1,2,3,4)
sum(1,2,3,4,5,6,7)

注意:可变参数不是最后一个参数时,后面的参数需要采用命名参数形式传递。

###展开运算符“ * ”
如果已经有一个数组变量,能否传递给可变参数呢?这需要使用展开运算符*

val ary = intArrayOf(1,2,3,4)
sum(*ary)

###表达式函数体
如果在函数体中表达式能够表示成单个表达式时,那么函数可以采用更加简单的表示方式。

fun add(n1:Int,n2:Int) :Int{
    val sum = n1 + n2
    return sum
}

fun add(n1:Int,n2:Int) :Int = n1 + n2

###局部函数
声明在一个函数中的函数就叫做局部函数

fun calculate(n1: Int, n2: Int, opr: Char): Int {

    //局部函数
    fun add(a: Int, b: Int): Int = a + b

    fun sub(a: Int, b: Int): Int = a - b

    return if (opr == '+') add(n1, n2)
    else sub(n1, n2)
}

内部函数的作用于是在外函数体内,如果直接访问局部函数,会发生编译错误

##函数式编程
**函数式编程(functional programming)**是一种编程典范,也就是面向函数的编程。在函数式编程中一切都是函数。

#####函数式编程核心概念如下:

  • **函数是“一等公民”:**是指函数与其他数据类型是一样的,处于平等的地位。函数可以作为其他函数的参数传入,也可以作为其他函数的返回值返回。
  • **使用表达式,不用语句:**函数式编程关心的输入和输出,即:参数和返回值。在程序中使用表达式可以有返回值,而语句没有。例如:控制结构中的if和when结构都属于表达式。
  • **高阶函数:**函数式编程支持高阶函数,所谓高阶函数就是一个函数可以作为另外一个函数的参数或返回值。
  • **无副作用:**是指函数执行过程会返回一个结果,不会修改外部变量,这就是“纯函数”,同样的输入参数一定会有同样的输出结果。

Kotlin语言支持函数式编程,提供了函数类型、高阶函数和Lambda表达式。

###函数类型
Kotlin中每一个函数都有一个类型,称为**“函数类型”**。函数类型作为一种数据类型与其他数据类型在使用场景上没有区别。可以声明变量,也可以作为其他函数的参数或者其他函数的返回值使用。

//该函数的函数类型为:(Int,Int) -> Int
fun rectangleArea(width:Int,height:Int):Int{
return width * height
}

//声明一个变量,类型为:(Int,Int) -> Int 赋值为rectangleArea
val getArea :(Int,Int) -> Int = ::rectangleArea

//调用函数
val area = getArea(3,4)

Kotlin 中 双冒号操作符 表示把一个方法当做一个参数,传递到另一个方法中或者变量进行使用,通俗的来讲就是引用一个方法。

函数类型就是把函数参数列表中的参数类型保留下来,再加上箭头符号和返回类型,形式如下:

**参数列表中的参数类型 -> 返回类型 **

(Int,Double) -> Double

每一个函数都有函数类型,即便是函数列表中没有参数,以及没有返回值的函数也有函数类型(()->Unit)。

###函数字面量
函数类型可以声明变量,那么函数类型变量能够接收什么的数据呢?即函数字面量如何表示?,函数字面量可以有三种表示:

  • **函数引用:**引用到一个已经定义好的,有名字的函数。它可以作为函数字面量。
  • **匿名函数:**没有名字的函数,即匿名函数,它也可以作为函数字面量。
  • **Lambda表达式:**Lambda表达式是一种匿名函数,可以作为函数字面量。
fun calculate(opr:Char):(Int,Int) -> Int{

    fun add(a:Int,b:Int):Int = a + b //加法函数

    fun sub(a:Int,b:Int):Int = a - b //减法函数

    val result :(Int,Int) -> Int =
            when(opr){
                '+' -> ::add   //函数引用
                '-' -> ::sub   //函数引用
                '*' -> {
                    fun(a:Int,b:Int) :Int = a * b  //匿名函数
                }
                else ->{ a,b ->  //Lambda表达式
                    a / b
                }
            }
    return result
}

###高阶函数
可以把函数作为另一个函数的返回值或者参数使用,那么这个函数就属于高阶函数
上述calculate函数就是一个高阶函数,返回值为(Int,Int) -> Int

#####函数作为参数使用

//funcName参数是函数类型
fun getAreaByFunc(funcName:(Int,Int) -> Int,width:Int,height:Int) :Int{
return funcName(width,height)
}
//调用
var result = getAreaByFunc(::rectangleArea,3,4)

##Lambda表达式
Lambda表达式是一种匿名函数,可以作为表达式、函数参数和函数返回值使用,Lambda表达式的运算结果是一个函数。

#####语法格式
Kotlin中的Lambda表达式很灵活,其标准语法格式如下:

{ 参数列表 -> Lambda体 }

Lambda表达式的参数列表与函数的参数列表形式类似,但是Lambda表达式参数列表前后没有小括号。箭头符号将参数列表与Lambda体分隔开,Lambda表达式不需要声明返回类型。Lambda表达式可以有返回值,如果没有return语句Lambda体的最后一个表达式就是Lambda表达式的返回值,如果有return语句返回值是return语句后面的表达式。

注意:Lambda表达式与函数,匿名函数一样都有函数类型,但从Lambda表达式的定义中只能看到参数类型,看不到返回值类型,但是返回值类型可以通过上下文推导出来。

Lambda表达式也是函数类型,那么就可以声明变量,也可以作为其他函数的参数或者返回值使用。

//funcName参数是函数类型
fun getAreaByFunc(funcName: (Int, Int) -> Int, width: Int, height: Int): Int {
    return funcName(width, height)
}
//Lambda表达式作为函数参数使用
val result = getAreaByFunc({ w, h -> w * h }, 3, 4)

####Lambda表达式简化写法
Kotlin提供了多种Lambda表达式简化写法,下面介绍其中几种。

  • 参数类型推导简化: 类型推导是Kotlin的强项,Kotlin编译器可以根据上下文环境推导出参数类型和返回值类型。
以下代码是标准形式的Lambda表达式: 
{ a: Int, b: Int -> a + b } 
Kotlin能推导出参数a和b是Int类型,当然返回值也是Int类型。简化形式如下:
{ a, b -> a + b }
  • 使用尾随Lambda表达式: Lambda表达式可以作为函数的参数传递,如果Lambda表达式很长,就会影响程序的可读性。如果一个函数的最后一个参数是Lambda表达式,那么这个Lambda表达式可以放在函数括号之后。示例代码如下:
fun getAreaByFunc(width: Int, height: Int, funcName: (Int, Int) -> Int): Int {
    return funcName(width, height)
}
//尾随Lambda表达式
val result = getAreaByFunc(3, 4) { w, h -> w * h }

如果没有其他的参数,可以省略括号

fun printFunc(funcName: (Int, Int) -> Int) {
    println("${funcName(1, 2)}")
}
//调用printFunc方法,只有一个参数,类型是函数类型,传递的是Lambda表达式,括号可以省略
printFunc { w, h ->
    w * h
}

注意:尾随Lambda表达式容易被误认为是函数声明,但它不是,而是函数调用

  • 省略参数说明:如果Lambda表达式的参数只有一个,并且能够根据上下文环境推导出它的数据类型,那么这个参数声明可以省略,在Lambda体中使用隐士参数it替代Lambda表达式的参数,示例代码如下:
fun printFunc(str:String,funcName: (String) -> String) {
    println(funcName(str))
}
printFunc("省略参数",{
    it.reversed()   //隐士参数 it
})

Lambda体it隐式变量是由Kotlin编译器生成的,它的使用有两个前提:一是Lambda表达式只有一个参数,二是根据上下文能够推导出参数类型。

##闭包
**闭包(closure)**是一种特殊的函数,它可以访问函数体之外的变量,这个变量和函数一同存在,即使已经离开了它的原始作用域也不例外。这种特殊函数一般是局部函数、匿名函数或Lambda表达式。

闭包可以访问函数体之外的变量,这个过程称为捕获变量

//全局变量
var value = 10
fun main(args: Array<String>) {
    //局部变量
    var localValue = 20
    val result = {a:Int ->
        value++
        localValue++
        val c = a + value + localValue
        println(c)
    }
    result(30)//输出62
    println("localValue = $localValue")//输出21
    println("value = $value")//输出11
}

JavaLambda表达式捕获局部变量时,局部变量只能是final的。在Lambda体中只能读取局部变量,不能修改局部变量。而Kotlin中没有这个限制,可以读取和修改局部变量。

闭包捕获变量后,这些变量被保存在一个特殊的容器中被存储起来。即便是声明这些变量的原始作用域已经不存在,闭包体中仍然可以访问这些变量。

##内联函数
高阶函数中参数如果是函数类型,则可以接收Lambda表达式,而Lambda表达式在编译时被编译称为一个匿名类,每次调用函数时都会创建一个对象,如果这种被函数反复调用则创建很多对象,会带来运行时额外开销。为了解决次问题,在Kotlin中可以将这种函数声明为内联函数

内联函数在编译时不会生成函数调用代码,而是用函数体中实际代码替换每次调用函数。

###自定义内联函数
Kotlin标准库提供了很多常用的内联函数,开发人员可以自定义内联函数,但是如果函数参数不是函数类型,不能接收Lambda表达式,那么这种函数一般不声明为内联函数。声明内联函数需要使用关键字inline修饰

//参数类型有函数类型,声明为内联函数
inline fun printFunc(str:String,funcName: (String) -> String) {
    println(funcName(str))
}

###let函数
Kotlin中任何对象都可以一个let函数let函数后面尾随一个Lambda表达式,在对象非空时执行Lambda表达式中的代码,为空时则不执行。

a?.let { 
    print(a) //a不为空时输出,为空时不执行
}

###with和apply函数
有时候需要对一个对象设置多个属性,或调用多个函数时,可以使用with或apply函数。与let函数类似Kotlin中所有对象都可以使用这两个函数。

在自定义view中当我们初始化画笔时很多时候我们会写下边的代码

var paint = Paint()
paint.color = Color.BLACK
paint.strokeWidth = 1.0f
paint.textSize = 18.0f
paint.isAntiAlias = true

如果使用with,那么就可以写成这样

var paint = Paint()
with(paint) {
    color = Color.BLACK
    strokeWidth = 1.0f
    textSize = 18.0f
    isAntiAlias = true
}

apply函数with函数类似,apply函数是有返回值的,它的返回值就是当前对象。 如果不需要返回值可以使用with函数

关注微信公众号获取Kotlin视频和书籍资料

Android行动派

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值