kotlin项目开发总结

来源:互联网 发布:ipad音乐软件 方块 编辑:程序博客网 时间:2024/05/19 14:37

前言

最近都是在用Kotlin开发Android项目,总结了一些心得在这里和大家分享

1. 定义变量

kotlin定义变量有三种形式

1)使用var定义可修改变量,最常见的用法,也是很灵活,

private var point: Point? = null//使用的时候,因为point是可空的,所以有两种用法println(point?.x) //如果你不能确保point是否为空println(point!!.x) //如果你能确保point一定不为空,否则point为空,这里会报运行时空指针

//下面用法会报编译时错误,因为point是Point?类型,所以point有可能是null

println(point.x)

从上面的注释,我们可以发现,kotlin从编译时预防了空指针的可能性,VeryGood的特性,能够避免很多人为导致的空指针错误

2)使用val定义不可修改的变量

private val point = Point(20, 20)

这种情况下point一定不会为空,因为val定义的变量必须要初始化,从这个角度来看,又避免了人为导致的空指针错误
比如下面会产生编译时错误

point = Point(30, 30)

需要注意的是,point不能被修改,但是Point类里面的成员变量是可修改的,所以下面操作是允许的

point.x = 30point.y = 30

3)使用lateinit var,可以使得变量的初始化可以延迟到需要的时候
比如在使用dagger的时候,需要inject,如下

@Inject lateinit var mPresenter: MapHomePresent

但是需要慎用lateinit, 因为你可能后面忘记了初始化,但是编译器又不会报错提醒。只有运行之后才会检测到,如下

fun main(args: Array<String>) {    var test = Test()    println(test.a)}class Test {    lateinit var a: String}

会报错如下:

Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property a has not been initialized    at Test.getA(Simplest version.kt:14)    at Simplest_versionKt.main(Simplest version.kt:10)

2.定义方法

注意下面是两种类型

var a: String? = nullvar b: String = null

b可以自动转换成a, a需要使用a!!转换成b,这种在定义方法的时候特别有用,如下举例

fun test(a: String) {
a.apply{
print(this)
}
}

上面这个方法在传入参数时,必须保证这个参数不为空,比如下面会报编译错误

var temp: String? = nulltest(temp)//这里会报编译错误,L类型自动转换test(temp!!)//ok,但是需要在别的地方把temp重新赋值为非null

反过来,下面是可以调用的

fun test(a: String?) {    a?.apply{        print(this)    }}var temp: String = null test(temp)//会自动转换

3. 通过data class 定义entity

可以看下data class的定义,它是专门用来存储数据的,很适合entity的场景,比如从服务器拉取数据。

4. 多使用Kotlin提供的标准函数

比如使用apply函数,可以使得代码看起来非常简洁

