“确认过“踩过坑”,遇上对的 kotlin

Num 1:方法入参是常量,不可修改

Java 与 Kotlin 互操中,Java 程序员会有点不适应

class Main {    /**     * Kotlin 入参是常量     */    fun print(a: Int = 1, b: String = "") {        // a = 10; // 错误:Val cannot be reassigned!!!    }}

Num 2:不要 Companion、INSTANCE?

Java 访问 Kotlin 中定义的静态变量以及静态方法,需要 Companion。例如:

// Main.kt
class Main {  companion object {      val EMPTY = ""      fun isEmpty(string: String = EMPTY) {          //todo code      }            @JvmField      val FULL_NUMBER = "1234567890"      @JvmStatic      fun isNumber(string: String = FULL_NUMBER) {          //todo code      }  }}
// Test.java
class Test {  public static void main(String[] args) {      // Java 访问 Kotlin 中的常量      Keng.Companion.getEMPTY();      Keng.Companion.isEmpty("");            KengInstance.INSTANCE.getEMPTY();            // Java 访问 Kotlin 中带有 JvmField 修饰的常量,无需 Companion      String FULL_NUMBER = Keng.FULL_NUMBER;      // Java 访问 Kotlin 中带有 JvmStatic 修饰的方法,无需 Companion      Keng.isNumber("");  }}

不想使用 Companion@JvmField@JvmStatic 注解了解一下。

在 Kotlin object Main{...} 定义的静态对象依旧适用。

这些注解,特别推荐在Kotlin中使用,它们让Java与Kotlin互操,如丝般顺滑,没有任何一点点改变,就当什么都没发生过一样。

Num 3:Java 重载,在 Kotlin 中怎么巧妙过渡一下?

Kotlin 调用 Kotlin 中的方法,如果有默认参数,是可以不传递参数的。Java 与 Kotlin 互操中,好像还是需要传?

例如:isNumber(string: String = FULL_NUMBER)

// Test.java
class Test {  public static void main(String[] args) {    Keng.isNumber("");// 必须要传递个参数  }}

能不能让 Java 也享受到 Kotlin 默认参数的快乐?

// Test.java
class Test {  public static void main(String[] args) {    // JvmOverloads 注解的作用,默认实现了 重载 特性    Keng.isNumberWithOverLoads();    Keng.isNumberWithOverLoads("");  }}

注意:@JvmOverloads与Android View 体系控件搭配使用时,需要额外投入精力关注下。详情,可以进入传送门:不要总是相信 @JvmOverloads

Num 4:Kotlin 中的判空姿势

如下代码,在 Java 中应该还好,但在 Kotlin 中是无法通过编译器编译的。

var nullableString: String? = null...fun testNullableString() {    if (nullableString != null) {        var nullableStringLength = nullableString.length // 此处会报错!!!    }}

会报如下错误:

Error:(9, 40) Kotlin: Smart cast to 'String' is impossible, because 'nullableString' is a mutable property that could have been changed by this time

所以,在kotlin中,正确的判空姿势应该是如下的样子:

fun testNullableString() {    var nullableStringLength = nullableString?.length}

Num 5:Kotlin 复写 Java 父类中的方法,这里有坑:

第一步:Java 父类定义 onDialogCreate,其中包含一个非空参数:

savedInstanceState// JavaKengBase.javapublic class JavaKengBase {    public void onDialogCreate(Object savedInstanceState) {        // todo nothings    }}

第二步:Kotlin 继承并复写 JavaKengBase

class Keng : JavaKengBase() {    override fun onDialogCreate(savedInstanceState: Any) {// 注意:此处,是Any,不是Any?        super.onDialogCreate(savedInstanceState)    }}

第三步:利用 Java 多态特性,调用 onDialogCreate,并传入 null 参数

public class KengJava {    public static void main(String[] args) {        JavaKengBase keng = new Keng();        keng.onDialogCreate(null);// 注意:空参数    }}

这里可以有两个问题:

第一个:"overrides nothing"

原因就在 onDialogCreate(savedInstanceState: Any) 方法定义中的:Any,不是Any?上。

注意: 不要相信 AS 编译器,使用快捷键 Override Method 时,还是需要额外关注参数是否 Nullable?

Error:(17, 5) Kotlin: 'onDialogCreate' overrides nothing

第二个:IllegalArgumentException: Parameter specified as non-null is null

就算通过了编译,但在运行时,可能会抛出 Parameter specified as non-null is null异常,这个异常也是JavaKotlin混合开发中的高频异常。

综上:上述问题,很好解决,只需要在方法参数后面,增加一个?即可。

override fun onDialogCreate(savedInstanceState: Any?) 

Num 6:Kotlin “狠”起来,连TODO都不放过!

就下面这个平淡朴实无奇的 TODO() ,却会抛出一个 RuntimeException !

fun testTodo(): Unit {    TODO()}

这个错误就是:kotlin.NotImplementedError: An operation is not implemented.,让我们看看 TODO() 的实现:

public inline fun TODO(): Nothing = throw NotImplementedError()

Num 7:is、as 中的坑

obj is String之后,作用域之中,类型就已经转换了,有点类似 java 的多态。

fun testAsIs() {  var obj: Any? = null  if (obj is String) {// 方法体内的作用域,obj 就是 String      var length = obj.length  }}

as的两种不推荐写法,会抛出异常:TypeCastException: null cannot be cast to non-null type kotlin.String

//错误写法1,text不是String或为空时,会报异常
var strAble1 = text as String

//错误写法2,text不是String时,同样会报异常
var strAble2 = text as String?

as的推荐写法:

//正确写法,转换失败自动转换为空对象
var strAble = text as? String

Num 8:Kotlin 中的 Property 的理解

在Kotlin中一个 property 不管有没有 backing field 都称之为 property,而在 Java 中 field + get、set方法一起才能是一个 property。

var age = 0    set(value){        age = value + 1    }

这样写其实是会发生递归,无法赋值成功。

珠玉在前,就不重复造轮子了。额外感兴趣的,可以进入传送门 :Howshea 理解 Kotlin 中的属性(property)


因为 实践中的坑总是伴随着最佳实践一起出现,所以,最后附上几则最佳实践,以飨读者:

Num 1:also 关键字,最佳实践:

while (bufferReader.readLine().also({ line = it }) != null) {  // do something}

相当于 Java 中的:

while ((line = bufferReader.readLine()) != null) {  // do something}

Num 2:takeIf 关键字,最佳实践:

// 原代码
if (someObject != null && status) {   doThis()}

// 最佳实践
someObject?.takeIf{ status }?.apply{ doThis() }

Num 3:单例模式的写法

关于设计模式的写法,珠玉在前,同样不重复制造轮子了,传送门:Kotlin的5种单例写法和java对比。

//Java实现
public class Singleton {    private volatile static Singleton instance;    private Singleton(){}    public static Singleton getInstance(){        if(instance==null){            synchronized (Singleton.class){                if(instance==null){                    instance=new Singleton();                }            }        }        return instance;    }}

//kotlin实现
class Singleton private constructor() {    companion object {        val instance: Singleton by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {            Singleton()        }    }}
作者:海路IFEI
链接:https://juejin.cn/post/6910918847067324430

关注我获取更多知识或者投稿

40593064cd912708562ff0543b730955.png

9f880a68a9399c87c0d211eae6bd4c9f.png

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