Android知识体系梳理笔记五:Kotlin学习笔记二:空安全,操作符,Lambad表达式...

前言

已经用kotlin完成了项目中的一个模块,现在总结一下在项目中学到的知识(但想学的很好,要书籍和代码结合到一起才行)

空安全

NullPointerException这异常相信大家都见到过,虽然NPE(NullPointerException)解决起来很简单,但是一出现异常就让人很不爽的对不对,而Kotlin 的类型系统旨在从我们的代码中消除 NullPointerException。zaikotlin中出现NPE 的唯一可能的原因可能是下面几条:

  • 显式调用 throw NullPointerException()
  • 使用了 !! 操作符
  • 外部 Java 代码导致的
  • 对于初始化,有一些数据不一致(如一个未初始化的 this 用于构造函数的某个地方)

PS: 这里有一条可以在JAVA中很快的确定NPE的小技巧:再出现NPE的语句里,绝大部分情况下都是调用者为空。例如:xxx.hhh.getyyy().get() 都是 “.” 前面的语句为空

避免NPE和空安全检查

Kotlin 的类型系统旨在从我们的代码中消除 NullPointerException,那么他是怎么消除的呢

  • 空判断

可以和java中一样的进行null条件判断

if (b != null&&b.length > 0 ) {
    return b.length
} else {
    return null
}
  • ?操作符
//如果 b 非空,就返回 b.length,否则返回 null,这个表达式的类型是 Int?
b?.length

像上面PS中的xxx.hhh.getyyy().get()我们可以写成xxx?.hhh?.getyyy()?.get(),只要其中一个为null就会返回null,而不出现NPE

  • let操作符

如果一个数据中有空和非空,而只要对非空值执行某个操作,可以使用let操作符

val listWithNulls: List<String?> = listOf("A", null)
for (item in listWithNulls) {
     item?.let { println(it) } // 输出 A 并忽略 null
}
  • Elvis 操作符

这个操作符实际上和Java中的三目操作符很像,只不过一个做的是空判断,一个做的是Boolean判断

val l = b?.length ?: -1

如果 “?:”左侧的表达式不为空,Elvis 操作符就会返回该表达式的值,如果为空则使用其右边的值

  • !!操作符
    如果需要的值一定不为空,可以用!!标识它
val l = b!!.length

如果b为空,则会抛出一个NPE

修饰符和操作符

这里只提一下在项目中用到的修饰符,修饰符,操作符大全

  • vararg 允许一个参数传入可变数量的参数
  • in 指定在 for 循环中迭代的对象;
    用作中缀操作符以检查一个值属于一个区间、 一个集合或者其他定义“contains”方法的实体;
    在 when 表达式中用于上述目的;将一个类型参数标记为逆变
 //vararg 表示参数个数可变,多参数
    fun gone(vararg views:View){
        for(view in views){
             view?.visibility=View.GONE
        }
    }
  • val 声明一个只读属性或局部变量
  • var 声明一个可变属性或局部变量
  • when 开始一个 when 表达式(执行其中一个给定分支)相当于Java中的switch语句,但比switch要强大很多
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
        when(ev?.action){
            MotionEvent.ACTION_DOWN->
                  doSomeing()
            MotionEvent.ACTION_MOVE-> 
                  doSomeing()
            MotionEvent.ACTION_UP,MotionEvent.ACTION_CANCLE-> 
                  doSomeing()

        }
        return super.dispatchTouchEvent(ev)
    }

特定类型转换检查

when(view){
           is TextView->
                  doSomeing()
           is Button-> 
                  doSomeing()
           is listView-> 
                  doSomeing()
        }

检测一个值在(in)或者不在(!in)一个区间或者集合中

when (x) {
    in 1..10 -> print("x is in the range")
    in validNumbers -> print("x is valid")
    !in 10..20 -> print("x is outside the range")
    else -> print("none of the above")
}

when 也可以用来取代 if-else if链。 如果不提供参数,所有的分支条件都是简单的布尔表达式,而当一个分支的条件为真时则执行该分支:

