Kotlin在实际项目中的使用小结

文章目录

        • 一.Kotlin属性
          • 1.默认方法
          • 2.get 和set属性
          • 3.get /set 方法使用注意事项
        • 二.基本类型
        • 三.when表达式
        • 四.var、val、lateinit、by lazy的使用
        • 五. !!.和?.的区别
        • 六.Anko用于执行后台任务
        • 七.with 、apply、let、run、takeif等函数的使用场景总结
          • 1.with
          • 2.apply
          • 3.let
          • 4.run
          • 5.takeIf和takeUnless
        • 八 、类型别名(Type alias)的使用
          • 1.为集合类型(collection type)提供别名
          • 2.为函数类型(function type)提供别名(alias)
          • 3.为内部类(inner)和嵌套类(nested)创建别名

一.Kotlin属性

1.默认方法
    var uuid:String
        get() = CacheTool.localCache.getString(UUID)?:""
        set(value) {
            CacheTool.localCache.put(UUID,value)
        }
2.get 和set属性

如不想对外公开某个方法时,可以使用修饰符private实现,如:

// 是否加载失败
var isLoadFail = false
	private set

设置 set 属性为private 后, 那么它将只在同一个源代码文件内可以访问,在其他作用域下 如其他包名下不能访问。

注意:这种方式只适用于 set 方法, get的访问权限默认和属性是一致的,如下面的使用会编译错误

var isLoadFail = false
	private get  //// 编译错误,get的访问权限和属性一致
3.get /set 方法使用注意事项
var isSelected: Boolean = false
        set(value) {
            isSelected = value
            invalidate()
        }

这段代码会使得循环调用,最终ANR,正确的写法应该是:

var isSelected: Boolean = false
        set(value) {
            field = value
            invalidate()
        }

二.基本类型

Double类型值 如:123.5, 123.5e10

Float值需要用f或F标识:123.5f

Long类型需要大写的L来标识:123L

在数字字面值中添加下划线,提高可读性。(kotlin1.1开始支持)

val oneMillion = 1_000_000
val creditCardNumber = 1234_5678L

三.when表达式

如果对多种条件需要进行相同的处理,那么可以对一个分支指定多个条件,有逗号分隔:

when (x){
    0,1 -> print("x==0 or x==1")
    else -> print("otherwise")
}

四.var、val、lateinit、by lazy的使用

var 是一个变量,初始化的值可以修改,val 是一个常量,只读。

lateinit 和lazy是两种不同的延迟初始化

lateinit 只用于变量var ,lazy只用于常量val

lateinit 不能用于可空的属性上和java的基本类型

private val money: Int by lazy {1} //正确
private lateinit val test:String  //正确

lateinit val test:String //error
lateinit val test:Float //error

lazy 应用于单例模式 (if-null-then-init-else-return),而且当且仅当常量被第一次调用的时候,委托方法才会被执行。

实际项目中运用举例:

1.比如这样的常见操作,只获取,不赋值(val类型),并且多次使用的对象

private val mUserManager : UserManager by lazy {
    UserManager.getInstance()
} 

2.再比如 Activity中控件的初始化,一般传统的进入界面就初始化所有的控件,而使用懒加载,只有用到时才会对控件初始化

//kotlin 封装:
fun  Activity.bindView(id: Int): Lazy = lazy {
    viewFinder(id) as V
}
 
//acitivity中扩展调用
private val Activity.viewFinder: Activity.(Int) -> View?
    get() = { findViewById(it) }
 
//在activity中的使用姿势
val mTextView by bindView(R.id.text_view)
mTextView.text="执行到我时,才会进行控件初始化"

lateinit则用于只能在生命周期流程中进行获取或初始化的变量。有一些在 Android 中某些属性需要在 onCreate() 方法中初始化。

private lateinit var mAdapter: RecyclerAdapter<Transaction>

override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   mAdapter = RecyclerAdapter(R.layout.item_transaction)
}

//或者类似这样的
private lateinit var vehicleListDialog: VehicleListDialog

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
     
     initVehicleListDialog()
    }   
private fun initVehicleListDialog() {
        var dialog = childFragmentManager.findFragmentByTag(VehicleListDialog.TAG) as 						VehicleListDialog?
        if (dialog == null) {
            dialog = VehicleListDialog()
        }
    	//进行初始化
        vehicleListDialog = dialog
    }

