kotlin 学习

Statically typed programming language
for the JVM, Android and the browser
100% interoperable with Java™

正如官网所说,kotlin 完全兼容 java 而不像 scala 一样,很多类库都是自己整一套。废话不多说,我们先去学学它的语法。

定义函数

1
2
3
fun max(a: Int, b: Int): Int {
return if (a > b) a else b
}

字符串模板

1
2
3
4
5
6
fun main(args: Array<String>) {
if (args.size > 0) {
val name = args[0]
println("Hello, $name!")
}
}

使用空值及 null 检查

  • 当某个变量的值可以为 null 的时候,必须在声明处的类型后添加 ? 来标识该引用可以为空值
1
2
3
4
5
6
7
8
9

fun strLenSafe(s: String?): Int =
if (s != null) s.length else 0

fun main(args: Array<String>) {
val x: String? = null
println(strLenSafe(x))
println(strLenSafe("abc"))
}

类型判断

is 运算符用于类型的判断

1
2
3
4

if(obj is String){

}

循环和迭代于区间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
for (i in 1..100) {
print(fizzBuzz(i))
}


for (c in 'A'..'F') {
val binary = Integer.toBinaryString(c.toInt())
binaryReps[c] = binary
}

for ((letter, binary) in binaryReps) {
println("$letter = $binary")
}


fun isLetter(c: Char) = c in 'a'..'z' || c in 'A'..'Z'
fun isNotDigit(c: Char) = c !in '0'..'9'

when 表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fun getMnemonic(color: Color) =
when (color) {
Color.RED -> "Richard"
Color.ORANGE -> "Of"
Color.YELLOW -> "York"
Color.GREEN -> "Gave"
Color.BLUE -> "Battle"
Color.INDIGO -> "In"
Color.VIOLET -> "Vain"
}



fun recognize(c: Char) = when (c) {
in '0'..'9' -> "It's a digit!"
in 'a'..'z', in 'A'..'Z' -> "It's a letter!"
else -> "I don't know..."
}

枚举 enum

1
2
3
4
5
6
7
8
9
10
enum class Color {
RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET
}


enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF)
}

创建数据 DTO (POJO/POCO)

  • 所有属性都有 getters , var 定义的还有 setters
1
data class Client(val name: String, var postalCode: Int)

数据复制

1
2
3
4
5
6
7
8
9
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)


val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)

val jane = User("Jane", 35)
val (name, age) = jane
println("$name, $age years of age") // prints "Jane, 35 years of age"

创建不可变集合

1
2
3
val set = setOf(1, 7, 53)
val list = listOf(1, 7, 53)
val map = mapOf(1 to "one", 7 to "seven", 53 to "fifty-three")

延迟属性

1
val emails by lazy { loadEmails(this) }

扩展函数

1
2
3
4
5
6

fun String.lastChar(): Char = this.get(this.length - 1)

fun main(args: Array<String>) {
println("Kotlin".lastChar())
}

内联函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

data class FileContent(val file: List<String>)

/**
* 扩展 Gson 方法
* 内联函数
* 实体化的类型参数,关键字 reified
*/

inline fun <reified T : Any> Gson.fromJson(json: String): T {
return fromJson(json, T::class.java)
}

fun main(args: Array<String>) {

val json = File(ClassLoader::class.java.getResource("/list.json").path).readText()

val result: FileContent = Gson().fromJson(json)

println(result)

}

with 的使用

当一个对象实例需要调用多个方法

1
2
3
4
5
6
7
8
9
10
fun alphabet(): String {
val stringBuilder = StringBuilder()
return with(stringBuilder) {
for (letter in 'A'..'Z') {
this.append(letter)
}
append("\nNow I know the alphabet!")
this.toString()
}
}

apply 的使用

1
2
3
4
5
6
7

fun alphabet() = StringBuilder().apply {
for (letter in 'A'..'Z') {
append(letter)
}
append("\nNow I know the alphabet!")
}.toString()