mOption.apply {     locationMode = AMapLocationClientOption.AMapLocationMode.Hight_Accuracy//可选,设置定位模式,可选的模式有高精度、仅设备、仅网络。默认为高精度模式     isNeedAddress = false     /**      * 设置是否优先返回GPS定位结果,如果30秒内GPS没有返回定位结果则进行网络定位      * 注意:只有在高精度模式下的单次定位有效,其他方式无效      */     isGpsFirst = false // GPS优先会导致反应速度很慢     // 设置是否开启缓存     isLocationCacheEnable = true     // 设置是否单次定位     isOnceLocation = true     //设置是否等待设备wifi刷新,如果设置为true,会自动变为单次定位,持续定位时不要使用     isOnceLocationLatest = true     //设置是否使用传感器     isSensorEnable = true     interval = 1000     // 设置网络请求超时时间     httpTimeOut = 60000     //设置是否开启wifi扫描,如果设置为false时同时会停止主动刷新,停止以后完全依赖于系统刷新,定位位置可能存在误差     isWifiScan = true}

对比下,下面的普通用法

mOption.locationMode = AMapLocationClientOption.AMapLocationMode.Hight_Accuracy//可选,设置定位模式,可选的模式有高精度、仅设备、仅网络。默认为高精度模式mOption.isGpsFirst = false//可选,设置是否gps优先,只在高精度模式下有效。默认关闭mOption.httpTimeOut = 30000//可选,设置网络请求超时时间。默认为30秒。在仅设备模式下无效mOption.interval = 2000//可选,设置定位间隔。默认为2秒mOption.isNeedAddress = true//可选,设置是否返回逆地理地址信息。默认是truemOption.isOnceLocation = false//可选,设置是否单次定位。默认是falsemOption.isOnceLocationLatest = false//可选,设置是否等待wifi刷新,默认为false.如果设置为true,会自动变为单次定位,持续定位时不要使用AMapLocationClientOption.setLocationProtocol(AMapLocationClientOption.AMapLocationProtocol.HTTP)//可选, 设置网络请求的协议。可选HTTP或者HTTPS。默认为HTTPmOption.isSensorEnable = false//可选,设置是否使用传感器。默认是falsemOption.isWifiScan = true //可选,设置是否开启wifi扫描。默认为true,如果设置为false会同时停止主动刷新,停止以后完全依赖于系统刷新,定位位置可能存在误差mOption.isLocationCacheEnable = true //可选,设置是否使用缓存定位,默认为true

一对比发现,上面这种写法真的是很简洁

5. 巧用“?:”

?: 的意思是,左边的表达式没有成功,则使用右边的结果;如下,person是null,所以person?.name不会执行,所以最终a == "null"

var person: Person? = nullvar a = person?.name ?: "null"

配合闭包也可以使用,如下代码:

screenMarker?.apply {   val point = aMap!!.projection.toScreenLocation(position)   point.y -= SizeUtils.sp2px(125f)   val target = aMap!!.projection.fromScreenLocation(point)   val animation = TranslateAnimation(target)   animation.setInterpolator { input ->       // 模拟重加速度的interpolator       if (input <= 0.5) {           (0.5f - 2.0 * (0.5 - input) * (0.5 - input)).toFloat()       } else {           (0.5f - Math.sqrt(((input - 0.5f) * (1.5f - input)).toDouble())).toFloat()       }   }   //整个移动所需要的时间   animation.setDuration(600)   //设置动画   setAnimation(animation)   //开始动画   startAnimation()}?:KLog.d(TagObject.TAG, "screenMarker is null")

是不是看上去,代码连贯性很强。

6. 使用companion object

当你的类包含太多的东西,你想把它们隔离到另外一个类,又不想使用类引用的方式,你就可以使用companion object,如下

class AndroidFragment : MainFragment() {    override fun getAdapter(list: ArrayList<FuckGoods>): BaseBindingAdapter<*> {        return FuckGoodsAdapter(list)    }    override fun getType(): String {        return ANDROID    }    //companion object的好处是,外部类可以直接访问对象,不需要通过对象指针    companion object {        val ANDROID = "ANDROID"        fun newInstance(): Fragment {            val fragment = AndroidFragment()            val bundle = Bundle()            fragment.arguments = bundle            return fragment        }    }}

可以结合一起使用,又能实现代码分离,增强代码的可读性

7. 通过闭包减少接口类

有些时候你不想使用定义新的接口去实现回调,那就可以考虑使用闭包。如下代码:

protected fun <T> submit(observable: Observable<JsonResult<T>>, block: (T) -> Unit) {        addDisposable(              observable.observeOn(AndroidSchedulers.mainThread())                   .subscribe(                           {                               if(it != null && !it.error && it.results != null) {                                   block(it.results)                               }else if (it == null){                                   KLog.d(TagObject.TAG_Release, "result is null")                               }else {                                   KLog.d(TagObject.TAG_Release, "res.error:" + it.error + ",res.results:" + it.results)                               }                           },                           {                               KLog.d(TagObject.TAG_Release, "error android Presenter" + it.message)                           }                   )      )}

调用如下:

submit(mModel.getData(page, type)){   view.setData(it)}

这样写代码,会显示非常简洁,也避免了创建大量的接口回调类。

8. Java类转换成kotlin

首先选中Java类,然后按ctrl+shift+A,弹出一个“enter action or option name”的对话框,然后输入”convert Java file to kotlin file”,就像下面的截图:
这里写图片描述

点击“Convert java File to Kotlin File”(也可以使用快捷键),IDE就自动帮我们把Java文件转换成kotlin, 一般情况下,转换出来的文件,但是不排除需要做一些修改。但是相对来说也省事多了