Kotlin基础
内容包括
- 声明函数、变量、类、枚举以及属性
- Kotlin中的控制结构
- 智能转换
- 抛出和处理异常
2.1基本要素:函数和变量
2.1.1 Hello ,word
fun main(args: Array<String>) {
println("hello world")
}
- 关键字fun用来声明一个函数。
- 参数类型写在它的名称后面。
- 函数可以定义在文件的外层,不需要把它放在类中。
- 数组就是类。和java不同,Kotlin没有声明数组类型的特殊语法。
- 使用println代替了System.out.println。
- 和许多其他现代语法一样,可以省略每行代码后面的分号
2.1.2 函数
fun main(args: Array<String>) {
println(max(3,4))
}
fun max(a:Int,b:Int):Int{
return if (a>b) a else b
}
在Kotlin中,if是表达式,而不是语句。
语句和表达式的区别在于,表达式有值,并且能作为另一个表达式的一部分使用;而语句总是包围着代码块的顶层元素,并且没有自己的值。
表达式函数体
可以让上面的函数变得更简单。
fun max(a:Int,b:Int): Int =if (a>b) a else b
或者
fun max(a:Int,b:Int) =if (a>b) a else b
注意只有表达式体函数的返回类型可以省略
2.1.3 变量
val question="adsflsjdfl"
val answer1 = 42
// 显示的指定变量类型
val answer2: Int = 42
如果变量没有初始化,需要显示地指定它的类型:
val answer2: Int
answer2 = 42
可变变量和不可变变量
声明变量的关键字有两个:
- val 使用val声明的变量不能在初始化之后再次赋值。它对应的Java的final变量。
- var 这种变量的值可以被改变。这种声明对应的是普通(非final)的Java变量。
默认情况下,应该尽可能地使用val关键字来声明所有的Kotlin变量。
在定义了val变量的代码块期间,val变量只能进行唯一一次初始化,如果编译器能确保只有唯一一天初始化语句会被执行
val message: String
if(1<0){
message="www"
}else{
message="qqq"
}
println(message)
注意,尽管val引用自身是不可变的,但是它指向的对象可能是可变的。例如
val languages = arrayListOf("Java")
languages.add("Kotlin")
println(languages)
2.1.4 更简单的字符串格式化:字符串模板
fun main(args: Array<String>) {
val name = if (args.size >0) args[0] else "Kotlin"
println("Hello $name")
}
这个例子介绍了一个新特性,字符串模板。
如果要在字符串中使用$符号需要对它进行转义:
println("\$x")
还可以引用更复杂的表达式,而不是仅限于简单的变量名称,只需要把表达式用花括号括起来:
fun main(args: Array<String>) {
if(args.size > 0){
println("Hello ${args[0]}")
}
}
2.2 类和属性
Java代码
public class Person {
private final String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
转换成Kotlin代码
class Person(val name: String)
只有数据没有其它代码通常被叫做值对象
2.2.1 属性
class Person(val name: String, var isMarried: Boolean)
name:是只读属性生成一个字段和一个getter方法
isMarried:可写属性,一个字段、一个getter和一个setter
2.2.2 自定义访问器
class Rectangle(val height: Int, val width: Int) {
var isSquare: Boolean = false
get() {
return height == width
}
}
每次获取isSquare值的时候都会调用泳get(){…}返回计算后的属性值
2.2.3 Kotlin源码布局:目录和包
Kotlin可以用关键字导入类和函数
Kotlin中包名可以和文件名不同
2.3 表示和处理选择:枚举和“when”
2.3.1 声明枚举类
enum class Color {
RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET
}
Kotlin中,enum是一个软关键字:只有当它出现在class前面时才有特殊的意义,在其他的地方可以把它当做普通的名称使用
可以给枚举类声明属性和方法
print(Color.ORANGE.rgb())
enum class Color(val r: Int, val g: Int, val b: Int) {
RED(255, 0, 0), ORANGE(255, 165, 0), YELLOW(255, 255, 0), GREEN(0, 255, 0), BLUE(0, 0, 255), INDIGO(75, 0, 130), VIOLET(238, 130, 238);
fun rgb()=(r*256+g)*256+b
}
如果要在枚举类中定义任何方法,就要使用分号把枚举常量列表和方法分开
2.3.2 使用“when”处理枚举类
fun getMnemonic(color: Color) =
when (color) {
Color.BLUE -> "blue"
Color.GREEN -> "green"
else -> "other"
}
和if相似when是一个有返回值的表达式
fun getMnemonic(color: Color) =
when (color) {
Color.BLUE, Color.RED -> "blue"
Color.GREEN -> "green"
else -> "other"
}
如果多个条件的返回值相同,可以把多个值合并到同一个分支,只需要用逗号隔开。
2.3.3 在“when”结构中使用任意对象
Kotlin中的when结构比Java中的switch强大的多。switch要求必须使用常量(枚举常量、字符串或者数字字面值)作为分支条件,和它不一样when允许使用任何对象。
fun mix(c1: Color, c2: Color) =
when (setOf(c1, c2)) {
setOf(Color.RED, Color.ORANGE) -> Color.RED
setOf(Color.GREEN, Color.INDIGO) -> Color.GREEN
else -> throw Exception("color error")
}
2.3.4 使用不带参数的“when”
2.3.3中的例子每次调用的时候会创建多个Set实例,频繁调用效率低下
fun mixOptimized(c1: Color, c2: Color) =
when {
c1 == Color.RED && c2 == Color.ORANGE || c2 == Color.RED && c1 == Color.ORANGE -> Color.RED
c1 == Color.GREEN && c2 == Color.INDIGO || c2 == Color.GREEN && c1 == Color.INDIGO -> Color.RED
else -> throw Exception("color error")
}
性能优化了但是影响了可读性
2.3.5 只能转换:合并类型检查和转换
interface Expr
class Num(val value: Int) : Expr
class Sum(val left: Expr, val right: Expr) : Expr
fun eval(e: Expr): Int =
if (e is Num) {
e.value
} else if (e is Sum) {
eval(e.left) + eval(e.right)
} else {
throw IllegalArgumentException("unknown expression")
}
println(eval(Sum(Sum(Num(1), Num(2)), Num(4))))
在Kotlin中通过is判断一个变量是否是某种类型
如果通过is检测过一个变量是某种类型,后面就不需要转换他实际上编译器为你执行了类型转换,我们把这种行为称为智能转换
使用as关键字来表示到特定类型的显式转换
val n = e as Num
2.3.6 重构:用“when”代替“if”
fun eval(e: Expr): Int =
when (e) {
is Num -> e.value
is Sum -> eval(e.left) + eval(e.right)
else -> throw IllegalArgumentException("unknown expression")
}
2.3.7 代码块作为“if”和“when”的分支
fun eval(e: Expr): Int =
when (e) {
is Num -> {
println("num: ${e.value}")
e.value
}
is Sum -> {
val left = eval(e.left)
val right = eval(e.right)
println("Sum: ${left}+${right}")
left+right
}
else -> throw IllegalArgumentException("unknown expression")
}
2.4 迭代事物:“while”循环和“for”循环
2.4.1 “while”循环
Kotlin 有while循环和do-while循环,他们的语法和Java中相应的循环没有什么区别:
while(condition){
/*...*/
}
do{
/*...*/
} while(condition)
2.4.2 迭代数字:区间和数列
区间本质上就是两个值之间的间隔,这两个值通常是数字:一个起始值,一个结束值。使用…运算符来表示区间:
val oneToTen = 1..10
Kotlin的区间是包含的或者闭合的,两边的值是区间的一部分
一个FizzBuzz小游戏
for(i in 1..100){
println(fizzBuzz(i))
}
fun fizzBuzz(i: Int) = when {
i % 15 == 0 -> "fizzBuzz "
i % 3 == 0 -> "fizz"
i % 5 == 0 -> "buzz"
else -> "$i"
}
in 、downTo、step和until
- in 可以迭代区间和集合
- downTo 递减
- step 跳过几个
- until 不包含连段的例如(1 until 100 不包含1和100)
2.4.3 迭代map
val binaryReps = TreeMap<Char,String>()
for (c in 'A'..'F'){
val binary = Integer.toBinaryString(c.toInt())
binaryReps[c]=binary
}
for ((letter,binary) in binaryReps){
println("$letter = $binary")
}
可以迭代的时候跟踪下标
val list = arrayListOf<String>("10","11","1001")
for ((inde,valu) in list.withIndex()){
println("$inde:$valu")
}
2.4.4 使用“in” 检查集合和区间的成员
使用in运符来检查一个值是否在区间中,或者它的逆运算,!in,来检测这个值是否不在区间中。下面展示了如何使用in来检测一个字符是否属于一个字符区间。
fun isLetter(c:Char) = c in 'a'..'z' || c in 'A'..'Z'
fun isNotDigit(c:Char) = c !in '0'..'9'
in运算符和!in运算符也适用于when表达式
fun recognize(c: Char) = when (c) {
in '0'..'9' -> "it is a digit"
in 'a'..'z', in 'A'..'Z' -> "It is a letter"
else -> "I do not know ..."
}
2.5 Kotlin中的异常
2.5.1 “try”"catch"和“finally”
fun rreadNumber(reader: BufferedReader): Int? {
try {
val line = reader.readLine()
return Integer.parseInt(line)
} catch (e: NumberFormatException) {
return null
} finally {
reader.close()
}
}
2.5.2 "try"作为表达式
rreadNumber(BufferedReader(StringReader("asd")))
fun rreadNumber(reader: BufferedReader) {
val number=try {
Integer.parseInt(reader.readLine())
} catch (e: NumberFormatException) {
return
}
println(number)
}
2.6小结
- fun关键字用来声明函数。val关键字分别用来声明只读变量和可变变量
- 字符串模板帮助你避免烦琐的字符串连接。在变量名称前加上 前 缀 或 者 用 前缀或者用 前缀或者用{ }包围一个表达式,来把值注入到字符串中。
- 值对象类在Kotlin中以简洁的方式表示。
- 熟悉的if现在是带返回值的表达式。
- when表达式类似于Java中的switch但功能强大。
- 在检查过变量具有某种类型之后不必显示地转换它的类型:编译器使用智能转换帮你自动的完成。
- for、while和do-while循环与Java类似,但是for循环现在更加方便,特别是当你需要迭代map的时候,又或是迭代集合需要下标的时候。
- 简洁的语法1…5会创建一个区间。区间和数列允许Kotlin在for循环中使用统一的语法和同一套抽象机制,并且还可以使用in运算符和!in运算符来检查值是否属于某个区间。
- Kotlin中的异常处理和Java非常相似,除了Kotlin不要求你声明函数可以抛出的异常。