尾递归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

class Result(var value: BigInteger = BigInteger.valueOf(1))

/**
* 尾递归使用关键字 tailrec
*/
tailrec fun fac(num: Int, result: Result) {
if (num == 0) result.value else {
result.value = result.value.times(BigInteger.valueOf(num.toLong()))
fac(num - 1, result)
}
}

fun main(args: Array<String>) {

val result = Result()
fac(5, result)
print(result.value)
}

我们使用 kotlin 反编译字节码, 发现使用循环替代了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static final void fac(int num, @NotNull Result result) {
while(true) {
Intrinsics.checkParameterIsNotNull(result, "result");
if(num == 0) {
result.getValue();
return;
}

BigInteger var2 = result.getValue();
BigInteger var10001 = BigInteger.valueOf((long)num);
Intrinsics.checkExpressionValueIsNotNull(var10001, "BigInteger.valueOf(num.toLong())");
BigInteger var3 = var10001;
BigInteger var10000 = var2.multiply(var3);
Intrinsics.checkExpressionValueIsNotNull(var10000, "this.multiply(other)");
BigInteger var5 = var10000;
result.setValue(var5);
--num;
}
}

而 java 里面怎么进行尾递归优化呢, 一般使用循环而不是递归的方式。如下:

1
2
3
4
5
6
int factorial(int n) {
int result = 1;
for (int t=n; t > 1; t--)
result *= t;
return result;
}

数组的创建

1
2
3
arrayOf(1,2,3)

val asc=Array(5,{i->(i*i).toString()})

1
2
class Invoice {
}

构造函数

主构造函数

constructor 一般可以省略

1
2
class Person constructor(firstName: String) {
}

主构造函数初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class User constructor(_nickname: String) {
val nickname: String

init {
nickname = _nickname
}
}

class User(_nickname: String) {
val nickname = _nickname
}

class User(val nickname: String,
val isSubscribed: Boolean = true)

次构造函数

  • 类也可以声明前缀有 constructor 的次构造函数:
1
2
3
4
5
class Person {
constructor(parent: Person) {
parent.children.add(this)
}
}

如果类有一个主构造函数,每个次构造函数需要委托给主构造函数, 可以直接委托或者通过别的次构造函数间接委托。委托到同一个类的另一个构造函数 用 this 关键字即可:

1
2
3
4
5
class Person(val name: String) {
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}

私有构造

1
2
class DontCreateMe private constructor () {
}

创建类的实例

1
2
3
val invoice = Invoice()

val customer = Customer("Joe Smith")

类的继承

  • 继承使用 :
  • 在 Kotlin 中所有类都有一个共同的超类 Any,这对于没有超类型声明的类是默认超类:
  • 在 Kotlin 中所有的类都是 final. 除非有 open 修饰,否则子类不能继承
  • 类和成员默认 final
1
2
3
4
5
6
7
8
9
10
11
12
open class Base(p: Int)

class Derived(p: Int) : Base(p)


class MyView : View {
constructor(ctx: Context) : super(ctx) {
}

constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) {
}
}

方法的覆写

  • 使用关键字 override
  • 需要能被子类 override 的方法 需要加上关键字 open
1
2
3
4
5
6
7
open class Base {
open fun v() {}
fun nv() {}
}
class Derived() : Base() {
override fun v() {}
}

使用 super 来指定 实现那个父类的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
open class A {
open fun f() { print("A") }
fun a() { print("a") }
}

interface B {
fun f() { print("B") } // 接口成员默认就是 'open' 的
fun b() { print("b") }
}

class C() : A(), B {
// 编译器要求覆盖 f():
override fun f() {
super<A>.f() // 调用 A.f()
super<B>.f() // 调用 B.f()
}
}

伴生对象

  • 一个对象声明在一个类里可以标志上 companion 这个关键字:
1
2
3
4
5
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}

内部类 inner

1
2
3
4
5
6
7
8
9

