kotlin基础语法

作者: samychen | 来源:发表于2017-05-28 17:07 被阅读1543次

    每种编程语言都有一定的语法、语义和执行顺序(同步),学习一种新语言也都是从这三者出发,下面我们就只针对kotlin的语法来做简单的介绍。

    Kotlin有自己的特性不该被Java的思维所束缚。

    1. 基本语法准则:
      在Kotlin中常量用 val 声明,变量用 var 声明;
      关键字在前面,类型以冒号 :隔开在后面,也可以省略直接赋值;
      类型后带问号 ? 表示可为空类型(默认空类型安全);
      常量 val 延迟加载 by lazy{} ;
      默认是线程安全的,关闭线程安全 lazy(LazyThreadSafetyMode.NONE){} ;
      变量 var 延迟加载 lateinit ;
      内部类和参数默认为public,而在Java中为private
      类默认为不可继承(final),想要可被继承要声明为 open 或 abstract
      取消了static关键字,静态方法和参数统一写在 companion object 块
      internal模块内可见,inner内部类
    //常量数组int[][][] arrs = new int[3][2][1];
    val arrs = Array(3) { Array(2) { IntArray(1) } }
    internal var name: String? = null//类型后带问号 ? 表示可为空类型(默认空安全)
    internal var age: Int = 0//internal模块内可见,inner内部类
    //当我们只有单个构造器时,我们需要在从父类继承下来的构造器中指定需要的参数。这是用来替换Java中的super调用的。
    open class Animal(name: String)
    class Person(name: String, surname: String) : Animal(name)
    

    kotlin是空类型安全的,所有变量默认为"not null",必须显式在类型后添加?修饰符才可赋值为null。

    var notNullArtist: Artist = null//编译不通过,因为notNullArtist不能为null
    var artist: Artist? = null//编译通过
    artist.print()//编译不通过,因为artist可能为空
    /** Kotlin进行空判断处理,有两种处理方式:
         * 1. 抛出空异常,字段后加 !! 
         * 2. 不做处理直接跳过,字段后加 ?
         */
    artist?.print()//编译通过,做了非空判断,只有当artist!=null时才调用print()
    artist!!.print()//这种用法只有在确认artist不为null时才能调用,否则抛出空指针异常
    val name = artist?.name?:"empty"//当artist为null时可以指定一个默认值
    
    1. 条件语句
      if...else 正常使用,不过移除了 switch 用更强大的 when 替代,when子式可以是各种返回Boolean的表达式
    val x = 7
    when (x) {
      in 1..5 -> print("x is in the range")
      in validNumbers -> print("x is valid")
      !in 10..20 -> print("x is outside the range")
      else -> print("none of the above")
    }
    

    kotlin尽可能多的使用when

    1. 循环语句
      while 和 do...while 同Java并无区别, for 则有很大改变并多出了几个变种
    fun main(args: Array<String>) {
        var list = ArrayList<String>()
        add(list)
        list.forEachIndexed { i, s ->
        print(list[i])
        print(s)
        }
        println()
        //如果没有指定函数的返回值,它就会返回Unit,与Java中的void类似,但是Unit是一个真正的对象。当然也可以指定任何其它的返回类型:
        list.forEachIndexed(object :(Int,String) -> Unit{
        override fun invoke(i: Int, s: String) {
            print(list[i])
            print(s)
        }
    })
    }
    //递增for (int i = 0; i < list.size(); i++)
    for (i in list.indices) {
       print(list[i])
    }
    //递增for (int i = 2; i < list.size(); i++)
    for (i in 2..list.size-1) {
       print(list[i])
    }
    //递减for (int i = list.size(); i >= 0; i--)
    for (i in list.size downTo 0) {
        print(list[i])
    }
    //操作列表内的对象
    for (item in list) {
        print(item)
    }
    //加强版
    for((i,item) in list.withIndex()){
        print(list[i])
        print(item)
    }
    //变种版
    list.forEach {
        print(it)
    }
    
    list.forEachIndexed { i, s ->
        print(list[i])
        print(s)
    }
    
    list.forEachIndexed(object :(Int,String) -> Unit{
        override fun invoke(i: Int, s: String) {
            print(list[i])
            print(s)
        }
    })
    fun add(list:MutableList<String>) {
            for (i in 0..4) {
                list.add(i.toString() + "")
            }
        }
    

    冒号使用
      在Kotlin中冒号 : 用万能来称呼绝不为过。常量变量的类型声明,函数的返回值,类的继承都需要它

    除此之外还有一个特别的地方也需要它,使用Java类的时候。Kotlin最终会还是编译成Java字节码,使用到Java类是必然的,在Kotlin语法如下
    val intent = Intent(this, MainActivity::class.java)

    指定上下文的@

    除了冒号另一个重要符号 @ ,java代码中经常用到内部类和匿名内部类,有时我们不能确定this指代的上下文,Java可以使用XXX.this指代具体上下文,在kotlin中的做法是this@XXX

    class User {
        inner class State{
            fun getUser(): User{
                //返回User
                return this@User
            }
            fun getState(): State{
                //返回State
                return this@State
            }
        }
    }
    

    kotlin的特色

    Java的 getter/setter 方法自动转换成属性,对应到Kotlin属性的调用

    public class User {
        private String name;
        private String age;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getAge() {
            return age;
        }
    
        public void setAge(String age) {
            this.age = age;
        }
    }
    

    对应的kotlin

    val user = User()
    //赋值
    user.name = "tutu"
    user.age = "23"
    //取值
    val name = user.name
    val age = user.age
    
    class User {
        var name: String? = null
        var age: String? = null
    }
    

    有时 getter/setter 方法比较复杂,这就需要自定义 getter/setter 了,实现一个Java中常用的单例,这里只为了展示,单例在Kotlin有更简单的方法实现,只要在 package 级别创建一个 object 即可

    class User {
        companion object {//静态方法和参数统一写在 companion object 块
             //volatile不保证原子操作,所以,很容易读到脏数据。在两个或者更多的线程访问的成员变量上使用volatile
            @Volatile var instance: User? = null
                get() {
                    if (field == null) {
                        synchronized(User::class.java) {
                            if (field == null)
                                field = User()
                        }
                    }
                    return field
                }
        }
    
        var name: String? = null
        var age: String? = null
    }
    

    自定义 getter/setter 重点在 field ,跟我们熟悉所Java的 this 指代当前类一样, field 指代当前参数,直接使用参数名 instance 代替不会报错但单例就没效果了

    字符串问题

    在Java中拼接字符串的代码可读性都很差,在Kotlin字符串拼接变得非常简洁,只需用 $ 后面加上参数名,复杂的参数要加上 {}

     val pair = Pair(1, "one")
     val (num, name) = pair
     println("num = $num, name = $name")
    

    输出num = 1, name = one

    Java8新特性lambda的支持

    lambda需要一个函数,但是又不想费神去命名一个函数的场合下使用,也就是指匿名函数。使用功能接口,把接口名、方法名和参数类型省掉不写再加个 -> 罢了。

    使用Java开发Android时,处理监听回调是常见的事,kotlin可以直接编写监听回调而不用再通过匿名对象传递onClick方法,这个特性被称为Lambda表达式

    view.setOnclickListener({
          Toast.makeText(this, "Hello World!", Toast.LENGTH_LONG).show()
    })
    

    扩展函数

    可以为任何已经存在的类添加新函数,相比传统工具类,扩展函数更具有可读性。

    //为Fragment添加扩展函数
    fun Fragment.toast(message: CharSequence, duration: Int = Toast.LENGTH_LONG){
          Toast.makeText(getActivity(), message, duration).show()
    }
    

    调用时直接调用fragment.toast("Hello World!")或fragment.toast("Hello World!", 2000)

    Kotlin中的参数与Java中有些不同。如你所见,我们先写参数的名字再写它的类型。上面调用的第二个参数(length)指定了一个默认值。这意味着你调用的时候可以传入第二个值或者不传,这样可以避免你需要的重载函数。

    函数式支持(lambda),函数是一级公民

    集合操作

      list转map(associateBy)

      场景:订单列表转换成以 id为key 的订单map

    val mainOrders = orderDao!!.queryUserOrder(param)
    val orderMap = mainOrders.associateBy { it.id }.toMap()
    

      map的key或者value转换

      假如一个map的key是String,需要转换成Long;或者map的value是一个对象,要转成另一个对象。按照标准Java写法,可以要new一个新的map,然后循环老的map,在kotlin中,一行代码搞定

    val map = mutableMapOf(1 to 1, 2 to 2)
    val newMap = map.mapKeys { "key_${it.key}" }.mapValues { "value_${it.value}" }
    println(newMap)
    //打印结果 {key_1=value_1, key_2=value_2}
    val pair = Pair("ss","sg")
     val map = mapOf(pair)
    val map1=map.mapKeys { entry -> "${entry.value}!"  }
        for((key,value) in map1){
            println("map1:key=$key")
            println("map1:value=$value")
        }
            val map2 =map.mapKeys { (key, value) -> "$value"  }
        for((key,value) in map2){
            println("map2:key=$key")
            println("map2:value=$value")
        }
            val map3=map.mapValues { entry -> "${entry.value}!" }
        for((key,value) in map3){
            println("map3:key=$key")
            println("map3:value=$value")
        }
            val map4=map.mapValues { (key, value) -> "$value" }
        for((key,value) in map4){
            println("map4:key=$key")
            println("map4:value=$value")
        }
    打印结果:
    map1:key=sg!
    map1:value=sg
    map2:key=sg
    map2:value=sg
    map3:key=ss
    map3:value=sg!
    map4:key=ss
    map4:value=sg
    

    参考文献
      Kotlin for android Developers
      kotlin 脚本练习

    相关文章

      网友评论

        本文标题:kotlin基础语法

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