Kotlin入门

作者: ProfessorJason | 来源:发表于2018-05-26 17:59 被阅读0次

    Kotlin 这种语言是基于对象和函数混合的这样一种语言,它跟 Scala 很像,但是 Scala 更多是一种实验性的语言。Kotlin 侧重于可供生产使用,能够让大家编程的整体体验感觉更好。

    Kotlin 从它诞生的那个时候我们就希望它是能够非常紧密的和 Java 能够结合在一起的。所以,你可以创造这样的一个 Kotlin 的类,但是从 Java 的代码当中调用这个类,同样的你也可以在 Kotlin 的代码中调用 Java 的类。这样做的原因是, Java 的编程人员能够从他们代码的很小的地方开始试一试 Kotlin,从一些小的地方进行修改,这样不需要大费周章的把整个代码全部改成 Kotlin 这种语言。

    第一个例子

    Kotlin 这种语言它是有修饰函数,然后它的标识符是在 fun 这个函数后面,后面会紧跟着一个冒号。这个方法首先是从 fun 这个关键词开始,接下来是这个函数的名称,然后是它的参数、冒号,以及这个参数的类型,冒号之后是这个方法返回值的类型。

    fun cube(x: Int): Int {
      return x * x * x
    }
    
    fun bang(s: String): String = s + "!"
    
    fun main(args: Array<String>) {
      println(cube(3))
      println(bang("pop"))
    }
    

    第二个函数,首先是 fun 这个关键词,然后是函数名,接下来你用一个等号表达这个函数的具体内容。这个函数没有返回值的具体类型,编译器在看到这个方法具体构造的时候就能够知道这个返回值是一个 String 的类型。这个地方大家可以注意到,这是和 Java 很不一样的一点,就是我们不需要一定有一个类,这个地方我们只要确定一个方法,然后把它带的参数全部列出来,然后我们就可以进行运行了,不需要一个类去包裹它。大家可能注意到了,Kotlin 不要求大家打分号。

    print 的方法也会变得简单了很多,大家不用再打“System.out.println()”,这个地方很简单的用 println()就可以打印出来。

    for循环

    不像 Java 会写很多的参数和条件在 for loop 里面,写出 1..10 就可以循环从 1 到 10 的所有整数

    fun main(args: Array<String>) {
      for (i in 1..10) {
        print("$i ")
      }
    }
    

    val和var

    在 Kotlin 里面一共有两种变量,一种叫做 val,是不可变的标识符,另一种是 var是可变的标识符。在大多数情况下,我们会使用 val 这种不可变的变量,这也是函数类编程的一个基础思想。

    class

    类的写法也被大大的简化了,现在看到的 class NoBody 后面没有跟着大括号,里面也没有具体的内容,这是一种很像 Python 的写法,这也是 Kotlin 自己的一个理念,就是说如果你不需要做这样的事情,它不会强制你去做。

      class NoBody
    
      class SomeBody {
        val name = "Janet Doe"
      }
    
      class EveryBody {
        val all = listof(SomeBody(), SomeBody(), SomeBody())
      }
    

    这个类 SomeBody 它的内容就是它的构造器,就是它的构造函数。在这个构造函数当中,创建了一个属性,就是 name,在这个地方再一次的就是我们没有去给它一个类型,而是通过类型推测来知道它是一个 String。

    第三个 class EveryBody,这个地方的时候就是我们创建了一个 list,里面包含了三个 SomeBody 这个类,大家可以注意到,这个时候我们在构造一个新的 SomeBody 的时候没有用像类似 Java 一样的关键词 new。大家就会注意到 Kotlin 这种语言,它的很多的细节都是从其他的语言中获取灵感的,它其实是集很多语言的优点于一身。

    带参数的class

    Badger 这个类大家可以看到它必须强制的带两个参数,一个是 id String 类型,一个是 age,是一个 Int 的类型,就是 Kotlin 你在构造函数里面的时候必须要说明它的类型是怎么样的,因为编译器是无法推测出你这个带的参数的类型是什么样的。所以,这是一个非常特别的一个地方。

      class Badger(id: String, years: Int) {
        val name = id
        val age = years
        override fun toString(): String {
          return "Badger: $name, age: $age"
        }
      }
    

    这边我们创建了两个属性,一个是 name,一个 age,它们两个的赋值来自于构造函数的参数。接下来可以看到我们这边有一个是 override 的方法,叫 toString,这个跟 Java 的 toString 是一样的,这是 Kotlin 的一个特点,就是它其实希望能够跟 Java 非常紧密的结合,非常紧密的能够互通的。在 Kotlin 的时候如果我们想要去 override 一个方法,是必须要加上这个关键词的,要不然这个编译器会报错。

    构造参数和成员变量

    这是另外一个类,叫 Snake,它也有自己的变量和 override 的一个方法,大家看它和上一个 Badger 之间的区别就在于 Badger 的构造函数里面带了两个参数,但是 Snake 这个构造函数里面用到了 var 这种变量。

      class Snake(var type: String, var length: Double) {
        override fun toString(): String {
          return "Snake: $type, length: $length"
        }
      }
    

    还有一点不同的就是在 Badger 里面的时候我们是必须要明确的写出来,明确的定义刚才的两个,那个里面的 var、val,然后在 Snake 里面我们不需要把它明确的定义出来,但是在 toString 的方法里面也可以调用到它,因为它是属于 Snake 这个类里面自己的变量。

    Kotlin 也是支持 Private、public 和 protected 这三种访问符,你默认的情况下,如果你不做任何声明它是一个 public 的变量,但是你也可以说明它是一个 private 或者 protected。

    泛型

      fun main(args: Array<String>) {
        val numbers: List<Int> = listOf(1, 2, 3)
        val strings: List<String> = listOf("one", "two", "three")
        for (c in toCharList("seven"))
          print("$c ")
      }
    
      fun toCharList(s: String): List<Char> = s.toList()
    

    大家可以看到,numbers、strings 之后都有一个冒号,会有一个它们具体的类型,你在做 Kotlin 编程的时候可能会遇到你不得不,或者你希望去说明它的类型是怎么样的,有时候你不得不去说明,因为你不说明 Kotlin,就会给你提示你是错误的。

    可变参数

    Kotlin 也支持可变参数这种类型,它通过这个关键字 vararg 来支持。在这个函数里面,“s”是一个 string,“ints”它是可能有多个 int 组成的这样一个变量。在这个例子当中,ints 就是一个数组,我们可以用 for loop,然后说 in 这个数组,然后把数组里的每一个进行循环调用,然后把它打印出来。下面这个 main 函数就是调用了上面的这个 vararg 这个方法,你可以看到我在后面打了多个质数。

      fun varargs(s: String, vararg ints: Int) {
        for(i in ints)
          print("$i ")
        println(s)
      }
    
      fun main(args: Array<String>) {
        varargs("primes", 5, 7, 9, 11, 13, 15, 17, 19, 23)
      }
    

    三个引号

    在这个例子当中,我是想给大家看一下这三个引号这种写法,三个引号帮我圈起来文本的区块,在这种写法当中,比如说像换行之类的,或者单引号之类的,都会被这个写法照顾到。我非常确定这种功能,这种写法是来自 Python 的,因为我只在 Python 见过这样的写法。

    接下来我们调用了 split 这个方法,然后在里面代入了一个正则表达式,这个 regex 正则表达式是直接来自于 Java 的库。大家都很熟,这个正则表达式我们是在寻找一个或多个空格,然后通过一个或多个空格把上面的这个 String 进行分隔。而这个最后返回的结果可以是一个 list,也可能是一个数组,比如这个数组的情况下,接下来我们调用 sort 的这个方法把它进行排序。

      val colors = """
        Yellow Red Green Blue Orange Cyan
      """.split(Regex("\\W+")).sorted()
    
      fun main(args: Array<String>) {
        println(colors)
        val iSet = colors.toSet() - ""
        println(iSet)
        println(iSet + iSet)
        val mSet = iSet.toMutableSet()
        mSet += colors.slice(3..5)
        println(mSet)
        // Set membership
        println("Red" in iSet)
        println(iSet.contains("Chartreuse"))
      }
    

    接下来大家可以看到一些 Kotlin 自带的一些方法,比如说,我们刚才把这个颜色 colors 这个数组进行排序,排序之后我们可以调用 toSet 这个方法,把它变成一个集,然后下一个操作是减去所有里面空白的格。

    接下来我们可以把两个 iSet 加在一起,这边应该它已经变成了一个集,所以我们可以这样去做,这个也是很好的去解释 Kotlin 的一个特性,就是它能够重载一个运算符。这个“+”和上面这个“-”都是重载运算符的一个例子,因为它已经是一个 Set,一个集合,所以 iSet+iSet 最后的结果还是等于 iSet,所以这两行打印出来的结果应该是一样的。color.toSet 这个方法创造了一个不可变的 Set,MutableSet 的 Set。

    接下来我们可以调用 ToMutableSet 这个方法,重新创造一个新的 Mutable 可变的一个集。接下来我这边又是有一个运算符的重载,就是“+=”,这个时候我们把 color 这个数组切割,让它从第 3 到第 5 这样序列的三个字符,然后加到 mSet 这个可变的集合当中。

    接下来给大家展示的是如何能够确认一个词语是在一个集合当中,有两种方法,一种是用 in,或者是用“.contains”这种方法能够确认。大家可以看出来,第一种方法,in iSet 会比第二种读起来更好读,这也是为什么我觉得 Kotlin 是一种很好的作为 DSL 的一个语言。

    Map

    在这边我做了一个例子,创建了一个 map,大家可以看到 to,这个 to 不是一个关键字,是一个 Kotlin 作为 DSL 这种方式创造的一种方法,这边我把一个 string 和一个 Int 的映射关系存入 map 当中。理所当然,我们也可以看到,我们可以把它的一个 key 放进去,看它的值是多少,或者把它所有的 key 全部列出来,或者把它所有的值全部列出来。

      val ascii = mapOf(
        "A" to 65, "B" to 66, "C" to 67, "D" to 68, 
        "I" to 73, "J" to 74, "K" to 75
      )
    
      println(ascii["B"])
      println(ascii.keys)
      println(ascii.values)
    
      for (entry in ascii)
        print("${entry.key}:${entry.value},")
    

    接下来我把这一个 map 里面的每一个 Entry 对象循环一遍,Entry 是作为一个类定义出来的,然后我把每一个打印出来,Entry 这个类有两个自带的变量,一个是 key,一个是 value。我还想再给大家看一下,在这个 for 循环里面,我们没有说 Entry 包含的值是什么类型的,编译器在这个时候它能够知道你现在是在每一个的检验 map 里面的这个值。所以,它会给你自己去推测它的类型。

    Data class

    接下来我给大家看一下这种 Data class,Data class 是来自于 Scala,但是在 Python 的 3.7 版本中我也见到了类似的这种表达方法。在 Scala 里这种表达方法叫做 case class,case 类。

      class Data(var i: Int)
    
      fun main(args: Array<String>) {
        val data = Data(10)
        println(data.i)
        data.i = 20
      }
    

    这个 Data class 主要的好处是在于它帮你创造了一般在 Java 上一个类的时候里面自带的一些比较琐碎的方法,比如,getter、setter 这样的方法,它都帮你自动创建了,并且能够帮你把它们都写对。因为我把“i”这个变量声明成 var 这种方法,所以我可以对它进行重新赋值,我现在把它的 Data.i 的赋值是 20。Data class 帮你创建了比如像 hashcode、 toString、getter、setter 这样的方法,就是避免你忘记写这些方法导致运行错误。

    getter and setter

    还有一个就是 Kotlin 做的很好的是关于 getter 和 setter 的方法,这个其实是 Java 被诟病很长时间的,但是一直大家也没有解决的一件事情。Kotlin 的做法就是可以先声明一个 var 这样的类型,紧接着你可以写一个 get 或者一个 set,或者两个都写,当每次你去读写这个变量时,get 或者 set 方法会被自动执行。

      class GetterAndSetter {
        var i : Int = 0
          get() {
            println("get()")
            return field
          }
          set(value) {
            println("set($value)")
            field = value
          }
      }
    
      fun main(args: Array<String>) {
        val gs = GetterAndSetter()
        gs.i = 2
        println(gs.i)
      }
    

    field 是 Kotlin 里面一个特殊的提示符,在这个情况下,如果把“i”这个变量改名字了,叫做“j”或者其他的什么,但是你并不需要改变 Field,因为它能够通过 Field 这个特殊的关键词,能够对这个类的变量,这个“i”进行修改。当大家看到这个方法的时候,我们创建了一个 GettersAndSetter 这个类,我们现在要调用“gs.i”,这是一个 Set 的方法,或者我们做一个 Println(gs.i), 这是一个 get 的方法,因为大家可以看到,刚才 get() 和 set 里面都有打印,所以,在我们在做这两行操作的时候,那两个方法都会被调用,然后我们也会看到相应的打印。

    参考:Kotlin 或将取代 Java —— 《Java 编程思想》作者 Bruce Eckel

    相关文章

      网友评论

        本文标题:Kotlin入门

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