初识Kotlin

Kotlin

Kotlin 是Jetbrains 开发的基于JVM的静态类型的编程语言。 曾经幻想自己能不能裁剪下Java的语法,然后重新做一个JVM语言,不过在发现Kotlin之后发现完全没有这个必要了。 它已经做了我想做的东西,而且人家做的很好。 除了有专业团队的支持以外,还有专业的IDE支持(给IDEA加分),这样的待遇其实我想是很多新生语言都羡慕的。

Scala也有TypeSafe来支持,但是目前看上去语法复杂,有时候IDE都不能知道语法对不对。

Kotlin还很年轻,现在也才 1.0-beta,不过背后的Java生态算是很大一部分助力,因此Kotlin其实很多类库都是借用JDK的现成东西。 在学习的时候不免产生Kotlin会不会自己的东西太少了的担忧。 但是Kotlin基于JDK 1.6 引入了高阶函数等特性,而这些特性在Java语言中需要等到 JDK 8 才能提供。 因此基于这点,Kotlin对于想使用JDK 8 特性的人来说是一个吸引力。 这意味着,即便你的生产环境是JDK 6 也可以安全的使用Kotlin。

到现在为止也学习了不少编程语言,Haskell、Python、JavaScript、Scala,大致学习的流程遵循了:基本数据类型 -> 基本语法结构 -> 高级语法特性 -> 类库, 这样的顺序来学习。 在这篇文章中同样遵循这样的顺序来学习Kotlin,并会和Java做下比较。

基本数据类型

Kotlin 的设计理念不同于 Scala的是,它倾向于在原有Java的基础上对不好的地方进行补丁,而不是完成重新设计一套体系。 不过在基础类型这个地方两者是一样的,所有的类型都是对象

基本的数据类型主要有

  • Number
  • Character
  • Boolean
  • String
  • Array

Number

下面是Number 各具体类型的长度

Type Bits Bytes
Double 64 8
Float 32 4
Long 64 8
Int 32 4
Short 16 2
Byte 8 1

和Java是一致的

整形字面量的表示

  • 十进制。 123, 长整形型 123L
  • 十六进制。 0x1F
  • 二进制。 0b000101

Kotlin 不支持八进制

浮点型字面量的表示

  • Double。默认的浮点型都是Double, 123.322
  • Float。 需要通过尾巴添加 fF 来指定, 123.4F

Number 使用的一些规则

相等性判断

如果Number是 nullable 的话 (例如,Int?),那么变量就会被当做引用来对待。这个时候要小心的使用 =====,前者相当于 equals(),而后者会判断两个引用是否指向同一个对象。

val a: Int = 10000
print(a === a) // Prints 'true'
val boxedA: Int? = a  // a 被装箱成 java.lang.Integer
val anotherBoxedA: Int? = a
print(boxedA === anotherBoxedA) // !!!Prints 'false'!!!

精度转换

数据类型中并没有所谓的 低精度的类型是高精度类型子类 的说法,因此不能够直接的把一个低精度的变量赋值给另一个高精度的变量

val b: Byte = 1 // 字面量是静态检查了的,赋值没问题
val a: Int = b  // Error !!

// 应该使用 toXXX()
val a: Int = b.toInt()

所有的数据类型都支持下面的方法

  • toByte(): Byte
  • toShort(): Short
  • toInt(): Int
  • toLong(): Long
  • toFloat(): Float
  • toDouble(): Double
  • toChar(): Char

操作符

Kotlin 支持标准的操作符。不过和Java不同的是,Kotlin没有直接的位操作符,即>>>>><<

Kotlin 的位操作方法

  • shl(bits) – 有符号左移
  • shr(bits) – 有符号右移
  • ushr(bits) – 无符号右移
  • and(bits) – & 操作
  • or(bits) – | 操作
  • xor(bits) – xor 操作
  • inv() – 按位取反

Character

字符不能当做数字对待

字符字面量

