kotlin项目开发总结

前言

最近都是在用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 = 30
point.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? = null
var b: String = null

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

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

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

var temp: String? = null
test(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//可选,设置是否返回逆地理地址信息。默认是true
mOption.isOnceLocation = false//可选,设置是否单次定位。默认是false
mOption.isOnceLocationLatest = false//可选,设置是否等待wifi刷新,默认为false.如果设置为true,会自动变为单次定位,持续定位时不要使用
AMapLocationClientOption.setLocationProtocol(AMapLocationClientOption.AMapLocationProtocol.HTTP)//可选, 设置网络请求的协议。可选HTTP或者HTTPS。默认为HTTP
mOption.isSensorEnable = false//可选,设置是否使用传感器。默认是false
mOption.isWifiScan = true //可选,设置是否开启wifi扫描。默认为true,如果设置为false会同时停止主动刷新,停止以后完全依赖于系统刷新,定位位置可能存在误差
mOption.isLocationCacheEnable = true //可选,设置是否使用缓存定位,默认为true

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

5. 巧用“?:”

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

var person: Person? = null
var 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): 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  submit(observable: Observable>, 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”,就像下面的截图:
kotlin项目开发总结_第1张图片

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

你可能感兴趣的:(kotlin,kotlin)