Kotlin 语法上的一些亮眼操作

作者: MeloDev | 来源:发表于2017-06-14 15:32 被阅读566次

    Kotlin 语法上的一些亮眼操作

    本文原创,转载请注明出处。
    欢迎关注我的 简书 ,关注我的专题 Android Class 我会长期坚持为大家收录简书上高质量的 Android 相关博文。

    Kotlin 初体验

    写在前面:
    上上周我们创建了第一个 kotlin 的 android 应用。上周我花了一周的时间,在工作之余了解了 kotlin 的语法。感叹 kotlin 做为“高级”语言与 java相比,展现出来的简洁、高效、智能。不过如果有人问我 kotlin 和 java 的具体区别,那我肯定会首先描述为 命令式编程语言函数式编程语言 的区别。

    命令式编程语言函数式编程语言 用概念描述,可以为:命令式编程语言泛指所有把修改变量的值当作最基本计算方式的语言,函数式编程语言指把一个程序的输出定义为其输入的数学函数的语言,纯函数式编程没有内部状态的概念,也没有副作用。

    对两者而言,我的体会并不深刻,所以来引用知乎的一段话:

    纯函数式编程语言中的变量也不是命令式编程语言中的变量,即存储状态的单元,而是代数中的变量,即一个值的名称。变量的值是不可变的(immutable),也就是说不允许像命令式编程语言中那样多次给一个变量赋值。比如说在命令式编程语言我们写“x = x + 1”,这依赖可变状态的事实,拿给程序员看说是对的,但拿给数学家看,却被认为这个等式为假。

    函数式语言的如条件语句,循环语句也不是命令式编程语言中的控制语句,而是函数的语法糖,比如在Scala语言中,if else不是语句而是三元运算符,是有返回值的。

    关于二者的比较可以进一步查阅资料来了解,对于理解编程语言的本质和计算机的构成,很有帮助。

    下面我们就来看看与 java 相比,kotlin 有哪些“高级”的语法。

    函数扩展

            val array: Array<Int> = arrayOf(4, 5, 6)
            val array3: Array<Int> = arrayOf(7, 8, 9)
            // 声明一个函数扩展,我们需要在函数前加一个接收者类型作为前缀。上面就是为 `Array<Int>` 添加一个 swap 函数
            fun Array<Int>.swap(x: Int, y: Int) {
                // 在扩展函数中的 this 关键字对应接收者对象。
                val temp: Int = this[x]
                this[x] = this[y]
                this[y] = temp
            }
            // 可以在任何 Array<Int> 实例中使用这个函数了
            array.swap(1, 2)
            array3.swap(0, 3)
    

    如果用 java 来做的话,我们需要继承父类。或者用一个方法做这个操作,需要将 array 作为参数传递进去,这样一来就加大了出错概率,也不够自由美观,这就是扩展带来的好处。

    空安全

    导致 java 程序崩溃最多的 Exception 就是 NullPointerException 也叫 NPE,Kotlin 类型系统致力与消灭它。

    在 Kotlin 类型系统中可以为空和不可为空的引用是不同的。比如,普通的 String 类型的变量不能为空:

            var s: String = "activity"
            s = null //编译报错 普通字符串类型不可为空
            
            var t:String? = "fragment"
            t = null //同?声明的 String 可以为空
            
            print(s.length)// 可以调用,这里的 s 永不为 null
            print(t.length)// 报错,这里的 t 可能为空
    

    可以看到,这里在声明一些可能为 null 的引用时,kotlin 是区分对待的,并且在调用的时候自动判断它是否可能为 null,当可能为空的时候,直接用 . 调用,会直接报错。

    如果我们想调用,可以用条件判断的方式:

            val u = if (t != null && t.length > 0) {
                print(t.length)
                t.length
            } else {
                -1
            }
    

    这样显然稍显啰嗦,kotlin 自然想到了这一点,这样安全操作符 ?. 就出现了。

            val r: Int? = t?.length
    

    这个表达式,当 t 为 null 时,返回 null,否则返回 t.length

    安全调用在链式调用是是很有用的。比如,如果 Bob 是一个雇员可能分配部门(也可能不分配),如果我们想获取 Bob 的部门名作为名字的前缀,就可以这样做:

        bob?.department?.head?.name //这样的调用链在任何一个属性为空都会返回空
    

    如果用 java 来做,那就会完全体现 java 的“又臭又长”了

    Elvis 操作符
    ?: Elvis 操作符表达的是,当左边表达式为空时,会执行右边的表达式。否则执行左边的表达式。

            val o = t?.length ?: -1
            val i = t?.length ?: throw NullPointerException()
    

    !! 表达式

            val y = t!!.length
    

    当 t 为 null 时,抛出一个 NPE,否则返回 t 的长度值。

    智能转换

    is !is 表达式

        fun smartCast(any: Any) {
            if (any is String) {
                println(any.length)// x is automatically cast to String
            }
        }
    

    通过 is 关键字,在 if 表达式中,any 已经自动被转换成了 String 类型。

    安全转换 as?

            val s: String? = any as? String
    

    若 any 为 null 时,显然是不可以转换成 String 的,这时候用 as? 安全转换,如果失败了,则返回 null。

    字符串模板

            val firstName = "Android"
    
            val lastName = "Studio"
    
            println("his name is $firstName $lastName")
    

    如上所示,kotlin 可以用 $ 将一些值直接连接在字符串当中

    单例模式

    在 java 中,我们创建一个单例模式的对象可能是这样子的:

    public class Utils {
    
        private Utils() { 
          // This utility class is not publicly instantiable 
        }
        
        public static int getScore(int value) {
            return 2 * value;
        }
        
    }
    

    在 kotlin 中,就方便了很多:

    object Utils {
    
        fun getScore(value: Int): Int {
            return 2 * value
        }
    
    }
    

    Bean 对象

    在 java 中创建一个 bean 对象,可能是这样的:

    public class Developer {
    
        private String name;
        private int age;
    
        public Developer(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
    
            Developer developer = (Developer) o;
    
            if (age != developer.age) return false;
            return name != null ? name.equals(developer.name) : developer.name == null;
    
        }
    
        @Override
        public int hashCode() {
            int result = name != null ? name.hashCode() : 0;
            result = 31 * result + age;
            return result;
        }
    
        @Override
        public String toString() {
            return "Developer{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    

    在 kotlin 中,可以使用 data 关键字,直接创建一个 bean 对象:

    data class Developer(var name: String, var age: Int)
    

    Ranges

    kotlin 还有一些特有的属性,比如类型的推断啊,一级构造函数啊,等等。这些自己去撸文档就可以了,最后一个想说的就是 kotlin 中的 Ranges 属性。

    Ranges 操作符是由 in 关键字实现的:

    if (i in 1..10) {
        println(i) // 打印 1 - 10 闭区间
    }
    
    if (x !in 1.0..3.0) println(x)
    
    if (str in "island".."isle") println(str)
    

    当然还有更多的用法和关键字,根据打印值体会:

    for (i in 1..4 step 2) print(i) // prints "13"
    
    for (i in 4 downTo 1 step 2) print(i) // prints "42"
    
    for (i in 1.0..2.0 step 0.3) print("$i ") // prints "1.0 1.3 1.6 1.9 "
    

    好了本文对 kotlin 一些我认为很亮眼的语法和用法就介绍完成了,下一步我打算研究下 kotlin 在快速开发 android 上,有哪些黑科技。

    相关文章

      网友评论

      本文标题:Kotlin 语法上的一些亮眼操作

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