再比如:

class App:Application(){
    init {
        instance = this
    }
    
    @Inject lateinit var apiComponent: ApiComponent
    override fun onCreate() {
        super.onCreate()
        					DaggerApiComponent.builder().apiModule(ApiModule()).appModule(AppModule(this)).build().inject(this)

    companion objectf{
        lateinit var instance:App
    }
}

注意:要慎用,使用延迟初始化时 要注意,变量的使用前需要初始化,否则会报错。

如:使用对象判断是否初始化

private lateinit var avatarFile: File
//是否初始化
private fun isAvaFileInitialzed():Boolean = ::avatarFile.isInitialized

五. !!.和?.的区别

!!.和?.都是Kotlin提供的检测空指针的方法。

“?”用来明确指定一个对象,或者一个属性变量是否可以为空。

private var mContext:Context? = null

"?"加在变量名后,系统在任何情况不会报它的空指针异常。
"!!"加在变量名后,如果对象为null,那么系统一定会报异常!

?.
//kotlin
foo?.run()

//与java相同
if(foo != null){
    foo.run()
}//不会空指针

!!.
//kotlin
foo!!.run()

//与java机同
if(foo != null){
    foo.run();
}else{//会空指针
    throw new KotlinNullPointException();
}

六.Anko用于执行后台任务

搭配 Anko lib 使用。后台和主线程的切换特别直观和简单。uiThread 在主线程上运行,并且我们不需要关心 Activity 的生命周期(pause 与 stop), 所以也不会出错了。

如果一个Activity调用doAsync,那么如果该Activity消亡(isFinishing返回true)uiThread代码是不会执行的。这样,我们就避免了AsyncTask经常出现的错误或其他没有注意activity生命周期的任何回调函数。


doAsync{
    //后台执行
    var result = getResult()
    //回到主线程
    uiThread{
        toast(result)
    }
}

项目中运用实例:

如在微信支付中,接收支付成功或失败的回调事件。

doAsync {
            WeChatPay.wxApi.handleIntent(intent, object : IWXAPIEventHandler {
                override fun onReq(req: BaseReq) {}

                override fun onResp(resp: BaseResp) {
                    uiThread {
                        if (resp.type == ConstantsAPI.COMMAND_PAY_BY_WX) {
                            handlePayResponse(resp)
                        }
                        finish()
                    }
                }
            })
        }

七.with 、apply、let、run、takeif等函数的使用场景总结

1.with

定义:

funwith(receiver:T,block:T.() ->R):R

功能:将对象作为函数的参数,在函数内可以通过this指代该对象。返回值为函数的最后一行或return 表达式。

实例1:

    /**
     * 画笔对象的引用
     */
    private var paint = Paint()
  		paint.strokeCap = Paint.Cap.ROUND
        paint.isAntiAlias = true // 消除锯齿
        paint.isDither = true //防止抖动
        paint.color = roundColor // 设置圆环的颜色
        paint.style = Paint.Style.FILL // 设置空心
        paint.strokeWidth = roundWidth // 设置圆环的宽度

