欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

kotlin语法分析(二)

程序员文章站 2022-06-20 18:28:15
...

Anko

Anko是JetBrains开发的一个强大的库。主要是用来替代以前的xml方式生成代码的ui布局,它可以让我们来简化一些代码就像咱们使用的Anko库中的某些东西,它们就会以属性名,方法等饭方式导入.
在 MainActivity:onCreate ,一个Anko扩展函数可以被用来简化获取一个
RecyclerView:

val forecastList: RecyclerView = find(R.id.forecast_list)

中缀表示法调用函数

要符合一下的条件 :

1.它们是成员函数或扩展函数
2.只有一个参数
3.使用 infix 关键字标注

// 给 Int 定义扩展
infix fun Int.shl(x: Int): Int {
……
}

// 用中缀表示法调用扩展函数

1 shl 2

// 等同于这样

1.shl(2)

可变数量的函数

函数的参数(最后一个参数) 可以使用 vararg 的关键字 修饰标记.

fun <T> asList(vararg ts: T): List<T> {
    val result = ArrayList<T>()
    for (t in ts) // ts is an Array
        result.add(t)
    return result
}

允许将可变数量的参数传递给函数:

val list = asList(1, 2, 3)

在函数内部,类型为T 的 vararg 的参数的可见方式是作为 T 数组 ,即上例中的 ts 变量具有类型Array,只有一个参数可以标注为 vararg ,如果参数不是列表的最后一个,可以使用 命名参数语法传递其后面的参数值,或者,参数有函数类型,则通过在{}外部传入一个lambda.
当我们调用 vararg 函数时, 可以一个一个的传参,或者传入一个数组并希望将其内容传给该函数,我们使用伸展(spread)操作符(在数组前面加 *):

val a = arrayOf(1, 2, 3)
val list = asList(-1, 0, *a, 4)

局部函数

kotlin支持局部函数 ,即一个函数在另一个函数的内部.

fun dfs() {
       var abc=123
        fun inner(){
            println(abc)
        }
    }

局部函数可以访问外部函数(即闭包)的局部变量

成员函数

成员函数在类和 对象中调用的函数

class Sample() {
    fun foo() { print("Foo") }
}

调用 Sample().foo()

泛型函数

函数可以有泛型参数,通过在函数名前使用尖括号指定。

fun <T> singletonList(item: T): List<T> {
    // ……
}

高阶函数

高阶函数是将函数用作参数或者返回值的函数举个例子:

 fun<T> lock1(lock : Lock,body: ()->T): T {
        lock.lock()
        try {
            return  body()
        }finally {
            lock.unlock()
        }

 }

这个函数接受一个 lock 和 函数,获取锁, 运行后并释放lock.
body 拥有函数类型 ()->T 这是个无参的返回值为T的函数.如果我们想调用lock()函数,可以把另一个函数传给它做参数.

fun toBeSynchronized() = sharedResource.operation()

val result = lock(lock, ::toBeSynchronized)

还有另一种更方便的方式:

val result = lock(lock, { sharedResource.operation() })

lambda表达式总是被{}包裹着,其参数(如果存在)在 -> 之前 声明,函数体在 -> 后面 ,在kotlin中又一个约定,如果函数的最后一个是函数,并且传给你的是 一个lambda 表达式,可在()之外的大括号{}指定.

lock (lock) {
    sharedResource.operation()
}

高阶函数还有个例子:

fun <T, R> List<T>.map(transform: (T) -> R): List<R> {
    val result = arrayListOf<R>()
    for (item in this)
        result.add(transform(item))
    return result
}

调用:

val doubled = ints.map { value -> value * 2 }

注意:如果lambda 是调用的唯一的参数,那么圆括号可以省略不写.

it:是lambda 的单个参数的隐式名称

另一个有用的约定是,如果函数字面值只有一个参数,他的声明可以省略(连同 -> ),名称是 it .

ints.map { it * 2 }

下划线适用于未使用的变量.如果lambda 的参数没有使用,可用 _ 来代替其名称:

