kotlin语法分析(二)
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")
}
上一篇: 牛肉炖番茄汤怎么做,让家人夸夸你
下一篇: 机器人已经在酒店陆续上岗