class Outer {
private val bar: Int = 1
inner class Inner {
fun foo() = bar
}
}

val demo = Outer().Inner().foo() // == 1

最终类

使用 sealed 关键字修饰

1
2
3
4
5
sealed class SealedClassTest {

object getStop : Opertion()
object getStart : Opertion()
}

委托类

  • 使用关键字 by

  • 在父类 Derived 中的 by- 语句表示 b 将会被 储存在 Derived 的内部对象中 并且编译器会生成所有的用于转发给 b 的 base 的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

interface Base {
fun print()
}

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

class Derived(b: Base) : Base by b

fun main(args: Array<String>) {
val b = BaseImpl(10)
Derived(b).print() // prints 10
}

抽象类

  • 我们并不需要用 open 标注一个抽象类或者函数——因为这不言而喻。
1
2
3
4
5
6
7
open class Base {
open fun f() {}
}

abstract class Derived : Base() {
override abstract fun f()
}

接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface MyInterface {
val property: Int // abstract

val propertyWithImplementation: String
get() = "foo"

fun foo() {
print(property)
}
}

class Child : MyInterface {
override val property: Int = 29
}

多接口实现冲突解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

interface A {
fun foo() { print("A") }
fun bar()
}

interface B {
fun foo() { print("B") }
fun bar() { print("bar") }
}

class C : A {
override fun bar() { print("bar") }
}

class D : A, B {
override fun foo() {
super<A>.foo()
super<B>.foo()
}
}

使用 retrofit2 网络请求数据

  1. 依赖包:
1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-gson</artifactId>
<version>2.1.0</version>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.GET

/**
* Created by feel on 2016/12/14.
* https://api.github.com/repos/Bestfeel/spring-cloud-project/stargazers
*/

interface IContent {

@GET("/repos/Bestfeel/spring-cloud-project/stargazers")
fun getFiles(): Call<List<FileDonwload>>
}

data class FileDonwload(val login: String, val id: Int, val url: String)


object Service {
val getContent: IContent by lazy {
Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://api.github.com")
.build().create(IContent::class.java)
}
}

fun main(args: Array<String>) {

val body = Service.getContent.getFiles().execute().body()

print(body)

body.map(::println)


}

泛型 (协变)

  • 在 java 中我们知道有通配符类型 ? extends T

如: Collection 表示为 Collection<? extends Object> 的子类型
类似于 scala 中的协变

  • 在 kotlin 中定义协变,使用关键字 out ,可以理解为生产者
1
2
3
4
5
6
7
8
 abstract class Source<out T> {
abstract fun nextT(): T
}

fun demo(strs: Source<String>) {
val objects: Source<Any> = strs // This is OK, since T is an out-parameter
// ...
}

逆变

  • 使用关键字 in
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

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

fun demo(x: Comparable<Number>) {
x.compareTo(1.0) // 1.0 has type Double, which is a subtype of Number
// Thus, we can assign x to a variable of type Comparable<Double>
val y: Comparable<Double> = x // OK!
}


fun fill(dest: Array<in String>, value: String) {
// ...
}

星 - 预测

1
2
3
Function<*, String> means Function<in Nothing, String>;
Function<Int, *> means Function<Int, out Any?>;
Function<*, *> means Function<in Nothing, out Any?>.

泛型约束 (上界于下界)

上界

1
2
3
4
5
6
fun <T : Comparable<T>> sort(list: List<T>) {
// ...
}

sort(listOf(1, 2, 3)) // OK. Int is a subtype of Comparable<Int>
sort(listOf(HashMap<Int, String>())) // Error: HashMap<Int, String> is not a subtype of Comparable<HashMap<Int, String>>
  • 多个上界

使用 where 子句

1
2
3
4
5
6

fun <T> cloneWhenGreater(list: List<T>, threshold: T): List<T>
where T : Comparable,
T : Cloneable {
return list.filter { it > threshold }.map { it.clone() }
}