Kotlin 实战经验中的那些坑+最佳实践

“确认过“踩过坑”,遇上对的 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.java
public 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

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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值