Kotlin - 入门基础

Kotlin编程语言 之 入门基础

1.Kotlin介绍

  • Kotlin 是一个基于 JVM 的新的编程语言,由 JetBrains 开发
  • Kotlin可以编译成Java字节码,也可以编译成JavaScript,方便在没有JVM的设备上运行
  • JetBrains,作为目前广受欢迎的Java IDE IntelliJ 的提供商,在 Apache 许可下已经开源其Kotlin 编程语言
  • Kotlin语言的使用场景:
    • Kotlin 用于服务器端
    • Kotlin 用于 JavaScript
    • Kotlin 用于 Android
      • 划重点 - > Kotlin已正式成为Android官方支持开发语言,并且是Android开发一级语言

2.对Kotlin的评价

1.如何评价 Kotlin 语言? - 知乎
2.如何看待 Kotlin 成为 Google 正式支持的 Android 开发一级编程语言? - 知乎
3.Kotlin 作为 Android 开发语言相比传统 Java 有什么优势? - 知乎
4.个人评价

  • 迁移:用Kotlin插件可将现有的Java代码,转换成Kotlin代码(单个、多个Java文件或对整个项目转换),虽然不能100%成功转换,但不成功的只需要微调下,就OK了
  • 互操作性质:可以和Java进行混合开发 —— 这就厉害了,没有人生下来什么都会,所以每个人都会有从接触到熟悉的一个过程,复杂的Java可以在后期转换成Kotlin,给开发人员一个学习的过度,这使得Kotlin慢慢渗透到你项目中,最后替代掉项目中的每一个.java,Kotlin和Java语言的基础是差不多的,对于开发者,短时间上手问题不大
  • 学习曲线:使用Kotlin后,项目的代码量确实减少不少,个人感觉Kotlin比Java8好很多(对Java8的一些新特性我其实还没闹明白...也不知道为毛,看一会就看不进去了,而这些天在学习Kotlin,我倒是求知欲爆发...)
  • 工具:代码检查很严格,避免后期编译成功运行后出问题,再回过头苦逼的debug...毕竟有的项目较大,每一次编译都是需要时间的...
  • 比较:没太深入了解过其他语言,就不和其他语言对比了

3.国际惯例 - Hello World

//新建一个Hello.kt,在文件中写入以下内容并运行,就可以看到Hello World!愉快的被打了出来...
fun main(args: Array) {
   println("Hello World!")
}

与Java相比,感觉代码少了和精简很多...(后面还有一段路要走,还有好多坑没有入...)

//Java Hello World
public class Hello{
  public static void main(String[] args){
    System.out.println("Hello World!")
  }
}

4.项目搭建

  • 因为Kotlin是JetBrains的自家语言,所以在IntelliJ IDEA中可以建立Kotlin的项目也是理所当然,IDEA Community版是免费的,可以做Kotlin开发
  • 若你是Android开发者,新版Android Studio已经集成了Kotlin插件(没有的话更新或在Plugins里面安装Kotlin),通过工具栏 - > Code - > Convert Java File to Kotlin FIle,即可将Java文件转换成Kotlin(转换前切记备份项目,别玩脱了...)
  • 如何搭建项目这里就不多说了,IDEA下载直达点此
  • 以下是Kotlin中文网对几种构建工具的使用说明:
    编写 Kotlin 代码文档
    使用 Kapt
    使用 Gradle
    使用 Maven
    使用 Ant
    Kotlin 与 OSGi
    编译器插件

5.基础语法