map.forEach( _ , value -> println("$value
"))

内联函数

使用 高阶函数会 带来一些性能上的缺失,每一个函数都是一个对象,并且会捕获一个闭包,就是指那些函数体内部的变量
内存的分配(类和对象) 和虚拟机的调用都会增加运行时间的开销.但是我们可以通过 内联化 lambda 表达式可以消除这样的额开销.

 lock(lock){foo()}

编译器没有为参数创建一个函数对象并生成一个调用的结果,取而代之的是:

l.lock()
try {
    foo()
}
finally {
    l.unlock()
}

为了让编译器这么做,可以使用一个关键字,inline修饰符标记

inline fun lock<T>(lock: Lock, body: () -> T): T {
    // ……
}

这个inline 会影响函数本身和传入的lambda的表达式,所有这些都可以内联到调用处,内联可能导致代码量增加,我们如果不使用(内联大函数),他会在性能上有所提升的.

禁用内联

如果你只想被当作参数传递给你内联函数的lambda的表达式,可以使用 noinline 的关键字去禁止

inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) {
    // ……
}

可以内联的lambda 的表达式只能在内联函数内部调用或者作为内联函数的参数传递,但是 noinline 可以在任何我们喜欢的方式操作,

扩展函数

扩展函数是指在一个类上增加新的行为,甚至我们没有这类代码的访问权,在java 中通常会有很多,带有static 的修饰的工具类,kotlin中,扩展函数的一个优势是我们不需要在调用方法 的时候将整个对象当作参数传入进去.扩展函数就像是这个类一样,我们直接可以用 this 关键字 来调用 所有 public 的方法.
举个例子: 在Activity中注入一个 toast3()的函数.

fun Activity.toast3(message: CharSequence, duration: Int = Toast.LENGTH_SHORT) {
    Toast.makeText(this, message, duration).show()
}

这个函数会在activity下 子类中使用,Anko已经添加了自己的toast扩展函数针对 CharSquencce 和 resource 的函数 ,还有两个 longToast 和toast 两中方法.

toast("Hello world!")
longToast(R.id.hello_world)

扩展函数也可以是一个属性.我们也可以通过相似的方法来扩展属性,下面的例子展示了是使用它自己的 getter 和 setter 的方法生成一个属性的方式,

public var TextView.text :CharSequence
    get() = getText()
    set(value) = setText(value)

扩展函数并不是修改了原来的类 ,而是已静态的方式注入了 这个方法,扩展函数可以声明在任何的file 文件中.

数据类

data class Forecast(val date: Date, val temperature: Float, val
details: String)

数据类是个非常强大的类,让你避免创建java时保存状态但又操作非常简单的模版代码,定义一个数据类,通过数据类,我们回得到很多有趣的函数

equals(): 它可以比较两个对象的属性来确保他们是相同的。  
hashCode(): 我们可以得到一个hash值,也是从属性中计算出来的  
copy(): 你可以拷贝一个对象,可以根据你的需要去修改里面的属性。

复制数据类

如果我们使用不可修改的对象,就必须要创建一个或者多个属性被修改的对象,这个任务是非常重复的.
举例: 我们要修改ForeCast中的 temperature(温度),我们可以这么做:

val f1 = Forecast(Date(), 27.5f, "Shinyday")
val forecast = f1.copy(temperature = 30.9f)

我们只拷贝了 Forecast 第一个对象后,只修改了temperature的属性而没有修改其他的属性.

映射对象到变量

映射对象的每一个属性到一个变量中,这个过程就是我们知道的多省明,这也就是 componentX 会被自动创建,依据这个 Forecast 的例子

val f1 = Forecast(Date(), 27.5f, "Shiny day")
val (date, temperature, details) = f1

那么再起编译后就会变成这个样子:

val date = f1.component1()  
val temperature = f1.component2()  
val details = f1.component3()

这个特性后面有非常强大的逻辑支撑的,大量的简化了我们的代码,例如 map中 有个扩展函数的实现,允许其在迭代的时候 可以使用 key 和 value 的形式

for ((key, value) in map) {
Log.d("map", "key:$key, value:$value")
}