Kotlin现在这么火,而且能直接兼容Java,在Android开发中,趋势就是用简洁的Kotlin逐步替代掉Java。Java我还没怎么学,不如就直接上Kotlin吧。Udacity刚好也有免费课程,推荐一下。
控制台
打开IntelliJ的IDE,tools-Kotlin-Kotlin REPL:
基本语法
单引号代表字符,双引号代表字符串:
1
2
val c = 'c'
val s = "string"
var 声明变量:
1
2
>> var a = 3
>> a = 4 // a等于4
val 声明常量:
1
2
>> val b = 5
>> b = 4 // error
加减乘除直接用,没毛病:
1
2
>> 3 + 4
>> 7
加减乘除functions:
1
2
>> 1.plus(8).minus(3).times(4).div(6) // (1+8-3)*4/6=24
>> 4
2种设定null值的方法:
1
2
var greenColor = null
var blueColor: Int? = null // 允许为空的整型变量
允许为空的数组:
1
2
3
4
>> listOf(null,null)
>> [null, null]
>> var list: List<Int?> = listOf(null, null) // 允许数组中的元素为空
>> var list2:List<Int>? = null // 允许数组为空
可变数组与不可变数组:
1
2
3
4
5
6
7
8
>> val myList = mutableListOf("tuna", "hadock", "shark") // 可变数组(即使myList是val类型)
>> myList.remove("shark") // myList不可被赋新值,但可在当前值的基础上进行操作
>> true
>> myList
>> [tuna, hadock]
>>
>> val myList = listOf("tuna", "hadock", "shark") // 不可变数组
>> myList.remove("shark") // error
带索引迭代数组:
1
2
3
4
5
6
7
8
>> var list1 = listOf("a", "b", "c")
>> for ((index, item) in list1.withIndex()) {
>> println("Index: $index; Value: $item")
>> }
>> Index: 0; Value: a
>> Index: 1; Value: b
>> Index: 2; Value: c
创建规律数组:
1
2
val array1 = Array(3, {it}) // [0, 1, 2]
val array2 = Array(5, { i -> (i * i).toString() }) // ["0", "1", "4", "9", "16"]
Elvis 操作符(?:):
啥是Elvis操作符?Google一下Elvis Presley,看看他的发型像不像?:
1
2
>> var nullTest: Int? = null // 定义一个允许为空的变量nullTest,赋值null
>> nullTest = nullTest?.inc() ?: 0 // 如果nullTest非空,则加1;如果为空,则赋值为0
设定范围(..):
1
2
3
4
5
6
7
8
9
10
11
>> if (50 in 1..50) println(50) // 如果50在1-50中,则打印50
>> 50
>>
>> for (i in 'b'..'g') print(i) // 从b到g
>> bcdefg
>>
>> for (i in 5 downTo 1) print(i) // 从5倒序到1
>> 54321
>>
>> for (i in 3..7 step 2) print(i) // 每2次打印1次
>> 357
when(相当于switch case):
1
2
3
4
5
when (50) {
0 -> println("empty")
in 1..50 -> println("1-50")
else -> println("others")
}
Kotlin入口文件:
1
2
3
fun main(args: Array<String>) {
// do something
}
在IntelliJ IDE中,点击Run-Edit Configurations,可配置args的value:
1
2
3
fun main(args: Array<String>) {
println("args[0] is ${args[0]}, args[1] is ${args[1]}") // args[0] is kotlin, args[1] is java
}
若要运行该文件,则可用ctrl+shift+R
,或使用如下2种方式:
表达式跳过赋值直接使用:
1
2
3
val value = 51
val msg = "You're a ${ if (value > 50) "good" else "okay" } man."
println(msg) // You're a good man.
生成随机数:
1
2
import java.util.*
println(Random().nextInt(6)) // 0-5的随机整数
定义循环名称:
1
2
3
4
5
6
7
8
9
abc@for (i in 1..10) {
if (i == 3) break@abc // 在名为abc的循环中,当i等于3,退出循环
print(i) // 12
}
从用户输入得到1个整数:
```java
val a = readLine()?.toIntOrNull() ?: 1 // toIntOrNull如果不能转换成整数,则转换成null;如果是null,则赋值为1
function示例
求和的function:
1
2
3
fun sum(a: Int, b: Int): Int {
return a + b
}
返回带有参数值的字符串:
1
2
3
fun printSum(a: Int, b: Int): Unit { // Unit代表不返回任何值
println("sum of $a and $b is ${a + b}")
}
给参数设置默认值:
1
2
3
4
5
6
7
8
fun basket(apple: Int, egg: Int = 5) { // 设置默认定值
println(egg)
}
basket() // error
basket(apple=3) // 5
basket(3) // 5
basket(apple=1, egg=3) // 3
basket(1, 3) // 3
1
2
3
4
5
6
7
fun getNewDefault() = 10
fun printData(targetData : Int = getNewDefault()) { // 以fun设置默认值
// 这种方法在未给出默认值,且fun过于复杂时,有内存溢出的危险
// 特别是getNewDefault的功能为读取文件等非常消耗内存的时候
// 因为每次执行printData function时,都会用getNewDefault function创建一个对象
}
function返回值:
1
2
3
4
5
6
7
8
fun basket() : String {
return "hello" // 返回hello
}
helper functions:
```java
fun isTooHot(temperature: Int) = temperature > 30
重复(repeat)function:
1
2
3
4
5
6
7
8
9
10
11
12
13
// 一般写法
repeat(3) {
println("I will run 3 times.")
}
// lambda传参写法
repeat(3, { it: Int -> anotherFunction(it)}) // 使用lambda传it到anotherFunction运行
fun anotherFunction(i : Int) {
print(i)
} // 012
// 另一种lambda写法
repeat(3, { it: Int -> println(it)}) // 012
筛选(filter) function:
1
2
3
4
5
6
7
8
9
10
11
val spices = listOf("curry", "pepper", "cayenne", "ginger", "red curry", "green curry", "red pepper" ) // 定义数组
spices.filter {it.contains("curry")} // 含有curry的元素
.sortedByDescending {it.length} // // 按长度降序排列:[green curry, red curry, curry]
spices.asSequence().filter {it.contains("curry")} // 懒加载保存起来:kotlin.sequences.FilteringSequence@13c90c06
// 3种方法,同一个结果,取首字母为c,末字母为e的元素
spices.filter {it[0] == 'c' && it.takeLast(1) == "e"} // [cayenne]
spices.filter {it.startsWith('c') && it.endsWith('e')} // [cayenne]
spices.filter {it.startsWith('c')}
.filter {it.endsWith('e')} // [cayenne]
// 在前3个元素中,取首字母为c的元素
spices.take(3).filter {it[0] == 'c'} // [curry, cayenne]
筛选(filter) 贪婪/懒惰加载:
1
2
3
4
5
6
7
8
9
10
11
12
13
val spices = listOf("curry", "pepper", "cayenne", "ginger", "red curry", "green curry", "red pepper" )
// 贪婪加载:直接执行,并赋值给eager
val eager = spices.filter {it.contains("curry")}
println(eager) // [curry, red curry, green curry]
// 懒惰加载:暂存序列,待需要的时候执行
val filtered = spices.asSequence().filter {it.contains("curry")} // kotlin.sequences.FilteringSequence@13c90c06
println(filtered.toList()) // [curry, red curry, green curry]
// 验证懒惰加载的demo
val lazyMap = spices.asSequence().map {
println("map: $it") // 不会打印任何项
it
}
println("Fitst item is ${lazyMap.first()}") // map: curry; first item is curry
匿名(lambda) function:
1
2
3
4
5
6
val printA = { println('a') }
printA() // a
val printX = { x : Char -> println(x) }
printX('b') // b
val divideThem : (Int, Int) -> Int = { dividend, divider -> dividend / divider }
divideThem(9, 3) // 3
匿名(lambda) function demo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var dirty = 20 // 设定初始鱼缸的肮脏度为20
val waterFilter: (Int) -> Int = { dirty -> dirty / 2 } // 净水器可把肮脏度 / 2
fun feedFish(dirty: Int) = dirty + 10 // 每喂一次食,肮脏度 + 10
fun updateDirty(dirty: Int, operation: (Int) -> Int): Int {
return operation(dirty) // 更新肮脏度的function,参数为"现有肮脏度"和"操作名称"
}
fun dirtyProcessor() { // 操作流程
dirty = updateDirty(dirty, waterFilter) // 净水,20 / 2 = 10。得益于lambda fun,dirty参数被直接传入waterFilter了
dirty = updateDirty(dirty, ::feedFish) // 喂食,10 + 10 = 20。由于是普通fun,为避免直接呼出,需要在前面加::
dirty = updateDirty(dirty) { dirty -> dirty + 50 } // 传入lambda参数,直接更新肮脏度: 20 + 50 = 70
// dirty = updateDirty(dirty, { dirty -> dirty + 50 }) // lambda fun也可以这样写,等价与上一条
}
lambda 随机数demo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 引入random
import java.util.*
val random = Random()
// fun类型的lambda,推荐使用
val rollDice: (Int, Int) -> Int = { from, to ->
if (to == 0) 0
else random.nextInt(to) + from
}
println(rollDice(1, 4)) // 返回随机数1-4
// 另一种写法,变量类型的lambda,没有声明返回类型,不易读,不推荐使用
val rollDice2 = { from: Int, to: Int ->
if (to == 0) 0
else random.nextInt(to) + from
}
println(rollDice(1, 4)) // 返回随机数1-4
类(class)
public:默认装饰符。可被任何地方看到。类和公有成员。 private:作用于class内部。子类无法。 protected:作用于class内部。子类可以看到。 internal:作用于模块(module)内部。
成员的定义方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 定义一个”盒子“类
class Box(var length: Int = 100,
var width: Int = 20,
var height: Int = 30) {
var volume: Int
get() = width * length * height / 1000
set(value) = { height = (value * 1000) / (width * length) }
}
fun main(args: Array<String>) {
val middleBox = Box() // 实例化
middleBox.length // 100cm
middleBox.volume // colume的getter function:100 * 20 * 30 / 1000 = 60 cm3
middleBox.volume = 80 // colume的setter function:设置容积为80cm3
middleBox.height // 高则会被设置为40cm,以达到所需容积
}
第二构造函数(secondary constructor)(不推荐直接使用,而用helper function取代):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Box(var length: Int = 100,
var width: Int = 20,
var height: Int = 30,
optionalValue: Int = 1) {
var volume: Int
get() = width * length * height / 1000
set(value) = { height = (value * 1000) / (width * length) }
constructor(numberOfCandy: Int): this() {
var volume = numberOfCandy * 2 // 假设1个糖果占2cm3,则算出所需容积
height = volume * 1000 / (width * length) // 根据容积算出盒子所需高度
}
}
fun main(args: Array<String>) {
val bigBox = Box(numberOfCandy = 50) // 注意:这里不能再传入width等参数,因为会与默认构造函数冲突
biglBox.height // 高度为:(50 * 2) * 1000 / (100 * 20) = 50
bigBox.volume // 容积为:100 * 20 * 50 / 1000 = 100
}
初始化(init)构造函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Fish(val friendly: Bollean = true, volumeNeeded: Int) {
val size: Int
init {
if (friendly) {
size = volumeNeeded
} else {
size = volumeNeeded * 2
}
}
}
fun main(args: Array<String>) {
val shark = Fish(false, 5)
// 如果不带val或var,volumeNeeded将不会被保存到成员中。也就是说shark.volumeNeeded不存在
shark.volumeNeeded // error
shark.size // 10
}
初始化构造函数和第二构造函数的注意事项:
- 运行顺序:默认构造函数 -> 初始化构造函数 -> 第二构造函数
- 初始化构造函数可以有多个,运行顺序自上而下。如果要用init,须确保init中的属性已经存在
- 一般不用第二构造函数。如果必须要用,则建议用helper function创建基于第二构造函数的实例
举例:
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
39
40
class Fish(val friendly: Boolean = true, volumeNeeded: Int) {
val size: Int
init {
println("First init block")
}
constructor() : this(true, 1) {
println("This is secondary constructor")
}
init {
if (friendly) {
size = volumeNeeded
} else {
size = volumeNeeded * 2
}
}
init {
println("Size is ${this.size}, volumeNeeded is $volumeNeeded")
}
init {
println("Last init block")
}
}
fun makeDefaultFish() = Fish(true, 1) // helper function,目的是使用第二构造函数
fun main(args: Array<String>) {
val cod = Fish(true, 3)
println("This fish is friendly? ${cod.friendly}, it needs volume ${cod.size}")
}
/* 结果:
First init block
Size is 3, volumeNeeded is 3
Last init block
This fish is friendly? true, it needs volume 3
*/
(未完待续)