    //用with后更自然
 	private var paint = Paint()
       with(paint) {
           //this指代Paint对象,此处可以省略
            strokeCap = Paint.Cap.ROUND
            isAntiAlias = true // 消除锯齿
            isDither = true //防止抖动
            color = roundColor // 设置圆环的颜色
            style = Paint.Style.FILL // 设置空心
            strokeWidth = roundWidth // 设置圆环的宽度
        }

实例2:

var list = mutableListOf<Int>()
list.add(1)
list.add(2)

//用with后 
var list = with(mutableListOf<Int>()){
    add(1)
    add(2)
    this
}

2.apply

定义:

fun T.apply(block:T.() ->Uint):T

功能:调用对象的apply函数,在函数范围内,可以任意调用该对象的任意方法,并返回该对象。

实例:

var list = mutableListOf<Int>().apply{
    add(1)
    add(2)
}


val paint = Paint().apply {
         strokeCap = Paint.Cap.ROUND
         isAntiAlias = true // 消除锯齿
         isDither = true //防止抖动
         color = roundColor // 设置圆环的颜色
         style = Paint.Style.FILL // 设置空心
         strokeWidth = roundWidth // 设置圆环的宽度
     }

此外由于apply函数返回的是其对象本身,那么可以配合?.完成多级的非空判断操作,或者用于建造者模式的Builder中。

3.let

定义:

fun <T,R> T.let(block:(T) -> R):R

功能:调用对象(T)的let函数,则该对象为函数的参数。在函数内可以通过 it 指代该对象。返回值为函数的最后一行或指定return表达式。

实例:有点类似于run(),let在使用中可用于空安全验证,变量?.let{}例如

val bean = listParkingVehicleEntity.find { it.plateNo == plateNo }
        bean?.let {
            parkingVehicleEntity = bean
            showUserVehicle(it)
        }
4.run

定义:有两种

fun <R> run(block: () -> R): R 
fun <T, R> T.run(block: T.() -> R): R

功能:执行传入的函数式,并返回函数的执行结果。run的主要目的是强调需要执行的函数。函数内可通过this引用当前对象; 返回值为最后一行, 可以与当前对象类型不同

实例:

intentEXTRA_URL的值,不为非空且内容不为空,赋值给url。否则弹出提示并关闭页面。

url = intent.getStringExtra(EXTRA_URL)?.takeIf{it.isNotEmpty()}?:run{
    toast("不能浏览一个空链接哦")
    activity.finish()
}

"kotlin".run{"Hello $this"}
>>> Hello kotlin

小结:run()、with(T)、T.run()、 T.let()、 T.apply() 、T.also()选择时的流程图参考如下:

Kotlin在实际项目中的使用小结_第1张图片

5.takeIf和takeUnless

定义:

/**
 * Returns `this` value if it satisfies the given [predicate] or `null`, if it doesn't.
 */
fun  T.takeIf(predicate: (T) -> Boolean): T?

功能:传递一个函数参数,如果函数结果为true,返回T对象,否则返回null。

实例1:

"123".takeIf {it.length > 10}

//返回值为null

//takeUnless与takeIf相反,参数函数返回false时返回T对象,否则返回null
"123".takeUnless{it.length > 10}
//返回值为123

实例2:

var file = File("filePath")
if(file.exists()){
    //do something
}else{
    return false
}

//引入takeIf 后
var file  = File("filePath").takeIf{it.exists()}?:return false
//do something

八 、类型别名(Type alias)的使用

自kotlin 1.1起,类型别名(Type alias)为现有类型提供替代名称,
如果类型名称太长,可引入较短别名替代原类型名。

1.为集合类型(collection type)提供别名
//缩短较长泛型类型(generic type)是很有吸引力的
typealias NodeSet = Set<Network.Node>
typealias FileTable<K> = MutableMap<K, MutableList<File>>
2.为函数类型(function type)提供别名(alias)
typealias MyHandler = (Int, String, Any) -> Unit
typealias Predicate<T> = (T) -> Boolean
3.为内部类(inner)和嵌套类(nested)创建别名
class A{
    inner class Inner
}
class B{
    inner class Inner
}
typealias AInner = A.Inner
typealias BInner = B.Inner

提示:
类型别名不会引入新类型,等效于相应底层类型,编译器会把别名翻译为原有类型:

//添加别名声明typealias Predicate后,Kotlin编译器总是把它扩展为(Int) -> Boolean     
typealias Predicate<T> = (T) -> Boolean
    fun foo(p: Predicate<Int>) = p(42) 
    
    fun main(args: Array<String>) {
        //类型别名和原有类型,可以相互替代,因为编译器会把别名翻译为原有类型
        val f: (Int) -> Boolean = { it > 0 }
        println(foo(f)) // 输出 "true"

        val p: Predicate<Int> = { it > 0 }
        println(listOf(1, -2).filter(p)) // 输出 "[1]"
    }

实例运用:如在网络请求中,有时为了命名的规范性,可以适当引入别名来实现。

typealias CZGetRequest = GetRequest

typealias CZPostRequest = PostRequest

参考:

1.慎用延迟初始化(lazy initialization)

2.Kotlin with 、apply等函数的使用场景总结

3.Kotlin官方文档介绍Type aliases

你可能感兴趣的:(Kotlin)