那些会上瘾的Kotlin语法

7 篇文章 0 订阅
5 篇文章 0 订阅

那些会上瘾的Kotlin语法

Kotlin有毒,用过就上瘾……略有夸张,其实就用来写了个记录POI和轨迹的demo而已,不过感觉语法确实很简洁。在此总(板)结(书)一下Kotlin与Java的不一样,或者说比Java更简洁、优雅的语法和特性。

1.字符串模板

相比于Java的字符串拼接,Kotlin的字符串模板更紧凑:

fun main(args:Array<String>){
    val name = if(args.size > 0) args[0] else "Kotlin"
    println("Hello, $name!")
}

除了通过 引 用 变 量 名 称 外 , 还 可 以 通 过 {} 引用更复杂的表达式

fun main(args:Array<String>){
    if(args.size > 0){
        println("Hello, ${args[0]}!")
    }
}

相比Java的字符串拼接,一连串的+,或者StringBuilder的append,Kotlin的字符串模板是不是显得优雅了许多。而且编译后的代码创建了一个StringBuilder对象,并把常量部分和变量部分附加上去,效率一样。

2.有值的表达式

在Kotlin中,if是表达式,不是语句,语句和表达式的区别在于,表达式有值,并且能作为另一个表达式的部分使用;而语句总是包围着它的代码块的顶层元素,并没有自己的值。在Java中,所有的控制结构都是语句,而在Kotlin中,除了循环(for、do和do/while)以外,大多数控制结构都是表达式。这种结合控制结构和其他表达式的能力,让你可以简明扼要地表示许多常见的模式。
另一方面,Java中的赋值操作是表达式,在Kotlin中反而变成了语句。这有助于避免比较和赋值之间的混淆,而这种混淆是常见的错误来源。

fun max(a: Int, b: Int): Int{
    return if(a > b) a else b // 类似与Java的三目运算符 (a > b) ? a : b
}

有值的表达式非常有趣,你不需要像Java那样在每个if-else中返回一个值,或者设置一个值,它表达式本身就是一个值,没有了更多的赋值,看起来更加清爽。

3.比switch更简洁的when

与Java的switch语句对应的是when,与switch相比,when的语法不需要一个一个的case-break,when可以接受任意对象作为参数,也可以不带参数,同时when是有值的表达式:

fun getColor(color: Int) = when(color){
     1 -> "Red"
     2 -> "Green"
     3 -> "Blue"
 }

4.区间和数列

在Kotlin中,没有常规的Java for循环,而是用区间的概念代替这种最常见的循环用法。区间的本质就是两个值之间的间隔,这两个值通常是数字,一个起始值,一个结束值,使用..运算符来表示区间:

val oneToTen = 1..10

迭代区间的值:

for(i in 1..10){
    println(i)
}

如果你能迭代区间中所有的值,这样的区间被称作数列

创建一个递减数列,步长为2:

for(i in 100 downTo 1 step 2){      

}

创建一个半闭合区间:

for(x in 0 until 100){      

}
// 等同于
for(x in 0..99){

}

5.命名参数和默认参数值

命名参数:

当调用一个Kotlin定义的函数时,可以显式地表明一些参数名称。如果在调用一个函数时,指明了一个参数的名称,为了避免混淆,那它之后的所有参数都需要表明名称。

默认参数值就是在声明函数的时候可以给参数指定一个默认值,这样可以避免创建重载的函数。这里举一个创建简单对话框的例子,考虑点击对话框之外的区域能关闭对话框,用Java实现,那么先写一个函数,有一个Boolean型的参数canceledOnTouchOutside,然后重载该函数,可以关闭对话框则赋值true,这样就多了一个重载函数。而有了Kotlin的默认参数值,只需要给参数默认值true就可以了:

/**
 * 一个没有标题,只有提示和确定按钮的对话框,点击边缘会消失
 * @param context
 * @param question
 * @param onOk
 * @param canceledOnTouchOutside 默认点击对话框外部消失
 */
fun createSimpleDialog(context:Context,
                       question: String,
                       onOk: DialogInterface.OnClickListener?,
                       canceledOnTouchOutside: Boolean = true): AlertDialog{
    val dialog =  AlertDialog.Builder(context, android.R.style.Theme_Material_Light_Dialog_Alert)
            .setMessage(question)
            .setPositiveButton(context.getString(R.string.ok), onOk)
            .create()
    dialog.setCanceledOnTouchOutside(canceledOnTouchOutside)
    return dialog
}

