美文网首页我爱编程
迟到的Kotlin体验

迟到的Kotlin体验

作者: Winter_F | 来源:发表于2017-07-07 10:44 被阅读81次

    Google在今年的I/O大会上宣布Kotlin正式成为Android的官方语言,这么好玩的新东西当然不能错过啦,陆陆续续看资料写Demo过了一个多月,希望这篇体验还不太晚。

    什么是Kotlin

    Statically typed programming language for modern multiplatform applications
    100% interoperable with Java™ and Android™

    Kotlin是一门基于 JVM 的静态语言,由JetBrains 在2011年推出并持续维护,目前已发布1.0正式版,官网突出的一个优点是完全兼容Java和Android,除此以外,Kotlin还有下面几个核心目标。

    • 简约:帮你减少实现同一个功能的代码量。
    • 易懂:让你的代码更容易阅读,同时易于理解。
    • 安全:移除了你可能会犯错误的功能。
    • 通用:基于 JVM 和 Javascript,你可以在很多地方运行。
    • 互操作性:这就意味着 Kotlin 和 Java 可以相互调用,同时 Jetbrains 的目标是让他们 100% 兼容。

    在2017年的I/O大会上,Google宣布Kotlin正式成为Android的开发语言。

    Kotlin vs Java

    Java的问题:

    • 空引用,这个被发明者吐槽价值十亿的错误(来源)一直困扰着众多Java程序员,这是使用Java语言无论如何都没办法避免的问题
    • 泛型的通配符,难以理解的super和extends,对编译器而言异常检查也很困难
    • 高阶方法,Java8才引入了函数式编程和Lambda,在此之前类似的实现都要写繁琐的接口来代替
    • 检查型异常,检查型异常的抛出有时会暴露实现细节,或者导致开发人员会被异常淹没,写出难以理解的代码

    以上问题部分在新版本的Java中被解决,但无奈Android还只支持Java6,API19以上才支持Java7,对Java8的支持更是遥遥无期,所以这时候,Kotlin对Android开发人员来说就异常吸引了。

    Kotlin的优点:

    • 空安全(Null safety)
    • Lambda 表达式
    • 数据类 (Data classes)
    • 函数字面量和内联函数(Function literals & inline functions)
    • 函数扩展 (Extension functions)
    • 智能转换(Smart casts)
    • 字符串模板(String templates)
    • 主构造函数(Primary constructors)
    • 类委托(Class delegation)
    • 类型推断(Type inference)
    • 单例(Singletons)
    • 声明点变量(Declaration-site variance)
    • 区间表达式(Range expressions)

    Kotlin还为Android开发准备了两个库,一个是Kotlin Android Extensions,目前只实现了对findView的简化。另一个是开源的Anko,为Layout、SQLite等操作作了简化。

    基本语法对比

    下面是一些个人感兴趣的语法对比,更详细的内容和说明请参考官方文档汉化文档。都说Kotlin是Android界的Swift,那我就不客气把Swift也一起拿来对比了。

    Print
    Hello,World!

    // Java
    System.out.println("Hello,World!");
    
    // Kotlin
    println("Hello,World!")
    
    // Swift
    print("Hello,World!")
    

    ** 变量**
    Kotlin使用var声明可变变量,val声明不可变变量,由于有类型推断,如果变量在初始化时就赋值,可以省略类型声明。

    // Java
    int x;
    x = 1;
    final int y = 2;
    
    // Kotlin
    var x: Int
    x = 1
    val y = 2
    
    // Swift
    var x:Int
    x = 1
    let y = 2
    

    方法
    Kotlin方法支持默认参数,使用vararg表示可变长度参数,直接使用“=返回值”可以省略返回值类型声明

    // Java
    public int sizeOf(float param0, String param1, int... param2) {
        return param2.length;
    }
    
    // Kotlin
    fun sizeOf(param0: Float, param1: String = "hhh", vararg param2: Int)
            = param2.size
    
    // Swift
    public func sizeOf(param0:Float, param1:String = "hhh", param2:Int...) -> Int{
        return param2.count
    }
    


    没有对比就没有伤害,Kotlin中类的定义可以异常简洁,类的成员变量和构造函数可以一起定义,同时因为有默认参数的存在,Java需要写多种构造函数,在Kotlin中并不必要。
    Kotlin还有个很方便的功能,只需要在$后面加上参数就可以拼接字符串,涉及方法调用和计算的话需要加上大括号。
    但Kotlin也有个让我不爽的地方是,静态方法和变量需要定义在companion object中,感觉这种写法白白增加了一段缩进。

    // Java
    public class User extends Person{
    
        public static final String TAG = "USER";
    
        String name;
        int birth;
    
        User() {
            this("", 0);
        }
    
        User(String name, int birth) {
            this.name = name;
            this.birth = birth;
        }
    
        public String getInfo() {
            int age = 2017 - birth;
            return String.format("%s's age is %d", name, age);
        }
    }
    
    // Kotlin
    class User(val name: String = "", val birth: Int = 0) : Person() {
        companion object {
            val TAG = "USER"
        }
    
        fun getInfo(): String = "$name's age is ${2017 - birth}"
    }
    
    // Swift
    class User:Person{
        
        static let TAG = "USER"
        
        var name:String
        var birth:Int
        
        init(name:String = "", birth:Int = 0) {
            self.name = name
            self.birth = birth
        }
        
        public func getInfo() -> String{
            return "\(name)'s age is \(2017-birth)"
        }
    }
    

    ** 空值处理 **
    Kotlin所有变量默认不能为空,可以加上?表示允许空的类型,Kotlin要求开发人员必须要标记可空值且必须要对可能的空值作处理,这就从机制上限制了Null Pointer的问题。实际写代码时,因为有很多语法糖,对空值的处理很方便,但和Java兼容的部分比较麻烦,基本全部变量都要视做可空变量处理。(还是Android的代码用的爽,正经的加上@Nullable和@NonNull注解)

    // Java
    String name = null;
    if(name !=null){
        int length = name.length();
    }
    
    // Kotlin
    var name:String? = null
    val length = name?.length
    val length = name!!.length // throw NullPointerException
    
    // Swift
    var name:String? = nil
    let length = name?.characters.count
    let length = name!.characters.count // ERROR?
    

    ** For循环**
    Kotlin增加了用..表示区间的写法,用在for和when中方便了不少

    // Java
    for (int i = 1; i < 11 ; i++) { }
    for (int i = 1; i < 11 ; i+=2) { }
    for (String item : collection) { }
    for (Map.Entry<String, String> entry: map.entrySet()) { }
    
    // Kotlin
    for (i in 1..10) { }
    for (i in 1..10 step 2) { }
    for (item in collection) { }
    for ((index, item) in collection.withIndex()) { }
    for ((key, value) in map) { }
    
    // Swift
    for i in 1...10 { }
    for i in stride(from: 1, to:10, by: 2) { }
    for item in collection { }
    for (index, item) in collection.enumerated() { }
    for (key, value) in map { }
    

    ** Switch**
    Kotlin的When集合了Java的switch和if...else...的功能,同时简化了判断值在区间内的写法

    // Java
    final int x = 10;
    final String xResult;
    switch (x){
      case 0:
      case 11:
        xResult = "0 or 11";
        break;
      case 1:
      case 2:
        //...
      case 10:
        xResult = "from 1 to 10";
        break;
      default:
        if(x < 12 && x > 14) {
          xResult = "not from 12 to 14";
          break;
        }
        xResult = "otherwise";
    }
    
    final int y = 10;
    final String yResult;
    if(isNegative(y)){
      yResult = "is Negative";
    } else if(isZero(y)){
      yResult = "is Zero";
    }else if(isOdd(y)){
      yResult = "is Odd";
    }else {
      yResult = "otherwise";
    }
    
    // Kotlin
    val x = 10
    val xResult: String = when (x) {
        0, 11 -> "0 or 11"
        in 1..10-> "from 1 to 10"
        !in 12..14 -> "not from 12 to 14"
        else -> "otherwise"
    }
    
    val y = 10
    val yResult = when {
      isNegative(y) -> "is Negative"
      isZero(y) -> "is Zero"
      isOdd(y) -> "is odd"
      else -> "otherwise"
    }
    
    // Swift
    let x = 10
    let xResult: String
    switch x {
    case 1,11:    xResult = "1 or 11"
    case 2...10:xResult = "from 2 to 10"
    default :xResult = "other"
    }
    
    let y = 10
    let yResult: String
    if isNegative(value: y){
      yResult = "is Negative"
    } else if isZero(value: y){
      yResult = "is Zero"
    }else if isOdd(value: y){
      yResult = "is Odd"
    }else {
      yResult = "otherwise"
    }
    

    更多对比:
    Kotlin to Java
    Kotlin to Swift

    Kotlin中各种酷炫的语法

    ** 函数扩展**
    Kotlin 最强大的特性之一,通过 被扩展类型.函数 的写法,就能将函数变成被扩展类型的一部分。

    // 先定义好Context函数扩展
    fun Context.toast(message: String, duration: Int = Toast.LENGTH_SHORT) {
        Toast.makeText(this, message, duration).show()
    }
    // 然后在所有Context的子类中都可以直接调用toast方法
    toast("Show it again?")
    

    下面这个例子是Anko库中使用函数扩展对intent的一个简化。

    // Java
    Intent intent = new Intent(this, SomeOtherActivity.class);
    intent.putExtra("id", 5);
    intent.setFlag(Intent.FLAG_ACTIVITY_SINGLE_TOP);
    startActivity(intent);
    
    // Kotlin + Anko
    startActivity(intentFor<SomeOtherActivity>("id" to 5).singleTop())
    

    ** 委托**
    Kotlin 把委托模式视为很重要的语言特性,Kotlin标准库中已经实现了多种常见的委托,开发者也可以很容易定义自己的委托,使用时只要加一个小小的by。

    //懒加载
    val app: SQLiteOpenHelper by lazy {
        MyDatabaseHelper(applicationContext)
    }
    
    //Not Null,如果还没赋值前调用get方法,会抛出错误
    var instance: Application by Delegates.notNull()
    
    // 从Map中取值
    val map: HashMap<String, Int> = ...
    val width: Int by map
    val height: Int by map
    

    PS:Kotlin版的Butter Knife 就是用了委托来实现,使用起来的比Java的注解简洁方便许多,还省去了调用bind方法的步骤。

    val titleTV: TextView by bindView(R.id.titleTV)
    val sourceTV: TextView by bindView(R.id.sourceTV)
    val webView: WebView by bindView(R.id.webView)
    

    ** 异步线程**
    引入Anko库可以很方便的切换子线程和主线程,这个线程切换更多的是Kotlin函数式编程的体现,可以接受函数作为参数和把函数作为返回值会令代码更简洁和有更多的可能性。

    doAsync {
        // 子线程
        for (i in 1..10) {
            val aTitle = titles[i]
            uiThread {
                // 主线程
                titleTV.text = aTitle
            }
            Thread.sleep(1000)
        }
    }
    

    总结

    个人看来Kotlin最吸引人的一点是完全兼容Java,换句话说现存的所有Java框架和库可以无需修改直接拿到Kotlin上使用,在开发的过程中还可以先在部分模块中使用Kotlin编写,慢慢过渡到Kotlin开发。Intellj还提供一个Java-to-Kotlin的功能,但批量转换成Kotlin很容易报错,感觉还是留着Java文件共存或者手写一份比较靠谱。
    其次各种简洁、易懂的语法也是一大亮点,Java中几十行的长代码,用Kotlin写可能只需要几行,简洁大方,逼格满满,还能保持很好的可读性,缺点就是会提高一些上手的门槛。
    Kotlin会编译成JVM字节码,理论上Kotlin执行效率和Java代码的执行效率是一致的。有时候Kotlin可能会显得高一些,比如Kotlin提供了方法的inline设置,可以设置某些高频方法进行inline操作,减少了运行时的进栈出栈和保存状态的开销。

    用Kotlin写了个新闻客户端,用了Dagger2、Retrofit、Glide、LifeCycle,兼容起来基本没什么问题,看着就舒爽。
    地址:KotlinTest

    相关文章

      网友评论

        本文标题:迟到的Kotlin体验

        本文链接:https://www.haomeiwen.com/subject/ujhifxtx.html