Kotlin开发细节

Kotlin是一门优秀的语言,简洁的编码风格大大简化了开发过程。但是在使用的过程中,也发现了一些需要注意的问题。

findViewById

使用Kotlin开发Android应用程序时,不需要通过findViewById获取指定id的View控件,在大多数情况下,这都没什么问题,但是在动态加载布局或使用AbsListView/RecyclerView等集合类的View组件时,需要以itemLayout.viewId的方式来引用,直接通过viewId不能关联到指定View:

// Activity/Fragment/Dialog 引用的方式
toolbar.title = "标题"

// 动态添加的子布局/需要适配器的容器类组件的引用方式
val itemView = View.inflate(this, R.layout.item_home_comment_of_list_layout, null)
itemView.name_text.text = "Freeman"

static成员

Kotlin中不支持static成员,可通过companion关键字来实现相应的功能:

companion object {
    fun enter(context: Context, id: String) {
        var intent = Intent(context, TargetActivity::class.java)
        intent.putExtra("id", id)
        context.startActivity(intent)
    }
}

// Java代码中的调用
XXXActivity.Companion.enter(mContext, "123");

// Kotin代码中的调用:
XXXActivity.enter(mContext, "123");

换行问题

由于Kotlin语言每一行代码是不需要分号表示结束的,同时支持任意的字符,变量,表达式的直接引用:

// 以下语句在Kotlin中都是合法的
var i = 10
var j = 20
100
200
i
i + j

所以在对比较长的表达式进行换行时,需要注意操作符的位置:

// 伪代码:JAVA版(换行时提倡操作符写在下一行)
int value = expression_A + expression_B
    + expression_C

如果Kotlin使用Java规范编写上面的代码,那么value的值为(expression_A + expression_B), 而(+ expression_C)会被编译器当做下一跳语句,所以Kotlin中正确的写法应该是 —— 操作符留在前一行的尾部或使用括号将表达式括起来。

val value = expression_A + expression_B + 
    expression_C
// 或者
val value = (expression_A + expression_B 
    + expression_C)

非空问题

虽然Kotlin不需要像Java一样进行显式的非空判断,但在使用Kotlin的非空运算符时还是要注意。Kotlin提供了两种非空运算符:

!!: 表示对象不为空时取其值,否则抛出NullPointerException;
?.: 表示对象不为空时取其值,否则当做null来处理;

相对来说,[?.]运算符安全许多,至少不会直接抛出异常。但当作为方法的参数时,如果该方法的参数不接收非空值,此时仍然会抛出NullPointerException。 如果方法的参数为引用类型,接收空值,那么在方法中引用参数的成员变量时需使用非空操作符。为了避免在方法中到处都是非空操作符可使用以下两种方案进行解决:

  • 引用类型的参数定义为非空类型,然后在调用时进行显式的非空判断;
  • 引用类型的参数定义为可为空的类型,然后在方法开始时判断;
// 第一种方法: 
fun printStr(person : Person) {
    println("Name=${person.name} Age=${person.age}")
}

// 第二种方法: 
fun printStr2(person : Person?) {
    if (person == null) {
        return
    }
    println("Name=${person.name} Age=${person.age}")
}

class Person {
    public var name : String? = "Freeman"
    public var age : Int? = 20
}

fun main(args: Array<String>) {
    val person : Person ? = Person()
    // 第一种方法调用
    if (person != null) {
        printStr(person!!)
    }
    // 第二种方法调用
    printStr(person)
}

整体来说,第二种方案封装性更好,调用也更简单,但它有个致命的问题,方法中参数的非空判断不能通过调用方法来判断,比如:

fun isEmpty(obj : Any?) : Boolean {
    if (obj == null) {
        return true
    }

    return false
}

如果使用isEmpty()来判断非空,那么后面引用成员变量时仍然需要非空运算符,对于普通对象这个问题倒没什么影响,但对于集合或数组类型的参数,我们通常都是封装的非空方法来判断,此时的非空判断就没多大意义了。所以第一种方法应用场景更广,熟练掌握这两种方案,就可保证在不抛出NullPointerException的同时消除代码中大多数的非空操作符。

对于成员变量,如果能够保证后面对其进行初始化,那么可使用lateinit延迟初始化,避免在引用其成员变量时使用非空运算符:

lateinit var userInfo : UserInfo
if (ClassName::userInfo.isInitialized) {
    println(userInfo.name)
}

字符串拼接

在Java中由于不能在字符串中直接取变量或表达式的值,所以当需要在某个字符串中显示变量或表达式的值时只能通过字符串拼接来实现。而Kotlin中提供了取值的功能,这样就不需要进行拼接了。

// Java版
int a = 10;
int b = 20;
System.out.println("a=" + a + " b=" + b + " a+b=" + (a+b));

// Kotlin版本
val a = 10
val b = 20
print("a=$a b=$b a+b=${a+b}")

Java中字符串拼接非字符串的常量时,字符串的位置没有限制,但是在Kotlin中拼接时,字符串只能放在前面:

// Java
String value = "value=" + 5;
String value2 = 5 + " apples";

// Kotlin(拼接时不支持字符串后置)
val value = "value=" + 5

其他问题

其他的问题,如:使用equals比较,容器根据index取值等,在编码过程中AS会自动提示警告,根据提示进行修改即可。

总结

以上问题是在使用过程中发现的一些细节问题,可能还有其他的细节问题暂未遇到,欢迎补充。当然,如有不正之处,也欢迎指正。

,