毕竟Kotlin是JVM的编程语言,所以后面会有一些Kotlin和Java相比较而言的话出现

  • 分号";":Kotlin 中在每行代码的结尾省去了分号(;) ,如果你执意要写,IDE的语法检查最多也是建议删去,代码并不会因为多一个分号而不能被执行
    我这些天学习过程中,总是打分号,写这些示例Demo时,手欠打了N个分号,后来都被我一个个的剔除了...(我的内心是拒绝分号的...可能是条件反射

  • package与import的基本用法和Java一样,基本用法相对Java来说,少了结尾的分号";"

    • package的包名不必非要和目录名一样,不写也不会出错,但不建议
    • 如果你还不到此为止,还要再深究的话,我只能告诉你,它们也有更厉害的用法,请参见官方文档:中文,英文
  • 属性

    • 常量(val):类似Java中的终态即常量(final),一旦赋值后,则不能再次修改,官方称它为局部变量(Defining local variables),但我觉得定义在函数中并对其他成员不可见的情况才算是局部吧?应该称它为只读变量或常量吧,局部是什么鬼?
    val a=1//自动推断出 `Int` 类型
    val s="string"//自动推断出 `String` 类型
    val temp:String//没有赋初始值,所以无法推断变量类型,所以类型不能省略!
    temp="fish_leong"
    
    • 变量(var):和Java中的成员变量、局部变量类似用法一样,是可被重新定义赋值的
      var a=1
      a=2
      var s="string"
      s="123"
    
    • 伴生对象(companion object):类似Java中的静态(static)变量
      //Test.kt
      class Test {
        companion object {
            var Global = "Hello Wordl!"//在伴生对象中添加Global属性
        }
      }
      //Hello.kt
      fun main(args: Array) {
         println(Test.Global)//在这里可以在未实例化Test情况下,获取或修改Test中的Global属性
         Test.Global="fish_leong"
         println(Test.Global)
      }
    
    • Getters 与 Setters
      声明一个属性的完整语法是

      var [: ] [= ]
      []
      []
      
      • 初始器(initializer)、getter 和 setter 都是可选的。属性类型如果可以从初始器 (或者从其 getter 返回值)中推断出来,也可以省略
      • 一个只读属性的语法和一个可变的属性的语法有两方面的不同:1、只读属性的用 val开始代替var 2、只读属性不允许 setter
      //例如
      var name: String
          get() {
            //get方法
            return "1"
          }
          set(value) {
           //set方法
          }
      
      
      //自 Kotlin 1.1 起,如果可以从 getter 推断出属性类型,则可以省略它
      val test get() = 2//test被推断为Int类型
      
  • 基本类型:与 Java 很相近

    Type Bit width
    Double 64
    Float 32
    Long 64
    Int 32
    Short 16
    Byte 8
    • 类型的问题:由于Kotlin中的类型推断,会导致一些初学者不知道某个变量到底是什么类型,可以用下面这个方法曲线救国:
      val intRange = 10..21//整型数区间
      println(intRange.javaClass)//class kotlin.ranges.IntRange
      
      val doubleRange = 10.1..20.1//浮点数区间
      println(doubleRange.javaClass)//class kotlin.ranges.ClosedDoubleRange
      
      val str = "fish_leong"//字符串
      println(str.javaClass)//class java.lang.String
      
    • 具体对类型的运算、转换等操作可参考官方基本类型文档:中文,英文
  • 注释:基本的行级和块注释,与Java用法差不多,Kotlin注释进阶 - 官方文档:中文,英文

  • 权限修饰符:类似Java中的private、public...这些,可以修饰类或类中成员的修饰符,在下面 函数 例8中会讲到

  • 函数:在Java里叫方法吧(Method),用fun定义

    /**
     * 定义函数
     * 权限修饰:
     * 参数:根据实际情况而定,可无,支持缺省参数
     * 返回值:类型根据实际情况而定,可无,支持返回值类型推断
     * 函数体:对于简单的逻辑可简写,下面会提到
     */
    权限修饰 fun 函数名(参数名:类型 , ...):返回值类型
    {
      //函数体,有多种写法
    }
    
    //例1:返回值为Int的函数
    fun add(x:Int,y:Int):Int{
        return x+y
    }
    
    //例2:返回值为Int的函数,返回值由类型推断,相比Java要写return和花括号要简单些
    fun add1(x: Int, y: Int) = x + y
    
    //例3:返回值为Int的函数,返回值由类型推断,若x>y,返回x...else...
    fun compare(x: Int, y: Int) = if(x>y) x else y
    
    //例4:不带返回值的函数,
    fun addPrint(x:Int,y:Int):Unit{
      println(x+y)
    }
    
    //例5:不带返回值的函数,Unit可以省略
    fun addPrint1(x:Int,y:Int){
      println(x+y)
    }
    
    //例6:返回可为null的函数,由于Kotlin空安全机制,若返回类型String后不加?,则不能返回null
    fun toCustomString(x: Int, y: Int):String?{
      if(x>y){
          return x.toString()
      }
      return null
    }
    
    /**
     * 例7:默认参数的函数
     * 默认参数,可以叫做缺省参数,
     * 缺省参数使用主要规则:调用时你只能从最后一个参数开始进行省略,换句话说,如果你要省略一个参数,你必须省略它后面所有的参数
     */
    fun defaultParam(content: String, a: Int = 2, b: Int = 2) {
       println(content + (a + b))
    }
    
    fun main(args: Array) {
        defaultParam("a+b=",1)//省略第三个参数
        defaultParam("a+b=")//省略后两个参数
        //想只省略第二个参数是不被允许的,因为第三个参数也是缺省参数,这样写不符合缺省参数的使用规则
    }
     
     /**
      * 例8 权限修饰符
      * 新建TestA.kt
      * TestA类,包含5种修饰函数
      * private 、 protected 、 internal 、public 、open 和 不修饰 , 如果没有显式指定修饰符的话,默认可⻅性是 public
      * 所以public和不修饰算一种
      */
      class TestA {
          init {
              testPrivate()//private修饰的函数和Java一样,对本类成员可见
          }
      
          /**
           * 只有用open修饰的函数,才能被子类覆写override
           * 被修饰的函数对本类成员、子类成员和外部都可见
           */
          open fun testOpen() {
              println(this)
          }
      
          /**
           * private修饰的函数和Java一样,对本类成员可见
           */
          private fun testPrivate() {
              println(this)
          }
      
          /**
           * protected修饰的函数,其属性和private⼀样,不一样的是这种函数对外部不可见,对子类成员可⻅
           */
          protected fun testProtected() {
              println(this)
          }
      
          /**
           *
           *  官方解释:能见到类声明的  本模块内 的任何客⼾端都可⻅其 internal 成员
           *  若TestA类在A模块(Modules)中,TestB类在B模块(Modules)中,那么TestB类中将无法调用父类TestA中的internal修饰的函数
           *  模块的概念, 一个模块是编译在一起的一套 Kotlin 文件:
           *  一个 IntelliJ IDEA 模块;
           *  一个 Maven 项目;
           *  一个 Gradle 源集;
           *  一次 <kotlinc> Ant 任务执行所编译的一套文件。
           */
          internal fun testInternal() {
              println(this)
          }
      
          /**
           * 没有加修饰符,和public修饰的函数是一样的
           */
          fun test() {
              println(this)
          }
      }
    
      /**
       * 新建TestB.kt
       * TestB类,继承于TestA类,不建议与TestA类在同一个模块(Modules)中,这样体现不出internal修饰的函数的作用
       */
      class TestB : TestA() {
          /**
           * 在override时,只能override父类中用open修饰的函数
           */
          override fun testOpen() {
              super.testOpen()//父类中用open修饰的函数,是可以被子类、外部调用或覆写override的
    
              test()//没有加修饰符和加public修饰符是一样的,public可以省去
    
              testInternal()//此方法在父类TestA中用internal修饰,同模块(Modules)中,对子类TestB成员可见,若TestA类和TestB不在同模块(Modules)中,那么该函数对子类不可见
              /**
               * 测试internal修饰符的函数,可以建立两个模块进行测试
               */
      
              testProtected()//可以调用父类中protected修饰的函数,但是...请看下面
              /**
               * 被protected修饰的函数,可以被父类和父类的成员变量或成员函数调用,但不能被外部调用,举个栗子:
               * var testA = TestA();
               * testA.testProtected();//像这种情况,testProtected是不可见的
               */
          }
      }
    

    点此了解:Kotlin之空安全

  • 字符串模板

    • 传统的字符串拼接,其实是很苦逼的,不但要写很多加好双引号进行连接,如果稍微写错位一个,将酿成大错!即使是String.format()也没有Kotlin的字符串模板强大
    • Kotlin这种字符串模板方式,可以让我们从此不再苦逼
        val a = "A"
        val b = "B"
        println("a的值是" + a + ",b的值是" + b)//传统写法
        println("a的值是$a,b的值是$b")//Kotlin替代传统写法
        println("a的值是${a.replace("A", "C")},b的值是$b")//Kotlin写法,在模板中写代码
      
  • if表达式的基本用法:基本用法与Java类似,但在Kotlin中还有些花式用法

        //声明a b c,比较a和b,将大的赋值给c
      val a = 1
      val b = 2
      var c: Int
      //1.传统写法
      if (a > b) {
          c = a
      } else {
          c = b
      }
      //2.Kotlin写法,表达式
      c = if (a > b) a else b
      //3.Kotlin写法,if的分支可以是代码块,最后的表达式作为该块的值
      c = if (a > b) {
          println(a)
          a
      } else {
          println(b)
          b
      }
    
  • Ranges(区间):通过in检测某变量是否在指定区间中
    区间的所有使用方法请看Kotlin Range官方文档:中文,英文

      val a = 10
      //10..100的类型是 IntRange
      if(a in 10..100){
          println("a在10-100区间中")
      }
      //循环输出区间中的数字
      for (s in 1..10) {
          println(s)
      }
    
  • when表达式的基本用法:类似Java中的switch...case,但Kotlin的when表达式似乎更强大

    fun testWhen(x: Int) {
      when (x) {
          in 1..10 -> print("x在1-10的区间内")//区间判断
    
          !in 20..21 -> print("x不在在20-21的区间内")//区间判断
    
          is Int -> print("x是Int类型")//类型判断
    
          1 -> println(1)//x=1
    
          3 -> {
              println("退出")
              System.exit(0)
          }
          else ->//x均不符合其他条件
              println("fish_leong")
      }
    }
    /**
     * when像if 一样,每一个分支可以是一个代码块,它的值是块中最后的表达式的值
     */
    fun testWhen2(x: Int) {
      val c = when (x) {
          1 -> 2
          2 -> 3
          else -> 0
      }
    }
    
  • for循环的基本用法

      val arrays = arrayOf("1", "2", "3")//构造一个数组
      for (element in arrays) {
          println(element)//循环打印出数组元素,类似Java中的for(String str : arrays){ }
      }
      for (pos in arrays.indices) {
          println(arrays[pos])//通过下标方式循环打印出数组元素,类似Java中的for(int i=*;i<*;i++)
      }
    
  • while与do while循环和Java一样,这里不多做介绍了

      var a = 10
      while (a > 0) {
          a--
      }
      a = 10
      do {
          a--
      } while (a > 0)
    
  • 类型检测及自动类型转换:Kotlin可通过is来推断一个表达式是否是指定的类型,类似Java中的instanceof,但功能比Java中的强大一些

    //Any和Java中的Object差不多
    fun main(args: Array) {
        val str = "fish_leong"
        val int = 5
        println(getObject(str))
        println(getObject(int))
    }
    fun getObject(obj: Any): Any {
        if (obj is String) {
            //如果这里写obj * 3是不可以的,因为在此块中,obj已经被推断是String类型,所以肯定不能做乘法计算
            return obj.length//返回字符串长度
        }
        if (obj is Int) {
            return obj * 3//这里被推断为Int类型,返回obj*2的结果
        }
        return obj
    }
    
  • 返回与跳转

    • return:默认从最直接包围它的函数或者匿名函数返回。它可以写表达式里:
      fun main(args: Array) {
          printUpCaseName(null)
          printUpCaseName("1")
          printUpCaseName("fish_leong")
      }
      
      /**
       * 检查name,转大写并输出
       * @param name 名字
       */
      fun printUpCaseName(name: String?) {
          val nameCopy = name ?: return//如果name为null,则return,printUpCaseName方法结束,下面代码不会执行
          if (name == "1") {
              //常见的return
              return
          }
          println(nameCopy.toUpperCase())
      }
      
    • continue:继续下一次最直接包围它的循环。也可以使用类似Java循环标签,只是书写方式有点不一样:
      fun main(args: Array) {
          loop1@ for (i in 1..3) {//打印 1~3的数字
          println("loop1:$i")
      
          loop2@ for (j in 5..8) {//打印 5~8的数字
              if (j == 5) {//继续本循环
                  continue@loop2////在这里loop2写不写都可以,这个continue相对本循环的
              }
      
              if (j == 6) {//跳出本循环,继续loop1标签的循环
                  continue@loop1
              }
              println("loop2:$j")
          }
          //最后看输出,你会发现,loop2循环什么也没输出,因为每当j=6时,都会跳出loop2继续执行loop1,和下面的Java用法和接近
          //        loop1:
          //        for (int i = 0; i < 3; i++) {
          //            System.out.println("loop1:" + i);
          //            loop2:
          //            for (int j = 5; j < 8; j++) {
          //            if (j == 5) {
          //                continue loop2;//在这里loop2写不写都可以,这个continue相对本循环的
          //            }
          //            if (j == 6) {
          //                continue loop1;
          //            }
          //            System.out.println("loop2:" + j);
          //        }
          //      }
          //    }
        }
      }
      
    • break:终止最直接包围它的循环。标签用法和continue一样:
       fun main(args: Array) {
           loop1@ for (i in 1..3) {//打印 1~3的数字
                 println("loop1:$i")
                 loop2@ for (j in 5..8) {//打印 5~8的数字
                     if (j == 5) {//
                       break@loop1//终止loop1循环,loop2循环自然也会终止
                     }
                     println("loop2:$j")
                 }
            }  
         }
      
    • 标签处返回:Kotlin 有函数字面量、局部函数和对象表达式。因此 Kotlin 的函数可以被嵌套。 标签限制的 return 允许我们从外层函数返回。 最重要的一个用途就是从 lambda 表达式中返回:
      fun main(args: Array) {
          test(arrayOf(1, 2, 3, 4))
      }
      
      fun test(value: Array) {
          value.forEach for1@ {
              // it代表forEach循环访问value数组中元素时的某个元素
              if (it == 1) return@for1//当循环访问到等于1的元素时,则返回至标签处,下一行代码不执行,forEach不会因此停止,还会继续循环访问后面的元素
              println("for1$it")
          }
          value.forEach for2@ {
              if (it == 1) return//这个厉害了,此return是相对test函数的,一旦条件满足,后面代码将不会被执行
              println("for2$it")
          }
          println("函数结束")
      }
      
      输出
        for1:2
        for1:3
        for1:4
      
  • 编码规范
    就没什么可以白话的了...搬运自Kotlin 语言中文站

    • 命名风格
      如果拿不准的时候,默认使用Java的编码规范,比如:
      使用驼峰法命名(并避免命名含有下划线)
      类型名以大写字母开头
      方法和属性以小写字母开头
      使用 4 个空格缩进
      公有函数应撰写函数文档,这样这些文档才会出现在 Kotlin Doc 中

    • 冒号
      类型和超类型之间的冒号前要有一个空格,而实例和类型之间的冒号前不要有空格:
      interface Foo : Bar { fun foo(a: Int): T}

    • Lambda表达式
      在lambda表达式中, 大括号左右要加空格,分隔参数与代码体的箭头左右也要加空格 ,lambda表达应尽可能不要写在圆括号中

      list.filter { it > 10 }.map { element -> element * 2 }
      

      在非嵌套的短lambda表达式中,最好使用约定俗成的默认参数 it
      来替代显式声明参数名 ,在嵌套的有参数的lambda表达式中,参数应该总是显式声明

    • 类头格式化
      有少数几个参数的类可以写成一行:

      class Person(id: Int, name: String)
      

      具有较长类头的类应该格式化,以使每个主构造函数参数位于带有缩进的单独一行中。 此外,右括号应该另起一行。如果我们使用继承,那么超类构造函数调用或者实现接口列表应位于与括号相同的行上:

      class Person( id: Int, name: String, surname: String) : Human(id, name) { // ……}
      

      对于多个接口,应首先放置超类构造函数调用,然后每个接口应位于不同的行中:

      class Person( id: Int, name: String, surname: String) : Human(id, name), KotlinMaker { // ……}
      

      构造函数参数可以使用常规缩进或连续缩进(双倍的常规缩进)

    • Unit
      如果函数返回 Unit 类型,该返回类型应该省略:

      fun foo() { // 省略了 ": Unit"}
      
    • 函数还是属性

      很多场合无参的函数可与只读属性互换。 尽管语义相近,也有一些取舍的风格约定

      底层算法优先使用属性而不是函数:

      • 不会抛出任何异常
      • O(1)
      • 复杂度
      • 计算廉价(或缓存第一次运行)
      • 不同调用返回相同结果

还有好多东西没有提到,例如集合、类与对象等,会在以后文章中写,上面这些算是入门的基础吧。

参考文档:
[1]基本语法 - Kotlin 语言中文站
[2]控制流:if、when、for、while - Kotlin 语言中文站
[3]编码规范 - Kotlin 语言中文站
[4]返回与跳转 - Kotlin 语言中文站
[5]模块 - Kotlin 语言中文站
[6]Kotlin - 百度百科
[7]缺省参数 - 百度百科

你可能感兴趣的:(Kotlin - 入门基础)