目录
1. Lameda
1.1 Lamebda演变过程
1.2 Lambda语法
1.3 、lambda中下划线(_)
1.4 Lambda和高阶函数的关系
1.5 Lambda的底层原理:
1.6 Lambda和闭包的关系
2.kotlin的核心是啥? 高阶函数
3.扩展函数
3.1 let、with、apply区别是使用
4.内敛函数inline
5 .匿名函数和顶层函数
6.泛型 ,使用Kotlin Reified 让泛型更简单安全
7.DSL-Anko布局 高深,看不懂
8.Kotlin系列之顶层函数和属性
1. Lambda介绍
1.1 Lamebda演变过程
这里举例一个Android中最常见的按钮点击事件的例子
gl_surface_view.setOnClickListener(View.OnClickListener {
})
简化;
view.setOnClickListener { v: View ->
switchToNextPage()
}
另外,如果这个 Lambda 是单参数的,它的这个参数也省略掉不写:
简化成: ()-> 都省了!
gl_surface_view.setOnClickListener {
}
1.2 Lambda语法
有多个参数不能用it、自己定义
当另外一个参数可以用下划线表示_
为了让大家彻底的弄明白Lambda语法,我这里用三种用法来讲解。并且举例为大家说明
箭头后面是返回值 : -> 返回值
语法如下:
1). 无参数的情况 :
val/var 变量名 = { 操作的代码 }
2). 有参数的情况
val/var 变量名 : (参数的类型,参数类型,...) -> 返回值类型 = {参数1,参数2,... -> 操作参数的代码 }
可等价于
// 此种写法:即表达式的返回值类型会根据操作的代码自推导出来。
val/var 变量名 = { 参数1 : 类型,参数2 : 类型, ... -> 操作参数的代码 }
3). lambda表达式作为函数中的参数的时候,这里举一个例子:
fun test(a : Int, 参数名 : (参数1 : 类型,参数2 : 类型, ... ) -> 表达式返回类型){
...
}
实战分析:
//给传参数命名, 2个参数, 要自己写名字aNum,bNum
var method5:(Int,Int)->Unit={aNum,bNum->
Log.d("dd","updateGrade: $aNum","+$bNum")
}
//屏蔽传参, 下划线占位
var method6:(Int,Int)->Unit={aNum,_ ->
Log.d("dd","updateGrade: $aNum","+$aNum")
}
//传入值和返回值(高级的写法)
var method07:(String) ->String ={str->str}
//省去返回值
val method2={ Log.d("dd","updateGrade:")}
//一个参数默认是it
var method3:(String) ->Unit={
Log.d("dd",":$it")
}
var method4:(Int)->Unit={
when(it){
1-> Log.d("dd",":$it")
else ->Log.d("dd",":$it")
}
}
经过上面的实例讲解与语法的介绍,我们对其作出一个总结:
1).lambda表达式总是被大括号括着。
2). 定义完整的Lambda表达式如上面实例中的语法2,它有其完整的参数类型标注,与表达式返回值。当我们把一些类型标注省略的情况下,
就如上面实例中的语法2的另外一种类型。当它推断出的返回值类型不为'Unit'时,它的返回值即为->符号后面代码的最后一个(或只有一个)表达式
的类型。
3). 在上面例子中语法3的情况表示为:高阶函数,当Lambda表达式作为其一个参数时,只为其表达式提供了参数类型与返回类型,
所以,我们在调用此高阶函数的时候我们要为该Lambda表达式写出它的具体实现。
invoke()函数:表示为通过函数变量调用自身,因为上面例子中的变量b是一个匿名函数。
it
it并不是Kotlin中的一个关键字(保留字)。
it是在当一个高阶函数中Lambda表达式的参数只有一个的时候可以使用it来使用此参数。it可表示为单个参数的隐式名称,是Kotlin语言约定的。
1.3 、lambda中下划线(_)
在使用Lambda表达式的时候,可以用下划线(_)表示未使用的参数,表示不处理这个参数。
同时在遍历一个Map集合的时候,这当非常有用。
举例:
val map = mapOf("key1" to "value1","key2" to "value2","key3" to "value3")
map.forEach{
key , value -> println("$key \t $value")
}
// 不需要key的时候
map.forEach{
_ , value -> println("$value")
}
输出结果:
key1 value1
key2 value2
key3 value3
value1
value2
value3
1.4 Lambda和高阶函数的关系
函数里面传入一个lambda表达式是什么意思?
举例: 登录
fun loginAction(userName:String,userPwd:String, loginResponseResult:(Boolean)->Unit){
if(userName==null){
return
}
loginEngine(userName,userPwd,loginResponseResult)
}
Lambda表达式是为我们减少了大量的代码,但是Lambda表达式是为后面的高阶函数章节打下基础
在 Java 里是不允许把方法作为参数传递的,但是我们有一个历史悠久的变通方案:接口把方法当成参数传递到参数
1.4.1 双冒号 ::method 到底是什么?
解释:
1). 一个函数名的左边加上双冒号,它就不表示这个函数本身了,而表示一个对象
2).通过变量, 把函数赋值给变量
@JvmStatic
fun startActivity(context: Context, bean: SingleTrainBean) {
val intent = Intent(context, AICoachSingleTrainActivity::class.java)
intent.putExtra(SINGLE_TRAIN_BEAN, bean)
context.startActivity(intent)
}
这个双冒号的写法叫做函数引用 Function Reference
因为加了两个冒号,这个函数才变成了一个【对象】。这样就可以赋值给左边的变量!
fun b(param: Int): String {
return param.toString()
}
val d = ::b
val e = d
创建一个函数类型的对象有三种方式:双冒号加函数名、匿名函数和 Lambda;
Lambda 和匿名函数的联系!
匿名函数:没有函数名
a(fun b(param: Int): String {
return param.toString()
});
val d = fun b(param: Int): String {
return param.toString()
}
另外,这种写法的话,函数的名字其实就没用了,所以你可以把它省掉:
a(fun(param: Int): String {
return param.toString()
});
val d = fun(param: Int): String {
return param.toString()
}
Kotlin 的匿名函数和 Lambda 表达式的本质,它们都是函数类型的对象。
因为对象才能当参数传递匿名函数虽然叫匿名函数,但是它不是函数,是函数类型的对象!
1.5 Lambda的底层原理:
Kotlin 的匿名函数和 Lambda 表达式的本质,它们都是函数类型的对象
Lambda表达式的本质其实是匿名函数,因为在其底层实现中还是通过匿名函数来实现的。但是我们在用的时候不必关心起底层实现。不过Lambda的出现确实是减少了代码量的编写,同时也是代码变得更加简洁明了。
原理:lameda表达式---->高阶函数--------->函数类型的对象------>匿名函数------(简化通过lemeda)----->lameda
内部类: 内部类访问外部类的变量, 需要用final修饰, 不然会报错!
面试官:kotlin的lameda表达式和java的lameda有啥区别?
问题: Java有lambda表达式吗?
Java 8 的 Lambda 只是一种便捷写法,本质上并没有功能上的突破,而 Kotlin 的 Lambda 是实实在在的对象。
Java 8 Lambda 表达式来创建匿名类对象。lambda 表达式不生成匿名类文件,但它本质上还是在创建一个匿名类对象,只是一种简化写法而已
Kotlin lambda 表达式完全通过生成匿名类实现,其它特点类似。 Kotlin 的 Lambda 还能当函数参数?
支持动态传入函数,
因为 Kotlin 的 Lambda 是实实在在的函数类型的对象
1.6 Lambda和闭包的关系
<pre style="margin: 8px 0px;">在 Java8 版本使用 lambda 表达式是有限制的,它不是真正意义上的闭包,而 Kotlin 中的 lambda 才是真正意义上支持闭包的实现。 </pre>
2. 高阶函数
kotlin的核心是啥? 高阶函数, 携程是扩展库
Kotlin 函数是一级函数,这意味着它们可以存储在变量和数据结构中,作为参数传递给其他高阶函数,也可以从其他高阶函数返回。
对于其他非函数值,你可以以任何可能的方式对函数进行操作。
高阶函数定义-Heigher-Order-Functions
- 高阶函数定义:参数类型是函数类型,或者返回值是函数类型的函数称为高阶函数,lambmda也算是一个函数, 除此之外函数类型还能赋值给变量
源码: Functions.kt里面定义了函数
高阶函数的作用: 可以动态执行哪个方法, 具体在 a 执行的时候内部调用哪个方法,我希望可以动态设置
问题: java中是如何实现: 方法中调用方法?
通过接口, 接口里面定义方法, 传入接口搞定!
2.1 举例:登入:
/**
* 传入2个值,还有一个是内部得到的结果
*/
loginAction("peng","cai"){
if(it){
println("suc")
}else{
println("fail")
}
}
}
fun loginAction(userName:String,userPwd:String, loginResponseResult:(Boolean)->Unit){
if(userName==null){
return
}
loginEngine(userName,userPwd,loginResponseResult)
}
/***
* 传入3个值,最后一个赋值结果变量
*/
fun loginEngine(userName:String,userPwd:String, loginResponseResult:(Boolean)->Unit){
if("peng"==userName||userName=="cai"){
loginResponseResult(true)
}else{
loginResponseResult(false)
}
}
2.2 apply()高阶函数源码分析:
2.3 内敛高阶函数:
高阶函数:
把lambda作为参数传递!
() -> Unit //表示无参数无返回值的Lambda表达式类型
(T) -> Unit //表示接收一个T类型参数,无返回值的Lambda表达式类型
(T) -> R //表示接收一个T类型参数,返回一个R类型值的Lambda表达式类型
(T, P) -> R //表示接收一个T类型和P类型的参数,返回一个R类型值的Lambda表达式类型
(T, (P, Q) -> S) -> R //表示接收一个T类型参数和一个接收P、Q类型两个参数并返回一个S类型的值的Lambda表达式类型参数,返回一个R类型值的Lambda表达式类型
3.扩展函数
3.1 什么是扩展函数
T.()是啥 T.()也是一个泛型扩展函数
扩展函数: 不需要再添加类, 或者类中增加方法!
Kotlin允许开发者在不改变已有类的情况下,为某个类添加新的函数。这个特性叫做扩展函数。
你可以给已有的类去额外添加函数和属性,而且既不需要改源码也不需要写子类
问题: 谁可以调用这个扩展函数?
.前面的可以用, T类型的可以调用
class Extensions {
fun String.method1(i: Int) {
...
}
...
kotlin FunctionType: function references , lambda , 匿名函数
扩展方法编译成java后,实际上是一个 public static的静态方法,传入了对象实例和参数
扩展函数:
为什么要使用Kotlin中的扩展函数?
如何使用扩展函数和扩展属性?
扩展函数和属性原理
扩展函数和成员函数区别
扩展函数不可以被重写
3.2 Kotlin写一个let的扩展函数,扩展函数和扩展属性?Kotlin中的扩展函数本质原理是什么?
kotlin的扩展方法是怎么实现的,inline关键字的作用
因为只要反编译过Kotlin扩展函数的代码都知道回答
扩展函数的缺点: 找不到!
3.3. 扩展函数的使用场景:
3.3.1 比方说,把dp转成px
val Float.dp
get() = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
this,
Resources.getSystem().displayMetrics
)
...
val RADIUS = 200f.dp
3.3.2 举例: Math.pow()
顶层函数:所有地方都可以访问,就像局部变量.
并且它不属于任何类,但是可以声明限制某个类才可以使用!
举一个简单的例子。如果要关闭一个I/O流,使用Java可能是写一个工具方法。
/**
* 安全关闭io流
* @param closeable
*/
public static void closeQuietly(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
对Kotlin而言,可以对Closeable扩展一个函数closeQuietly()。
fun Closeable?.closeQuietly() {
try {
this?.close()
} catch (e: Throwable) {
}
}
之后,任何实现了Closeable接口的类,都可以使用它本身的closeQuietly()方法来关闭流。我们不再需要那个工具方法了。
使用扩展函数
fun loge(tag: String, content: String?) {
Log.e(tag, content ?: tag)
}
3.3.3 项目中的实战:
在项目中,我们使用扩展函数对Glide做了封装,大大简化了Glide的使用。
/**
* 占位符矩形
*/
fun ImageView.load(url: String) {
get(url).placeholder(R.drawable.shape_default_rec_bg)
.error(R.drawable.shape_default_rec_bg)
.into(this)
}
/**
* 占位符圆角矩形
*/
fun ImageView.loadRound(url: String) {
get(url).placeholder(R.drawable.shape_default_round_bg)
.error(R.drawable.shape_default_round_bg)
// .apply(RequestOptions.bitmapTransform(RoundedCornersTransformation(DisplayUtil.dp2px(context, 6f), 0)))
.transform(RoundedCornersTransformation(DisplayUtil.dp2px(context, 6f), 0))
.into(this)
}
/**
* 占位符圆形
*/
fun ImageView.loadCircle(url:Drawable) {
get(url).placeholder(R.drawable.shape_default_circle_bg)
.error(R.drawable.shape_default_circle_bg)
.into(this)
}
fun ImageView.loadCircle(url: String) {
get(url).placeholder(R.drawable.shape_default_circle_bg)
.error(R.drawable.shape_default_circle_bg)
.into(this)
}
fun ImageView.get(url: String): GlideRequest<Drawable> = GlideApp.with(context).load(url)
fun ImageView.get(url: Drawable): GlideRequest<Drawable> = GlideApp.with(context).load(url)
fun Context.toast(str: String) = Toast.makeText(this, str, Toast.LENGTH_LONG).show()
// 当我们需要toast的时候,我们可以这样调用
context.toast("test")
function type: 对应function这么一个类
fun printChar2(str: String, block: String.() -> Char) { // 高阶函数变成函数的扩展
println(str.block())
}
fun printChar1(str: String, block: (String) -> Char) { // string是入参, char是返回值
println(block(str)) // 函数里面调用函数
}
fun String.lastChar(): Char { // 得到最后一个字符
return this.get(this.length - 1)
}
fun main2() {
val lastChar: (String) -> Char = String::lastChar
printChar1("Kotlin") { // 如何传入函数,是在{}中传入的lastChar()函数
lastChar(it)
}
}
3.4 问题: Kotlin的高阶函数使用过吗(平时使用的map,let ,apply ,also之类的都是高阶函数),let、with、apply的适用场景和区别?
说到扩展函数,就不得不提的几个基础库的扩展函数,也叫做作用域函数,它们是:
T.run、T.let、T.also、T.apply 以及run、width两个函数。对于这几个函数的使用刚开始的时候我还是很懵逼的,只是大概了解用法,以及带来的链式调用的便捷
let、with、apply、run、also这几个高阶函数非常相似
let 经常和?.操作符号联合使用,替代IF判断,比如非空判断
let 扩展函数的实际上是一个作用域函数,当你需要去定义一个变量在一个特定的作用域范围内,let函数的是一个不错的选择;let函数另一个作用就是可以避免写一些判断null的操作。
apply 创建对象并初始化设置
run run函数有两种:一个是全局函数、一个是扩展函数
全局函数run 替代 java中 { } 代码作用域
run 函数实际上可以说是let和with两个函数的结合体,run函数只接收一个lambda函数为参数,以闭包形式返回,即返回 lambda 表达式的返回值。
let比较.jpg4.内敛函数inline
@InlineOnly
public inline fun <T> Continuation<T>.resume(value: T): Unit =
resumeWith(Result.success(value))
高阶函数有性能缺陷一般: 会把代码拷贝到引用的地方, 会增加很多代码! 主要是因为lameda, lameda的本质是函数类型的
对象, 会产生多余的对象, 如果是循环里面创建, 就会影响性能
inline: 打标记, 编译的时候, 可以把代码铺平, 不产生临时对象, 减少临时对象的创建!
内敛函数, inline关键字的作用,知不知道编译后Java中如何表示
Inline关键字修饰的函数是内联函数,用于优化函数调用的压栈操作,相当于在编译期间把内联函数的代码拷贝到函数调用处,可以提升性能,但是增加了代码量
LiveData源码里面用到了inline
noinline: 不内敛, 有啥用, 会把高阶函数的功能缩减! 主要作用: 本来可以拿到的对象, 现在获取不到了
crossinline: return的使用的时候, 解除限制,
总结下来就是:
inline 可以让你用内联——也就是函数内容直插到调用处——的方式来优化代码结构,从而减少函数类型的对象的创建;
noinline 是局部关掉这个优化,来摆脱 inline 带来的「不能把函数类型的参数当对象使用」的限制;
crossinline 是局部加强这个优化,让内联函数里的函数类型的参数可以被当做对象使用。
5 .匿名函数
val d = fun(param: Int): String {
return param.toString()
}
它没有名字 , 变成lambda程这样
val b = { param: Int ->
return param.toString()
}
另外 Lambda 的返回值不是用 return 来返回,而是直接取最后一行代码的值:
匿名函数的本质:
因为 Kotlin 的匿名函数不——是——函——数。它是个对象. (匿名函数不是函数)
6. 解释一下泛型的逆变和协变?
泛型:in和out *
限制集合和数组不一样
?extend 上界 用来解除限制------->, out 使用 extend通配符表示可以读,不能写
? super 下界------------------------------> in。 使用 super 通配符表示可以写,不能读
一个代表消费,一个代表产出
如果in操作符在if语句中,则表示判断含义
Kotlin 中 out 相当于 <? extends T>,in 相当于 <? super T>
public interface Continuation<in T> { //
/**
* The context of the coroutine that corresponds to this continuation.
*/
public val context: CoroutineContext
/**
* Resumes the execution of the corresponding coroutine passing a successful or failed [result] as the
* return value of the last suspension point.
*/
public fun resumeWith(result: Result<T>)
}
intercepted扩展函数也是expect的,又得去kotlin仓库里面找jvm相关的实现
// 接口.接口。 接口里面嵌套接口
*代表的含义!
override val key: CoroutineContext.Key<*> get() = Job
网友评论