Android开发经验谈Android开发Android 开发技术分享

Kotlin语法梳理(二)

2018-09-18  本文已影响13人  慕涵盛华

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)是一种编程典范,也就是面向函数的编程。在函数式编程中一切都是函数。

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

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)。

函数字面量

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

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表达式简化写法,下面介绍其中几种。

以下代码是标准形式的Lambda表达式: 
{ a: Int, b: Int -> a + b } 
Kotlin能推导出参数a和b是Int类型,当然返回值也是Int类型。简化形式如下:
{ a, b -> a + b }
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表达式容易被误认为是函数声明,但它不是,而是函数调用

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函数

上一篇下一篇

猜你喜欢

热点阅读