使用单引号(')来声明字符字面量

val c: Char = 'c'
val a: Int = c.toInt() // 显式的转换到整形

装箱

当Char可能是 nullable 的时候也是自动装箱,这个时候要区别对待 =====

Boolean

Boolean 包含两个值: truefalse

操作符有

  • &&
  • ||
  • !

String

String 是不可变的

字符串字面量

Kotlin有两种不同类型的String 字面量

第一种是以单个双引号来声明的,和正常的Java字符串一样。这种字符串要处理escaped 字符

val s = "Hello, world!\n"

第二种是以三个双引号来声明的。这种字符串可以包含任意的字符

val text = """
            for (c in "foo")
            print(c)
            """

字符串模板表达式

在String的字符串可以包含模板表达式,来引用当前上下文中的变量

val i = 10
val s = "i = $i" // evaluates to "i = 10"

val s = "abc"
val str = "$s.length is ${s.length}"

如果要使用$字符,需要再用模板表达式处理下

val price = "${'$'}9.99"

Array

Kotlin 的数组都是通过 Array 类来实现的,没有原生的数组类型。正因为如此,Kotlin的数组是invariant

class Array<T> private constructor() {
    val size: Int
    fun get(index: Int): T
    fun set(index: Int, value: T): Unit
    fun iterator(): Iterator<T>
    // ...
}

快速创建Array的方法

Kotlin类库里提供了一些方便创建Array的方法。

arrayOf()

arrayOfNulls()

带构造函数的array 方法

// Creates an Array<String> with values ["0", "1", "4", "9", "16"]
val asc = Array(5, { i -> (i * i).toString() })

另外还有基础数据类型的数组类

IntArrayByteArray 等,这些类和Array类没有继承关系,他们会直接生成JVM里面的数组类型 int[], byte[]

基本语法结构

这部分和Java大同小异,列一些比较独特的地方吧

控制流

if 语句

if语句在Kotlin中是表达式。

需要注意的是如果把 if 当做表达式来用,比如把 if 语句的值赋给一个变量, 把if的值return等,这个时候 else是必须的

val max = if (a > b) {
            println("Choose A")
            a
        } else {
            println("Choose B")
            b
        }

when 语句

Kotlin里面没有了 switch,引入了when语句。算是功能上的增强,但是还达不到Scala Pattern Match的程度(太伤感了,要是有就太赞了)

when可以当做表达式,也可以当做语句来使用

when 语句示例

when (x) {
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> { // Note the block
    print("x is neither 1 nor 2")
    }
}

合并多个条件

when (x) {
    0, 1 -> print("x == 0 or x == 1")
    else -> print("otherwise")
}

对条件分支使用表达式

when (x) {
    parseInt(s) -> print("s encodes x")
    else -> print("s does not encode x")
}

在分支中使用 inis

when (x) {
    in 60..70 -> println("D")
    else -> println("Else")
}

when (x) {
    is String -> println("x is String. length: ${x.length}") // 自动cast
    is Int -> println("x is Int")
    else -> println("Have no idea")
}

for 语句

for (xxx in yyy) {} 语法结构可以用在任何含有 iterator() 的对象上。

更具体的讲,iterator() 方法返回的类型中需要含有

  • operator修饰的 next() 方法
  • operator修饰的 hasNext() 方法

使用示例

fun main(args: Array<String>) {
    val wr = Wrapper("First", Wrapper("Second", Wrapper("Third")))
    for(w in wr) {
        print(w)
    }
}

class Wrapper(val content: String) {

    public var wrap: Wrapper? = null

    constructor(content: String, wrap: Wrapper) : this(content) {
        this.wrap = wrap
    }

    operator fun iterator() = WrapperIterator(this)

    override fun toString(): String {
        return this.content + "=>"
    }

}

class WrapperIterator(var current: Wrapper?) {

    operator fun next(): Wrapper? {
        val tmp = current
        current = current?.wrap
        return tmp
    }

    operator fun hasNext() = current !== null
}

for循环中使用索引

for ((index, value) in array.withIndex()) {
    println("the element at $index is $value")
}

Package 语法

Kotlin 的Package的路径不需要和文件目录结构一致

没有 import static

可以给import 的类起别名

比如你非常悲剧的从两个包下面导入了相同名称的类,在Java中你很可能要将其中一个使用的时候使用全限定名。但是Kotlin帮助很好的解决了这个问题

import foo.Bar
import boo.Bar as bBar

Enum

Kotlin 使用 Enum的方式和 Java类似

enum class Name(val type: String) {
    Tree("tree"), Bar("bar")
}

enum class ProtocolState {
    WAITING {
        override fun signal() = TALKING
    },
    TALKING {
        override fun signal() = WAITING
    };
    abstract fun signal(): ProtocolState
}

另外还有两个比较有用的方法

EnumClass.valueOf(value: String): EnumClass throws IllegalArgumentException
EnumClass.values(): Array<EnumClass>

高级语法特性

类库