Android知识体系梳理笔记五:Kotlin学习笔记二:空安全,操作符,Lambad表达式...
2017-09-14 15:48
633 查看
前言
已经用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 函数的参数类型匹配的上。
此功能仅限于Java互操作,只适用与 Kotlin 中对 java 的调用,而不支持对 Kotlin 的调用
只支持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 依赖注入框架在使用注解类框架中,要使用Kotlin注解类插件:apply plugin: ‘kotlin-kapt’
依赖:
//dagger kapt 'com.google.dagger:dagger-compiler:2.11' compile 'com.google.dagger:dagger:2.11'
在module注解的类中,提供对象的函数的返回类型必须是相对应的类名,在Java中,这个函数的返回类型可以使其实现的父类或接口,而在Kotling中不可以
//java @PerActivity @Provides public BaseQuickAdapter provideManageAdapter() { return new ManageAdapter(mView); }
//kotlin @PerActivity @Provides fun providesAdapter():ManageAdapter{ return ManageAdapter(mView) }
如果还有错误,可以看AndroidStudio的EventLog窗口,具体问题具体分析
结语
最近开始有新项目,恰好这个学习项目的一个模块写完了,就先总结一波学到的知识。Kotlin确实简洁方便,缺点也有,但是多掌握一点技术也是不错的,毕竟人生(矮油,不错哦)。。。Github
拼搏在技术道路上的一只小白And成长之路
相关文章推荐
- Android知识体系梳理笔记五:Kotlin学习笔记一:类和继承以及Anko(全)的基本使用
- Android知识体系梳理笔记四:组件化开发学习笔记
- Android知识体系梳理笔记一:Android跨进程通信:AIDL
- Android知识体系梳理笔记二:AIDL进阶之Binder机制
- 【Android开发新手的学习笔记】异步加载知识梳理
- 《Android 开发艺术探索》——View 事件体系--View基础知识 (学习笔记)
- Android Map开发基础知识学习笔记
- Android Map开发基础知识学习笔记
- [linux学习笔记]第3天:变量分类,重定向,管道命令,程序执行流,文本处理类命令, 正则表达式,短路操作符
- WCF学习笔记之基础知识梳理(1)
- Linux安全体系学习笔记之四:OpenSSL源代码分析(3)
- ruby编程语言-学习笔记2(第4章 表达式和操作符)
- Linux安全体系学习笔记之二:OpenSSL源代码分析(1)
- ruby编程语言-学习笔记3(第4章 表达式和操作符)
- Android Map开发基础知识学习笔记
- Android Map开发基础知识学习笔记
- Android Map开发基础知识学习笔记(转)
- Android Map开发基础知识学习笔记
- 邮件安全知识学习笔记
- Android Map开发基础知识学习笔记