Kotlin开发的100个Tips (二)

前言

这次继续来搞kt,接着kt开发的100个Tips,没看过上一篇感兴趣的朋友可以看看。juejin.cn/post/724929...

11. 慎用lateinit

使用lateinit有一个前提,那就是必须保证在使用前进行初始化,不然就会有可能出现报错。特别是多线程的情况下,会更容易发生错误。

类似Int、Boolean这些基础数据类型,其实在使用的时候可以设置一个默认值。而对象可以用var来修饰

csharp 复制代码
var yourObj: YourObj ?= null

如果想使用延时初始化,也可以尝试by lazy,当然,这里说的慎用并不是说代码中就不能出现lateinit,具体还需要根据自己的场景。

12. @JvmOverloads

这个注解多用于自定义view的构造方法,如果我们正常自定义View,需要重写两三个构造方法,而使用@JvmOverloads的话就能很方便的实现自定义View

kotlin 复制代码
class MyView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
) : LinearLayout(context, attrs, defStyleAttr){
    ......
    }

方便是方便,但是使用的时候也不能无脑使用,像有些控件,系统的或者你自定义的view,是有配置defStyleAttr的,这时候你这样使用,defStyleAttr传0,就会导致原本的默认值不生效。

13. kotlin-android-extensions弃用

如果是一些旧项目,可能还会使用kotlin-android-extensions,而kotlin-android-extensions现在是已经被废弃了。

以前用kae的时候开发就很爽,省去了findviewbyid的操作,所以很多人都喜欢。现在google把它废弃了,其实也不建议去使用,如果你把使用kotlin-android-extensions的kotlin代码转成java你就会发现其中的端倪。

源码

kotlin 复制代码
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)

        auto_scroll.duration = 3000
    }

转后的代码

可以看到它其实是会生成一个HashMap来存,这样就会消耗我们的内存。所以从优化的角度看还是不建议直接使用这个方式,而且现在官方也弃用了,如果现在依赖,也会有对应的提示。

可以看出系统推荐我们用databinding,当然你要是自己想findviewbyid也没啥问题

14. 序列化Parcelable

一般我们用Java写Parcelable做序列化的时候,往往要写很多代码,而kotlin中目前的版本可以使用@Parcelize 来快速进行序列化。

首先需要在gradle中引入插件

bash 复制代码
plugins {
    ......
    id("kotlin-parcelize")
}

然后在data中写

kotlin 复制代码
@Parcelize
data class TestData(
    var number: Int
) : Parcelable

我们可以直接看它转成java后的代码,会帮我们生成相应的代码

15. 可变参数的方法

这个就有点像一些脚本语言例如python这些的语法糖,用起来确实挺爽的。

假设我定义一个方法,有3个参数,一个必传参数和两个可传参数

可以看到调用的时候,可以传1个参数,也可以传某个参数,这种写法用起来十分的舒服。

可以转java看看他内部做了什么

这也能体现kotlin的其中一个优点,它帮你把具体的实现包装起来,让你能用少量的代码去实现之前Java要写较多代码的效果。

16. 快速实现单个方法的回调

如果你想做一些异步回调的操作,无需像java一样先定义一个接口,在kt中能比较方便的实现这个效果

这里就转java看了,原理就是它帮你定义一个接口。

17. 函数作为参数使用

既然上面讲到了函数相关的内容,那趁热打铁再讲讲函数作为参数的使用。

这种情况下java不会有这种语法,所以不会这么写,那会导致一些从java转到kotlin的朋友也不会这么写。

比如说我有一个封装方法add,我的另外两个方法都会调用这个方法,按照我们写java的写法,会先定义一个方法add,然后再这两个方法里面再去调用这个方法。我们写了很多年java都是这样写,所以转kt的时候,这种习惯也会带过来,但是kt能有另外一种写法,能把函数当成一个对象去传递,例如

这是一个简单的Demo,所以你可能会觉得和另外的写法差不多,没什么用处。其实在一些复杂的场景里面,这样写能帮你解决掉一些比较难处理的问题,或者说能帮你把一些比较乱的代码变得看上去更合理。

例如我的test1方法里面我要做一些判断,某种场景下要调用aaa方法,某种场景下要调用bbb方法,某种场景下要调用ccc方法,如果用java来写,或者说用java的思路来写,你会传一个场景参数进去,然后在方法里面做if-else操作,例如

kotlin 复制代码
fun test1(scene: Int): Int {
    if (scene == 1){
        return aaa(1,1)
    }else if(scene == 2){
        return bbb(1,1)
    }else {
        return ccc(1,1)
    }
}

类似这样,我这个Demo比较简单可能不是很明显啊,假如说你这个test1方法里面是一些封装某些功能的方法,它是和业务无关的,你这样写,传的scene是业务相关的,那这个代码是不是就不好看了。而如果你写

kotlin 复制代码
fun test1(function: (Int, Int) -> Int): Int {
    return function(1, 1)
}

我调用的地方是业务相关的,传aaa进来,bbb进来,ccc进来,都不会影响我里面的功能,你如果以后业务要改变,scene多加了一个类型,也不会影响我这个方法。

