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强转。

相关推荐
stevenzqzq11 分钟前
Android studio xml布局预览中 Automotive和Autotive Distant Display的区别
android·xml·android studio
QING6181 小时前
Kotlin commonPrefixWith用法及代码示例
android·kotlin·源码阅读
QING6181 小时前
Kotlin groupByTo用法及代码示例
android·kotlin·源码阅读
兰琛6 小时前
Compose组件转换XML布局
android·xml·kotlin
水w8 小时前
【Android Studio】解决报错问题Algorithm HmacPBESHA256 not available
android·开发语言·android studio
隐-梵10 小时前
Android studio进阶教程之(二)--如何导入高德地图
android·ide·android studio
南宫生10 小时前
Java迭代器【设计模式之迭代器模式】
java·学习·设计模式·kotlin·迭代器模式
Kika写代码10 小时前
【Android】界面布局-线性布局LinearLayout-例子
android·gitee
wangz7611 小时前
kotlin,jetpack compose,使用DataStore保存数据,让程序下次启动时自动获取
android·kotlin·datastore·jetpack compose
Thread.sleep(0)12 小时前
WebRTC源码解析:Android如何渲染画面
android·webrtc