调用:

createSimpleDialog(activity, "Lambda实现", 
    DialogInterface.OnClickListener { dialog, which ->

}).show()

在调用的时候省略了canceledOnTouchOutside ,编译通过,函数使用了默认值。

当使用常规的调用语法时,必须按照函数声明中定义的顺序来给定参数,可以省略的只有排在末尾的参数。如果使用命名参数,可以省略中间的一些参数,也可以以你想要的任意顺序只给定你需要的参数。

6.顶层函数和属性

代替Java的静态方法是顶层函数,顶层函数不属于任何一个类,依然是包内的成员,通过import即可从包外访问。顶层属性的定义和顶层函数也一样,放到顶层文件即可。

7.扩展函数

扩展函数就是一个类的成员函数,不过定义在类的外面,还是按照正常的方法调用,如代码所示,String就是要扩展的类,称为接收者类型,用来调用这个扩展函数的那个对象,叫做接收者对象,接收者对象成员也可以不用this来访问:

package strings
fun String.lastChar(): Char = this.get(this.length - 1)

// 调用
println("Kotlin".lastChar())

就是这么简单,为String类增加了一个lastChar()方法。
要使用扩展函数,需要像其它类或者方法一样进行导入

import strings.lastChar
val c = "Kotlin".lastChar()

实质上,扩展函数时静态函数,它把调用对象作为了它的第一个参数。调用扩展函数,不会创建适配的对象或者任何运行时的额外消耗。这使得从Java中调用Kotlin的扩展函数变得非常简单。

假设lastChar声明在一个叫StringUtil.kt的文件中:

char c = StringUtilKt.lastChar("Java")

最后,值得注意的是,扩展函数是不支持重写的。
扩展函数使不改变原有代码的情况下,增加类的功能成为可能,调用扩展函数就像调用类的函数一样。

8.局部函数和扩展

局部函数可以理解为在函数中定义的函数,使用局部函数可以减少很多重复的代码:
使用局部函数前:

class User(val id: Int, val name: String, val address: String)

fun saveUser(user: User){
    if(user.name.isEmpty()){
        throw IllegalArgumentException("Can't save user ${user.id}: empty Name")
    }
    if(user.address.isEmpty()){
        throw IllegalArgumentException("Can't save user ${user.id}: empty Address")
    }
    // 保存user到数据库
}

使用局部函数:

fun saveUser(user: User){
    fun validate(user: User, value: String, fieldName: String){
        if(value.isEmpty()){
            throw IllegalArgumentException("Can't save user ${user.id}: empty $fieldName")
        }
    }
    validate(user, user.name, "Name")
    validate(user, user.address, "Address")
    // 保存user到数据库
}

将验证代码抽取出来,作为局部函数之后,看起来好看多了。不过还可以优化,那就是把验证逻辑提取到扩展函数中:

class User(val id: Int, val name: String, val address: String)
fun User.validateBeforSave(){
    fun User.validate(value: String, fieldName: String){
        if(value.isEmpty()){
            throw IllegalArgumentException("Can't save user $id: empty $fieldName")
        }
    }
    validate(name, "Name")
    validate(address, "Address")
}
fun saveUser(user: User){
    user.validateBeforSave()
    // 保存user到数据库
}

这样,验证函数完全不用写在User类中,如果你能遵守,类的API只能包含必需的方法,那么就可以让类保持精炼的同时,也让你的思路更加清晰。

9.安全调用运算符:”?.“

终于到了最喜欢没有之一的安全调用运算符:?.,方法和属性调用时再也不需要一连串的null判断了。假设这样一个类 User,有一个address属性,属于Address类,有一个属性cityName,现在有一个User类的引用,需要访问cityName,Java代码如下:

if(user != null && user.addrss != null){
    println(user.address.cityName)
}

而Kotlin代码看起来则舒服多了:

println(user?.address?.cityName ?: "")

10.Elvis运算符(null合并运算符):”?:“

Elvis运算符接收两个运算数,如果第一个运算数不为null,则运算结果就是第一个运算数,否则时第二个运算数:

fun foo(s: String?){
    val t: String = s ?: ""
}

Elvis运算符经常和安全调用运算一起使用,用一个值代替对null对象调用方法时返回的null:

fun strLenSafe(s: String?): Int = s?.length ?: 0

暂时到此为止,更多好用的Kotlin语法有待进一步发现!

参考

Kotlin实战

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值