Step into Kotlin

 

Step into Kotlin - 01 - 入门

摘要

配置环境

入门

简介

Kotlin 是一门静态语言,可以编译成 Java或 Javascript。Kotlin 由 JetBrains 公司开发,所以开发工具是 IDEA。

配置环境

  1. 安装 IDEA 插件,plugins 中检索 Kotlin 下载并安装,安装完后重启 IDEA
  2. 建立 Maven 工程,配置 pom.xml
<properties>
        <kotlin.version>0.1-SNAPSHOT</kotlin.version>
    </properties>

    <repositories>
        <repository>
            <id>sonatype.oss.snapshots</id>
            <name>Sonatype OSS Snapshot Repository</name>
            <url>http://oss.sonatype.org/content/repositories/snapshots</url>
            <releases>
                <enabled>false</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>sonatype.oss.snapshots</id>
            <name>Sonatype OSS Snapshot Repository</name>
            <url>http://oss.sonatype.org/content/repositories/snapshots</url>
            <releases>
                <enabled>false</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>


    <dependencies>

        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-stdlib</artifactId>
            <version>${kotlin.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>


    <build>
        <plugins>
            <plugin>
                <artifactId>kotlin-maven-plugin</artifactId>
                <groupId>org.jetbrains.kotlin</groupId>
                <version>${kotlin.version}</version>

                <configuration/>
                <executions>
                    <execution>
                        <id>compile</id>
                        <phase>process-sources</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>test-compile</id>
                        <phase>process-test-sources</phase>
                        <goals>
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>

        <sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
        <testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
    </build>
  •  

第一个 Kotlin 程序

右键选择新建 Kotlin File,命名为 Hello.kt

fun main(args: Array<String>) {
    println(getHelloString())
}

fun getHelloString(): String {
    return "Hello World!"
}

运行后可以看到控制台输出 “Hello World!”。

 

Step into Kotlin - 02 - 数据类型

目录

 

 

摘要

显示转换,String templates

Basic Types

概要

Kotlin 中一切皆是对象

Numbers

分类

与 Java 相似,但是 Kotlin 没有自动向上转型( 如 Java 中的 int 可以自动向上转型为 long )。

TypeBitwidth
Double64
Float32
Long64
Int32
Short16
Byte8

Kotlin 中字符不是 Number

Literal Constants

  • Decimals: 123 
    • Longs are tagged by a capital L: 123L
  • Hexadecimals: 0x0F
  • Binaries: 0b00001011

Long 结尾不能为小写的 l 
Kotlin 不支持 8 进制字面常量

Representation

数字的装箱操作不会保持相同性,但是会保持相等性

val a: Int = 10000
val boxedA: Int? = a
val anotherBoxedA: Int? = a
println(a identityEquals a) // Prints 'true'
println(boxedA identityEquals anotherBoxedA) // !!!Prints 'false'!!!

println(a == a) // Prints 'true'
println(boxedA == anotherBoxedA) // Prints 'true'

Explicit Conversions

语法

所有 Number 类型都支持类似 toByte(),toInt() 这样的显示转换的方法。

val a:Int = 1
// val b:Long = a 错误,不支持自动向上转型
val b:Long = a.toInt()

字面量

字面量可以根据上下文推导类型,所以可以不用显示转换

val a:Byte = 1
val b:Long = 1
b = 1.toLong() + 2 //Long + Int => Long

为什么不支持自动向上转型

因为 Kotlin 最终会被编译为 Java 代码,比如说

val a:Int = 2   // java.lang.Integer a = 2
// 如果支持向上转型
val b:Long = a  // java.lang.Long b = a

假设支持向上转型,那么 a 就可以赋值给 b,这样 a==b 表达式会返回 false,因为两者类型不同,而这种错误只有在运行时才会发现。 
Kotlin 通过强制使用类型转换,使编译时就能发现这种错误,从而提高了程序的安全性。

Operations

Kotlin 支持标准的操作符,并且在对应的类上定义为成员方法。

常用操作

  • shl(bits) – 有符号左移 (Java’s <<)
  • shr(bits) – 有符号右移 (Java’s >>)
  • ushr(bits) – 无符号右移 (Java’s >>>)
  • and(bits) – 按位与
  • or(bits) – 按位或
  • xor(bits) – 按位异或
  • inv() – 按位取反

val b = 1 shl 2 //4
  • 1

Characters

Char 使用单引号 '' 来声明,不能直接当做数字来使用,需要使用 toInt()等方法

val c: Char = 'a'
  •  

Booleans

Boolean 值为 true 或者 false

val flag: Boolean = true && false
  •  

String

Usage

val s = "Hello World"
for (x in s) {
    print(x)    //  Hello World
}

Templates

String templates 可以使用变量值来替换字符串中的值

val name = "Peter"
val str = "name=$name, $name.length, ${name.length()}"
println(str)    //  name=Peter, Peter.length, 5
  •  

 

 

Step into Kotlin - 03 - 变量,常量与表达式

目录

 

 

摘要

声明变量,声明常量,表达式

变量,常量与表达式

变量

使用 var 关键字声明,类型写在变量名后

var x = 5
var y: Int = 5

常量

使用 val 关键字声明,又称作 “值”

val a: Int = 1
val b = 2
val c: Int
c = 3

表达式

Kotlin 也是一门面向表达式的语言,所以可以直接将一个表达式绑定在特定的变量上。

val z = if (a > b) a else b

 

Step into Kotlin - 04 - 函数

目录

 

 

摘要

创建函数,调用函数,默认参数,带名参数,可变长参数,Unit,函数作用域

函数

定义函数

格式

fun functionName(argumentName: arguemntType): returnType {function body}
  • 1
  • 以 fun 关键字进行定义,函数只有一句时可以加上 “=” 省略 “{}”
  • 参数名写在参数类型之前

//  只有一句时
fun addOne(m: Int): Int = m + 1

fun addTwo(m: Int): Int {
    return m + 2
}

调用函数

addOne(2)
  • 1

函数参数

默认参数

调用某些方法时可以不指定所有参数,而使用默认值

fun decorate(str: String, left: String = "[", right: String = "]") = left + str + right

println(decorate("abc"))    //  [abc]

带名参数

可以在调用函数时,指定参数的名称

decorate("abc", right = ">")
  • 1

可变长度参数

  • 可变长参数使用 vararg 修饰
  • 使用变长参数时只能一个个传值,不能直接使用外部的 Array,除非使用 * 表示将 Array 中的每个元素当做参数处理
fun capitalizeAll(vararg args: String): List<String> {
    return args.map { arg ->
        arg.capitalize()
    }
}
println(capitalizeAll("a", "b", "c"))  //  [A, B, C]

val array = arrayOf("d", "e", "f")
println(capitalizeAll("a", "b", "c", *array))   //  [A, B, C, D, E, F]

Unit

当返回类型为 Unit 时,表示没有返回值,相当于 Java 中的 void,此时也可以省略 Unit 的声明。

fun echo(s: String): Unit {
    println(s)
}

fun echo2(s: String) {
    println(s)
}

函数作用域

Kotlin 中函数可以直接定义在文件中,而不需要依托任何类。其中定义在 class 或 object 中的函数又被称为成员函数 (Member Function)。除此之外,Kotlin 中还有一种特殊的函数:本地函数 (Local Function),本地函数是定义在函数内部的函数。

fun factorialTail(n: Int): Int {
    val zero = 0
    //  本地函数 loop()
    fun loop(acc: Int, n: Int): Int =
            if (n == zero) acc else loop(n * acc, n - 1)
    return loop(1, n)
}

本地函数可以访问外部函数的私有成员

 

Step into Kotlin - 05 - 流程控制

目录

 

 

摘要

if,for,Range 操作(指定步长,逆序,递减)

流程控制

条件表达式

  • if/else 结构与 Java 类型,不过由于 Kotlin 面向表达式,所以可以直接将表达式赋给一个变量。
val s = if (x > 0) 1 else -1
  • 1

这种方式可以给常量赋值,有点类似 Java 中的三元操作符,此时 s 的类型为 Int

  • 如果是下面这种混合型的,s 的类型就为 Any
val s = if (x > 0) "a" else 65
  • 1
  • 因为 Kotlin 中表达式都必须有值,所以如果没有 else 的话,需要引入 Unit 类
val s3: Any = if (x > 0) 1 else Unit
  • 1
  • Kotlin 在 if 语句块的最后可以自动返回最后一行表达式的值,而不需要写 return 语句
val max = if (1 < 2) {
    println("a")
    2
} else {
    println("b")
    1
}

循环

while 和 do-while

Kotlin 中的 while 和 do-while 类似 Java

for

基本使用

val arr = intArrayOf(1, 2, 3)
for ( a in arr) {
    println(a)
}

用于字符串上

for (s in "Hello")
    print(s)

Range

概述

Range 使用特殊符号 .. 表示,可以结合 if 和 for 语句使用。

If

在指定范围内

val x = 3
val y = 10
if (x in 1..y - 1)
    println("OK")
else
    println("OUT")

在指定范围外

val arr = intArrayOf(1, 2, 3, 4, 5)
if ( x !in 0..arr.lastIndex)
    println("OUT")

也可结合 String 来使用

val str = "abcdef"
if (str in "abc".."abe") {
    println(str)    //  abcdef
}

For

for (x in 1..n)
    println(x)

此表达式表示让变量 i 遍历 in 右边的表达式的所有值 ( “1” 到 “n” ) 
变量 i 不需要事先定义,循环变量的作用域会一直持续直到循环结束。

除了基本的关键字 in 之外,也可以使用 step 控制步长,使用 downTo 进行递减操作。

for (z in 4 downTo 1)
    print(z)    //  4321

for (z in 1..4 step 2)
    print(z)    //  13

for (z in 4 downTo 1 step 2)
    print(z)    //  42

for (z in (1..4).reversed())
    print(z)    //  4321

break 和 continue

Kotlin 提供 break 和 continue 来退出循环,用法与 Java 相似。

 

Step into Kotlin - 06 - 异常

目录

 

 

摘要

try…catch,try 表达式

异常

  • Kotlin 没有检查异常
try {
    throw IOException()
} catch(e: IOException) {
    println("io exception")
} catch(e: Exception) {
    e.printStackTrace()
} finally {

}
  •  

Kotlin 中 try 语句也是种表达式,所以可以将它绑定到一个变量上

val a: Int = try {
    val x: Int = 1 / 0
    x
} catch(e: Exception) {
    -1
}

 

Step into Kotlin - 08 - 数组

目录

 

 

摘要

创建数组(3种方法),空数组,访问元素,遍历数组,检查下标

数组

概述

  • Array 类代表着数组
  • [] 可以用于访问数组的元素,实际上 [] 被进行了操作符的重载,调用的是 Array 类的 setter 和 getter 方法

创建数组

直接指定长度

val fixedSizeArray = arrayOfNulls<Int>(5)
  • 1

使用装箱操作

val arr = arrayOf(1, 2, 3)
val intArr = intArrayOf(1, 2, 3)    //同理还有 booleanArrayOf() 等
  • 1
  • 2

使用闭包进行初始化

val asc = Array(5, { i -> i * i })  //0,1,4,9,16
  • 1

空数组

 val empty = emptyArray<Int>()
  • 1

长度为 0 的空数组

访问数组元素

val arr = arrayOf(1, 2, 3)
println(asc[1])         //  1
println(asc.get(1))     //  1
//    println(asc[10])      ArrayIndexOutOfBoundsException

[] 虽然调用的是 setter 和 getter 方法,但是编译成字节码时会被进行优化,变成直接访问数组的内存地址,所以不会造成性能损失。

修改元素

asc[1] = 10
  • 1

遍历数组

for (i in asc) {
    println(i)
}

此操作不会创建 iterator 对象,不会影响性能

遍历数组下标

for (j in asc.indices) {
    println(j)
}

此操作不会创建 iterator 对象,不会影响性能

检查下标

if (i in asc.indices) { // i>=0 && i<asc.size()
    println("indices:" + i)
}
  •  

Step into Kotlin - 10 - 映射及多重声明

目录

 

 

摘要

创建不可变映射,创建可变映射,访问元素,更新元素,遍历,多重声明

Map 及多重声明

Map

构造映射

不可变映射

val map = mapOf("a" to 1, "b" to 2, "c" to 3)
  • 1

可变映射

val mMap = hashMapOf("a" to 1, "b" to 2, "c" to 3)
  • 1

访问元素

mMap.get("d")
  • 1

如果试图访问不存在的 key 时,会抛出 NullPointerException 异常,所以需要在访问前先进行判断

val x = if (mMap.containsKey("e")) mMap.get("e") else 0
  • 1

也可以使用上述的简写方式

mMap.getOrDefault("e", 10)
  • 1

更新元素

可变映射

更新或插入新元素

mMap.put("d", 20)
  • 1

删除元素

mMap.remove("c")
  • 1

不可变映射

不可变映射不可以被修改

遍历

遍历 entry

for ((k, v)in map) {
    println("$k -> $v")
}

只遍历 key 或 value

val keys = map.keySet()
val values = map.values()
for (k in keys) {
}

多重声明

只要类实现了 componentN() 方法,该类的对象就可以实现多重构造

class Person(val name: String, val age: Int) {
    fun component1(): String {
        return name
    }

    fun component2(): Int {
        return age
    }
}


val person = Person("Jane", 20)
val  (name, age) = person   //  多重声明
val pname = person.component1()
println(name.toString() + ", " + age + ", " + pname)    //  Jane, 20, Jane

以上 Person 实现了 component1 和 component2 方法,所以多重声明时第一个参数赋值给 name,第二个参数赋值给 age。

 

Step into Kotlin - 11 - 类

目录

 

 

摘要

定义类,Empty 类,实例化对象,访问权限,Setter 与 Getter,backing field,主构造器,辅助构造器

定义类

class Counter {
    private var value = 0
    var defaultValue = 1
    public var publicValue: Int = 2

    fun increment(): Unit {
        value += 1
    }

    fun current() = value
}
  • 一个 Kotlin 源文件中可以包含多个类。
  • 声明为 public 的成员必须显示指定类型。

Empty 类

Empty 类没有类的实体,可以用于表示概念

class Empty
  • 1

实例化对象

val counter = Counter()
counter.increment()
//    println(counter.value)    can not access
println(counter.publicValue)
println(counter.defaultValue)
  • 实例化对象时不使用关键字 “new”

Setter 和 Getter

  1. 默认定义一个属性时,编译器会自动为该属性生成拥有 public setter 和 getter 方法的 private 属性。且直接调用对象的属性时实际是调用对应的方法

  2. 如果定义属性为private 的话,该属性的 setter 和 getter 也会变为 private。

class Person {
    //  默认 getter 与 setter
    var address = ""
    public var age: Int = 0
}

val person = Person()
p1.age = 10
println(p1.age) //  10

p1.trueAge = 20
p1.trueAge = 14
println(p1.trueAge)  //  20

只生成 Getter

当声明的属性为 val 时,该属性只会生成 getter 方法

val birthday = Date()
  • 1

自定义 getter 与 setter

class Person {

    //  自定义 getter 与 setter
    var trueAge: Int
        get() = age
        set(pAge) {
            if (pAge > age) {
                age = pAge
            }
        }

    //  自定义的同时改变访问权限
    var setterVisiblity: String = "abc"
        private set
}

这里需要特别注意的是由于调用属性实际是调用方法,所以如果上述改为 get() = trueAge 实际会不停调用自身,造成栈溢出。

backing field

  • backing field 主要用于在自定义 setter 和 getter 时可以直接访问属性,而不是方法,从而防止栈溢出。
  • backing field 以字符 $ 开头,在使用前必须先被初始化。
class Person {
    var backAge: Int = 0
        get() = $backAge
        set(pAge) {
            if (pAge > $backAge) {
                $backAge = pAge
            }
        }
}

构造器

概述

  • 构造器分为主构造器和辅助构造器
  • 默认一个类总会有一个无参的主构造器

辅助构造器

  • 辅助构造器名为 constructor
  • 辅助构造器可以通过 this() 调用其它的辅助构造器,但是任何一个辅助构造器必须调用主构造器
//调用主构造器
constructor(name: String) : this(name, 0) {
}
constructor(age: Int) : this("Default Name", age) {
}
constructor() : this("Default Name") {
}

主构造器

主构造器直接定义在类名后

class Man(val name: String, val age: Int, private var from: String = "USA", description: String = "none") {

    init {
        println("sentences in primary constructor")
    }
}
  • 主构造器会执行类中 init 代码块中得所有语句,所以每实例化一个对象,上述例子都会打印一条语句
  • 主构造器中可以使用默认参数,如上述的 from
  • 主构造器中定义的参数可以是 val 也可以是 var,定义的参数会自动转换为该类的成员属性
  • 主构造器中的参数也可以不加 var 或 val,此时如果类中的方法有用到该参数,则此参数转换为成员属性,否则会被忽略
  • 主构造器也可以被声明为 private,这样就只能通过辅助构造器来实例化该对象

私有主构造器

class Woman private constructor(val name: String, val age: Int) {
    constructor(name: String) : this(name, 0) {

    }
}
  •  

 

Step into Kotlin - 12 - Object 与枚举

目录

 

 

摘要

创建单例对象,创建伴生对象,伴生对象与类的关系,使用对象表达式,创建枚举

Object 与 Enumeration

Object

对象声明

单例对象

  • 与类不同,单例对象使用 object 进行声明
  • Kotlin 没有静态属性和方法,需要使用单例对象来实现类似的功能。
  • 对象的构造器不能提供构造器参数,在第一次使用时会被初始化。
object Singleton {
  private var num = 0

  fun sequence(): Int {
    num += 1
    return num
  }
}

Singleton.sequence()

单例对象可以用于提供常量及共享不可变对象。

伴生对象

  • 伴生对象可以用于让一个类即拥有实例化方法又有静态方法。
  • 伴生对象必须声明在类中,且使用 companion object 关键字进行声明。
  • 伴生对象与类可以互相访问各自的私有成员。
class Companion(private var balance: Int = 0) {
    companion object Factory {
        private var num = 0

        fun create(): Companion = Companion()

        private fun sequence(): Int {
            num += 1
            return num
        }


        fun getInfo(comp: Companion): String {
            return "balance is " + comp.balance
        }
    }

    val id = Companion.sequence()
}

对象表达式

对象表达式用于对类的功能进行修改,但是又不用显示创建类,类型 Java 中的匿名类。

open class A(x: Int) {
    public open val y: Int = x
}

interface B {
    fun info()
}

首先定义了一个类和一个接口,其中 open 用于表名该类可以被继承。

val ab = object : A(1), B {
    override fun info() {
        println("info")
    }

    override val y: Int
        get() = 15
}
println(ab.y)

以上值 ab 继承了实现了 A 和 B,并重写了其两个方法,实际上本质是创建了匿名类。

对象表达式也可以没有任何父类或父接口

val adHoc = object {
    var x: Int = 1
    var y: Int = 2
}
println(adHoc.x)

对象声明与对象表达式的区别

  • 对象声明是延迟初始化的,直到第一次访问才会被初始化。
  • 对象表达式在使用时则会被立即初始化。

Enumeration

构造枚举

enum class TrafficColor(val info: String) {
    RED("stop"), YELLOW("warning"), GREEN("walking"), BLUE("walking")
}

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

使用枚举

//直接获得枚举
var green = TrafficColor.GREEN

TrafficColor.GREEN.name()   //返回  name
TrafficColor.GREEN.ordinal()    //返回 id

 

Step into Kotlin - 13 - 包

目录

 

 

摘要

创建包,导入包,默认包,导入别名,作用域

创建包

类似 Java,使用 package 进行声明,但是包名和文件夹名可以不一致。

package a.b
  •  

导入包

类似 Java,使用 import 进行声明

import foo.info
  •  

默认包

如果一个 kotlin 源文件中没有任何包声明,则其当中的代码均属于默认包,导入时包名即为函数名

fun hello() {
    println("hello, Default Package")
}

import hello
hello()

导入别名

导入类时可以为类起一个别名

import foo.bar as b
b.bar()

作用域

  • private 在声明范围及同模块的子作用域内可见
  • protected 类似 private,但是对子类也可见
  • internal 默认作用域,同模块中都可见
  • public 总是可见

注意:在 Java 中不同包名没有任何直接关系。

 

Step into Kotlin - 14 - 继承

目录

 

 

摘要

继承类,重写方法,实现接口,指定实现的父类方法,抽象类

继承

扩展类

  • 使用 : 符号
  • 默认所有类都是 final 的,如果希望类能够被继承,需要声明为 open class
  • 子类构造函数需要实现父类的构造函数
  • 由于辅助构造器必须调用主构造器或其它辅助构造器,所以辅助构造器永远不可能直接访问父类的构造器
open class Person(name: String) {}
class Employee(name: String) : Person(name)  {}

重写方法

  • 方法默认为 final的,如果希望被重写需要声明为 open fun
  • final 修饰的方法无法被重写
  • 重写方法时必须加上 override 关键字,override 方法默认为 open 的
  • 调用超类方法时也是使用 super 关键字
// 父类
open fun idf() {}
open fun idf2() {}
// 子类
override fun idf() {
    super.idf()
}
// 无法被重写
final override fun idf2() {
    super.idf2()
}

接口

  • 接口类似 Java8,支持方法,抽象方法和抽象属性
  • 接口及其中的方法默认是 open 的

定义接口

interface A {
    val prop: Int

    fun foo() {
    }

    fun bar()
}

实现接口

class B : A {
    override fun bar() {
        throw UnsupportedOperationException()
    }

    override val prop: Int
        get() = throw UnsupportedOperationException()
}

多重实现

如果一个类需要实现的类或接口中包含有同名方法,可以使用 <> 来指明需要调用哪个父类方法

open class TextView : View, OnClickListener {

    constructor(size: Int) : super(size)

    // onClick must be overridden
    override fun onClick() {
        super<View>.onClick()   //  call to View.onClick()
        super<OnClickListener>.onClick()
    }
}

抽象类

抽象类类似接口,默认也是 open 的,但是必须使用关键字 abstract 显示声明抽象成员。

abstract class Shape {
    abstract fun onClick()
}
class Rectangle : Shape(), OnClickListener {
    // not required to override onClick
}

以上 Shape 为抽象类,所以需要调用默认构造方法 (),而 OnClickListener 为接口,则不需要这样做。

 

Step into Kotlin - 15 - 注解

目录

 

 

摘要

声明注解,使用注解,带参数的注解

注解

声明注解

annotation class 注解名
  •  

使用注解

格式

@注解名
  •  
  • 注解可以用在类,方法,参数,变量上
  • 除了表达式和本地声明,其余符号 “@” 都可以省略

fancy class Foo {
    fancy fun baz(fancy foo: Int): Int {
        @fancy fun bar() {
        }
        return (@fancy 1)
    }
}

注解在主构造器上

主构造器必须加上关键字 “constructor”

class Foo3 @fancy constructor() {
    //  annotate property accessors
    var x: Int = 1
        @fancy set
}

带参数的注解

annotation class special(val why: String)
special("example") class Foo

注解 Lambda 表达式

var f = @fancy { println("lambda") }

 

Step into Kotlin - 16 - Data 类与 When 表达式

目录

 

 

摘要

Data 类的声明和使用,When 的匹配

Data Class 与 When

Data Class

特点

  • Data 类是使用关键字 data 声明的类
  • Data 类默认基于构造方法实现了 toString()componentN()copy()equals() 和 hashCode() 方法,不在构造方法中定义的属性不会产生在 toString() 结果中。
  • Data 类可以直接使用 ==进行比较,同样不在构造方法中定义的属性不会用在比较上
  • Data 类只表示数据,不能拥有行为

创建 Data 类

data class Customer(var name: String, var email: String)
  •  

使用 Data 类

创建对象

val peter = Customer("Peter", "peter@example.com")
  •  

复制对象

val peter2 = peter.copy()
val peter3 = peter.copy(name = "")

ComponentN

val (name, email) = peter
println("name=$name,email=$email")

When

特点

  • When 类似 switch 但是功能更加强大,且不需要 break 语句。
  • 模式匹配可以匹配值,类型

When 中的表达式

When 中可以使用的表达式类型

  • 类名,用于进行类型匹配
  • 范围,用于匹配范围
  • 函数,用于匹配参数
  • else,匹配其它情况

匹配值和范围

    val y = when (x) {
        1 -> 2
        3 -> 4
        3, 10 -> 30
        in 10..20 -> 20
        !in 20..30 -> 40
        else -> 0
    }

匹配类型

val x = 10
when (x) {
    is Int -> println("long")
    else -> println("else")
}

匹配参数

private fun add(x: Int): Int {
    return x + 1
}

val x = 10
when (x) {
    add(x) -> println("x=" + x)
    else -> println("else")
}

 

Step into Kotlin - 17 - 泛型

目录

 

 

摘要

定义泛型,使用泛型,in 与 out,逆变与协变,集合的边界

泛型

定义泛型

class Holder<T>(val t: T) {
    fun info() {
        println(t)
    }
}
  •  

使用泛型

var strHolder = Holder("a")
var intHolder = Holder(3)
strHolder.info()    //  a
intHolder.info()    //  3

In 和 Out

Out

参数注解 out 用于表示该泛型只能用于输出,例如作为返回值。同时也提供了协变的特性。

abstract class Source<out T> {
    abstract fun nextT(): T
}

协变特性

fun demo(strs: Source<String>) {
    val obj: Source<Any> = strs
}

In

参数注解 in 用于表示该泛型只能用于输入,例如作为参数。同时也提供了逆变的特性。

abstract class Comparable<in T> {
    abstract fun compareTo(other: T): Int
}

逆变特性

fun demo(x: Comparable<Number>) {
    val y: Comparable<Double> = x
}

集合的边界

Java 中集合作为参数时可以指定边界,Kotlin 中也有类似的做法

Array<in String>
  •  

以上类似 Java 中的 Array<? super String>

Array<out String>
  •  

以上类似 Java 中的 Array<? extend String>

 

Step into Kotlin - 19 - 正则表达式

目录

 

 

摘要

创建正则表达式,匹配所有,替换

正则表达式

构造正则对象

使用 String 的 toRegex 方法

val numPattern = "[0-9]+".toRegex()
  •  

为避免转义符的干扰,可以使用"""表示原样输出

匹配

返回所有匹配结果

for (matchResult in numPattern.matchAll("99 bottles, 98 bottles")) {
    println(matchResult.value)
}

返回第一条匹配结果

val first = numPattern.match("99 bottles, 98 bottles")
println(first?.value)

替换

val result = numPattern.replace("99 bottles, 98 bottles", "xxx")
println(result) //xxx bottles, xxx bottles

 

Step into Kotlin - 20 - 高阶函数

目录

 

 

摘要

函数字面值,函数参数,闭包与柯理化,方法扩展表达式,inline

高阶函数

概念

那些以函数作为参数或返回函数的函数被称为高阶函数。

函数字面值

Kotlin 中函数可以赋值给变量

两种写法

val sum = { x: Int, y: Int -> x + y }

val sum2: (Int, Int) -> Int = { x, y -> x + y }

函数作为参数

格式

(函数参数) => 返回值类型

fun add(f: (Int) -> Int) = f(10)
val x = add({ Int -> 2 })
println(x) //2

以上 add 函数接收另一个函数作为参数,并在函数体内调用此函数,所以 add 函数即为高阶函数。

闭包与柯理化

  • 闭包指由代码和非局部变量的自由变量组成的代码块
  • 柯理化指接收参数产生新的函数的函数,所以柯理化函数也是高阶函数
var sum3 = 0
val ints = intArrayOf(1, 2, 3)
ints filter { it > 0 } forEach {
    sum3 += it
}
print(sum3)

方法扩展表达式

val sum4 = fun Int.(other: Int): Int = this + other
1.sum4(2)
1 sum4 2
  • inline 注解

每个方法实际都是一个包含 Lambda 表达式的对象,inline 注解可以将每个 Lambda 表达式进行内联以提高性能,noinline 则相反。

inline fun inlineLock<T>(lock: Lock, body: () -> T, @noinline notInlined: () -> T): T {
    lock.lock()
    try {
        return body()
    } finally {
        lock.unlock()
    }
}

 

Step into Kotlin - 21 - I/O

目录

 

 

摘要

读取行,读取字符串

IO 操作

读取操作

逐行读取

var source = File("coffeetime-kotlin/myfile.txt")
val lines = source.readLines("UTF-8")
for (l in lines) {
    println(l)
}

读取到字符串中

val contents = source.readText("UTF-8")

 

Step into Kotlin - 22 - Xml

目录

 

 

摘要

解析 xml

Xml

解析 XML

xml

<rss>
    <channel>
        <title>Yahoo! Weather - Boulder, CO</title>
        <item>
            <title>Conditions for Boulder, CO at 2:54 pm MST</title>
            <forecast day="Thu" date="10 Nov 2011" low="37" high="58" text="Partly Cloudy"
                      code="29"/>
            <forecast day="Fri" date="11 Nov 2011" low="39" high="58"
                      text="Mostly Cloudy" code="28"/>
            <forecast day="Sat" date="12 Nov 2011" low="32" high="49" text="Cloudy"
                      code="27"/>
        </item>
    </channel>
</rss>

Kotlin

val f = File("coffeetime-kotlin/src/main/resources/weather.xml")

val builder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
println(builder.parse(f).toXmlString())

 

Step into Kotlin - 23 - 与 Java 交互

目录

 

 

摘要

Kotlin 调用 Java,Java 调用 Kotlin(类,伴生对象,重载,异常)

与 Java 互相调用

Kotlin 调用 Java

Java

public class JavaBean {

    private String name;

    public JavaBean(String name) {
        this.name = name;
    }

    public void hello() {
        System.out.println("hello,my name is " + name);
    }

    public boolean is(String name){
        return this.name.equals(name);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Kotlin

val javaBean = JavaBean("Peter")
javaBean.hello()

对于 is 等在 Kotlin 中是关键字而在 Java 中不是关键字的方法,在调用时需要加上 “ 符号。

javaBean.`is`("Peter")

Java 调用 Kotlin

调用类

Kotlin

class KotlinBean(val name: String) {
    fun hello() {
        println("hello,my name is " + name)
    }
}

Java

KotlinBean bean = new KotlinBean("Peter");
bean.hello();

调用伴生对象的方法

Java 调用伴生对象的方法时需要加上注解 platformStatic

Kotlin

class KotlinBean(val name: String) {
    fun hello() {
        println("hello,my name is " + name)
    }

    companion object {
        platformStatic fun foo() {
            println("companion foo")
        }

        fun bar() {
            println("companion bar")
        }
    }
}

Java

KotlinBean.foo();
  •  

以上 KotlinBean 的伴生对象中 foo() 对于Java 来说是静态方法,而 bar()则不是。

Java 重载

Kotlin 方法可以通过注解 jvmOverloads 实现 Java 调用中的方法重载

Kotlin

jvmOverloads fun f(a: String, b: Int = 0, c: String = "abc") {
}

Java

obj.f("str");
obj.f("str", 1);
obj.f("str", 1, "foo");

检查型异常

由于 Kotlin 没有 CheckException,所以如果需要在 Java 中抛出此种异常,需要添加注解 @throws

Kotlin

@throws(IOException::class) fun e() {
    throw IOException()
}

Java

try {
    obj.e();
} catch (IOException e) {
    e.printStackTrace();
}

 

Step into Kotlin - 24 - delegate

目录

 

 

摘要

类的委托,属性的委托,Lazy Properties,Observable Properties,Not Null,Map Val

Delegate

类的委托

类的委托使用 by 语句

创建接口和子类

interface Base {
    fun println()
}

class BaseImpl(val x: Int) : Base {
    override fun println() {
        println(x)
    }
}

建立委托类

class Derived(b: Base) : Base by b

以上 Derived 类 创建了所有来自 Base 的方法,并且委托一个传入的 Base 的对象 执行这些方法。

使用委托类

val b = BaseImpl(19)
b.println() //  19
Derived(b).println()    //  19

属性的委托

创建被委托类

class Delegate {
    fun get(thisRef: Any?, prop: PropertyMetadata): String {
        return "$thisRef, thank you for delegating '${prop.name}}' to me!"
    }

    fun set(thisRef: Any?, prop: PropertyMetadata, value: String) {
        println("$value has been assigned to '${prop.name} in $thisRef.'")
    }
}

创建委托类

class Example {
    var p: String by Delegate()
}

使用委托

val e = Example()
println(e.p)
e.p = "NEW"

内置属性委托

分类

Kotlin API 中有两种标准的属性委托方法,一种是 lazy properties,一种是 observable properties。

Lazy Properties

  • Lazy Properties 在第一次被访问时才会获取值。
  • Lazy Properties 是通过 lazy 方法来实现的。
  • lazy 方法接收一个Lambda 表达式,在第一次执行时会计算 Lambda 的值然后存储起来,之后访问的话会直接取得保存的值,而不会重新计算。
val lazy: String by lazy {
    println("computed!")
    "Hello ${System.currentTimeMillis()}"
}

println("lazy is ${lazy}")
println("lazy is ${lazy}")

lazy 是非线程安全的,线程安全的版本见下面的例子

val blockLazy: String by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
    println("computed2!")
    "Hello2 ${System.currentTimeMillis()}"
}

Observable Properties

  • Observable Properties 被存储在一个 Map 中,而不是单独的属性。
  • Observable Properties 可以设置监听值改变事件的监听器。
  • Observable Properties 是通过 observable()方法实现的。
  • observable()方法接收两个参数,第一个为初始值,第二个为监听的回调Handler,该 Handler 接收三个参数:被设置的属性,旧值,新值。
class User {
    var name: String by Delegates.observable("<no name>") {
        d, old, new ->
        println("$old -> $new")
    }
}

val user = User()
user.name = "first"
user.name = "second"

也可以使用 vetoable() 代替 observable(),该方法功能类似,但是返回布尔值,用以表示这次设置是否有效。

class User {
    var age: Int by Delegates.vetoable(0) {
        d, old, new ->
        println("$old -> $new")
        if (new < 20) true else false
    }
}

val user = User()
user.age = 10
println(user.age)
user.age = 20
println(user.age)
  •  

Not Null

由于 Kotlin 不允许定义任何非空的非抽象属性,但是实际使用时可能也需要定义这样的属性,这时就需要 not null 方法来实现。

class NotNullFoo {
    var bar: String by Delegates.notNull()
}

val foo = NotNullFoo()
foo.bar = "bar"

Map Val

Map Val 可以用于将所有属性保存在一个 Map 中,在使用 JSON 等数据格式或者实现某些动态特性时这种功能很有用。

class Account(val map: Map<String, Any?>) {
    val name: String by Delegates.mapVal(map) {
        o, p ->
        println("$o -> $p")
        "default name"
    }
    val age: Int by Delegates.mapVal(map) {
        o, p ->
        println("$o -> $p")
        0
    }
}

val account = Account(mapOf(
        "name" to "John",
        "age" to 25
))
println(account.name)
println(account.age)
  •  

mapVar()为以上的可变版本。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值