再比如我要把这个方法传给另外一个类的对象,我用这个函数作为参数就能直接传,而你用java的方式要实现就比较麻烦。

我说这条tips的意思是,有些在java中习惯的写法,你可能转kt后不屑于使用这边的东西,觉得按照java的写法也行,其实是也行,只不过kt提供给你的方法更快。

18. 扩展函数的进阶使用

上一篇也有提到扩展函数,但是也只是简单的介绍,其实扩展函数如果用得好的话,对开发效率会有十分大的提升。

例如我们做dp转px,一般都是写一个工具类dp2px,然后在使用的时去调用这个工具类

kotlin 复制代码
fun dip2px(dpValue: Float): Int {
    val scale = Resources.getSystem().displayMetrics.density
    return (dpValue * scale + 0.5f).toInt()
}

而如果做扩展函数,我们可以这样写

kotlin 复制代码
val Float.dp: Int
    get() = dip2px(this)

fun dip2px(dpValue: Float): Int {
    val scale = Resources.getSystem().displayMetrics.density
    return (dpValue * scale + 0.5f).toInt()
}

然后在调用的时候直接调用

kotlin 复制代码
val k: Int = 16f.dp
Log.d("mmp", "$k")

这样写就很方便,可以看看最终的结果

再举个栗子,在kotlin中没有像java一样的三目运算符,所以我们如果根据一个Boolean类型判断一个View的显示和隐藏的话,一般要怎么写

ini 复制代码
view.visibility = if (bol) View.VISIBLE else View.GONE

但是总不能每次都写这么长吧,所以可以做个扩展函数

kotlin 复制代码
fun View.visibilityByBoolean(bol: Boolean) {
    this.visibility = if (bol) View.VISIBLE else View.GONE
}

然后在使用的时候直接调用

bash 复制代码
val test: LinearLayout = findViewById(R.id.test)
test.visibilityByBoolean(false)

看得出这就比较舒服了。

所以如果扩展函数用得好的话,是能很大的提升自身的开发效率。那么这里再留下一个问题,扩展函数的原理是什么?这是面试会可能考的知识哦~~~

19. 带?的的判断

因为有时候定义的对象可为空的,如果是一个Boolean?的类型,可能有些人不知如何做判断,比如

可以看到底部会有个错误提示警告你不能直接这样写。

而为了处理这种情况,有些新人会直接写

csharp 复制代码
var bol : Boolean? = true
if (bol!!){
    // todo
}

其实这种写法不好。要处理这个问题其实很简单,可以这样写

csharp 复制代码
var bol : Boolean? = true
if (bol == true){
    // todo
}

这是一些细节上的处理,像!!的判空,还是尽量不要乱使用,不要为了图个方便,就逮着一个能用的解法就怼进去。

20. 类型强转

这期最后一条tips来个简单点的,kt比较基础的知识 is 用来判断类型, as 用来强转类型,比如说

vbnet 复制代码
val a : Any = 10
if (a is String){
    
}else {
    (a as String).length
}

这样肯定会报错,因为a不是String类型,但是基础数据类型有另外一种转换方式,如果写成这样就不会有问题

kotlin 复制代码
val a : Any = 10
if (a is String){

}else {
    val result = a.toString().length
    Log.d("mmp", "$result")
}

这个栗子不太好,毕竟万物都能toString(),如果我换成这样

kotlin 复制代码
val a = "c"

val result = a.toInt()
Log.d("mmp", "$result")

会报错吗?会报错,因为可以看源码

kotlin 复制代码
public actual inline fun String.toInt(): Int = java.lang.Integer.parseInt(this)

但如果这样写

kotlin 复制代码
val a = "c"

val result = a.toIntOrNull()
Log.d("mmp", "$result")

这样就不会出现强转导致的crash,所以在基础类型转换上,可以用xxxOrNull()来防止出现强转失败导致的crash,如果是非基础类型,可以先用is判断,再用as强转。

相关推荐
软件聚导航2 小时前
uniapp 安卓和ios震动方法,支持息屏和后台震动,ios和安卓均通过测试
android·ios·uni-app
zhangphil2 小时前
Android叠加双RecyclerView ScaleGestureDetector AnimatorSet动态放大缩小,Kotlin(1)
android·kotlin
studyForMokey2 小时前
【Android零碎笔记】
android·笔记
冷眼看人间恩怨3 小时前
【Android】Android Studio打包APK、精简APK大小与规范处理详解
android·ide·android studio·apk
vincent_woo3 小时前
再学安卓 - init进程
android·操作系统
zhoujun7984 小时前
Hybrid APP 移动开发-客户端
android
for_syq4 小时前
Android res复制脚本
android·linux·服务器
WeiComp6 小时前
Android数据存储——文件存储、SharedPreferences、SQLite、Litepal
android·数据库·sqlite·litepal
是店小二呀6 小时前
【C++】C++11新特性详解:可变参数模板与emplace系列的应用
android·c++
忘忧记13 小时前
Python MySQL SQLServer操作
android