when {
    x.isOdd() -> print("x is odd")
    x.isEven() -> print("x is even")
    else -> print("x is funny")
}
  • object 同时声明一个类及其实例

要创建一个继承自某个(或某些)类型的匿名类的对象(可以是类也可以是接口)

photoSetAdapter.setTapListener(object :PhotoSetAdapter.OnTapListener{
            override fun onPhotoClick() {
              ...
            }
        })

还可以创建单例:这称为对象声明。并且它总是在 object 关键字后跟一个名称。 就像变量声明一样,对象声明不是一个表达式,不能用在赋值语句的右边。

object DataProviderManager {
    fun registerDataProvider(provider: DataProvider) {
        // ……
    }

    val allDataProviders: Collection<DataProvider>
        get() = // ……
}

...
DataProviderManager.registerDataProvider(……)

类内部的对象声明可以用 companion 关键字标记:这使得其标记的内容看起来像其他语言的静态成员(就是在其里面声明的方法和成员变量可以用类名调用),在运行时他们仍然是真实对象的实例成员,而且,例如还可以实现接口

    companion object {
           lateinit var instance: AppApplication
    }

    override fun onCreate() {
        super.onCreate()
        instance = this
        mContext = applicationContext
        init()
    }

    ...
    //在其他类里可以使用
    AppApplication.instance.getXXX()

如果想要把方法和成员变量变成真正的静态成员,在 JVM 平台,可以使用 @JvmStatic 注解

companion object {
//@JvmField 标注这样的属性使其成为与属性本身具有相同可见性的静态字段。
        @JvmField
        val COMPARATOR: Comparator<Key> = compareBy<Key> { it.value }
    }
    companion object {
    //将这些函数标注为 @JvmStatic 的话。 编译器既会在相应对象的类中生成静态方法,也会在对象自身中生成实例方法。:
        @JvmStatic fun foo() {}
        fun bar() {}
    }

lambda 表达式

  • lambda 表达式总是被大括号括着
  • 其参数(如果有的话)在 -> 之前声明(参数类型可以省略)
  • 函数体(如果存在的话)在 -> 后面

要说清楚lambda 表达式,不得不说一下Kotlin的高阶函数和匿名函数

  • 高阶函数:高阶函数是将函数用作参数或返回值的函数。使用形式:
fun <T> lock(lock: Lock, body: () -> T): T {
    lock.lock()
    try {
        return body()
    }
    finally {
        lock.unlock()
    }
}

上面的的body为函数参数名,“()”里面的内容就是body这个函数需要的参数,什么都没有表示lock函数里面的这个body是一个无参函数,”->”右边的T表示body函数返回值的类型。而这个body函数还可以继续简化,这就是下面说的匿名函数。

  • 匿名函数:匿名函数看起来非常像一个常规函数声明,除了其名称省略了。其函数体可以是表达式或代码块:
fun(x: Int, y: Int): Int {
    return x + y
}

考虑这样一个高阶函数:max(…)

fun compare(x: String, y: String): Boolean {
    return x.length < y.length
}
max(strings, compare:(x,y)->Boolean)

可以使用匿名函数简化一下

max(strings, (x,y)-> {x.length < y.length})

这个匿名函数的参数类型和返回值类型虽然都没有写明,但是kotlin的特性就是类型可推断;
然后这个函数还可以再简化一下,那就是lambda 表达式了

  • lambda 表达式(简单化的函数体):根据上面的3条特性
//上面一条代码可以变为
max(strings, {x,y->x.length<y.length})

如果一个函数接受另一个函数作为最后一个参数,lambda 表达式参数可以在圆括号参数列表之外传递。 so:

//上面一条代码可以变为
max(strings) {x,y->x.length<y.length}

如果高阶函数只有一个函数最为参数值,则这个高阶函数的括号部分可以省略:

//max函数只有一个函数参数
max{x,y->x.length<y.length}

如果 lambda 表达式的参数未使用,那么可以用下划线取代其名称:

//max函数只有一个函数参数
max{_,y->println("$y!")}
  • it操作符,单个参数的隐式名称:如果lambda 表达式只有一个参数, 那么它的声明可以省略(连同 ->),其名称是 it:
//max函数只有一个函数参数,而这个函数参数只有一个参数
max{x->println("$x!")}

则可以这样

max{println("$it!")}
  • 带接收者的lambda 表达式:Kotlin 提供了使用指定的 接收者对象 调用函数字面值的功能。 在函数字面值的函数体中,可以调用该接收者对象上的方法而无需任何额外的限定符。 这类似于扩展函数,它允许你在函数体内访问接收者对象的成员。
sum : Int.(other: Int) -> Int

可以这样使用

1.sum(2)
  • SAM(单抽象方法) 转换:Kotlin 支持 SAM 转换,这意味着 Kotlin lambad表达式可以被自动的转换成 只有一个非默认方法的 Java 接口的实现,只要这个方法的参数类型 能够跟这个 Kotlin 函数的参数类型匹配的上。
    1. 此功能仅限于Java互操作,只适用与 Kotlin 中对 java 的调用,而不支持对 Kotlin 的调用
    2. 只支持Java接口,接口只有一个抽象方法,不支持抽象类
//未使用lambad表达式
Observable.just(...)
          .map (object :Function<NewsInfo,NewsMultiItem>{
                    override fun apply(t: NewsInfo): NewsMultiItem { //在这里不能在skipType使用'!!'符号,因为请求到的skipType字段可能为空,所以'?',?,!!区别
                        if (NewsUtils.isNewsPhotoSet(t.skipType)){
                            return NewsMultiItem(t,NewsMultiItem.NEWS_INFO_PHOTO_SET)
                        }
                        return NewsMultiItem(t,NewsMultiItem.NEWS_INFO_NORMAL)
                    }
                })
//使用lambad表达式
Observable.just(...)
          .map (Function<NewsInfo, NewsMultiItem> { 
          //t->是可以省略的,下面可以改成it
          t ->
                    //在这里不能在skipType使用'!!'符号,因为请求到的skipType字段可能为空,所以'?',?,!!区别
                    if (NewsUtils.isNewsPhotoSet(t.skipType)){
                        return@Function NewsMultiItem(t,NewsMultiItem.NEWS_INFO_PHOTO_SET)
                    }
                    NewsMultiItem(t,NewsMultiItem.NEWS_INFO_NORMAL)
                })

集合

Kotlin 区分可变集合和不可变集合(lists、sets、maps 等)。精确控制什么时候集合可编辑有助于消除 bug 和设计良好的 API。

  • 不可变集合:Kotlin 的 List 类型是一个提供只读操作如 size、get等的接口。和 Java 类似,它继承自 Collection 进而继承自 Iterable。

  • 可变集合:改变 list 的方法是由 MutableList 加入的。这一模式同样适用于 Set/MutableSet 及 Map

一些框架在Kotlin中遇到的坑

  • dagger2 依赖注入框架
    1. 在使用注解类框架中,要使用Kotlin注解类插件:apply plugin: ‘kotlin-kapt’
      依赖:
//dagger
    kapt 'com.google.dagger:dagger-compiler:2.11'
    compile 'com.google.dagger:dagger:2.11'
  1. 在module注解的类中,提供对象的函数的返回类型必须是相对应的类名,在Java中,这个函数的返回类型可以使其实现的父类或接口,而在Kotling中不可以
//java
@PerActivity
 @Provides
    public BaseQuickAdapter provideManageAdapter() {
        return new ManageAdapter(mView);
    }
//kotlin
 @PerActivity
    @Provides
    fun providesAdapter():ManageAdapter{
        return ManageAdapter(mView)
    }
  1. 如果还有错误,可以看AndroidStudio的EventLog窗口,具体问题具体分析

结语

最近开始有新项目,恰好这个学习项目的一个模块写完了,就先总结一波学到的知识。Kotlin确实简洁方便,缺点也有,但是多掌握一点技术也是不错的,毕竟人生(矮油,不错哦)。。。

Github

拼搏在技术道路上的一只小白And成长之路

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值