前言
这次继续来搞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强转。