2.1 函数
2.1.1 函数的声明
关键字(fun)
函数名 参数列表 返回值
- 无返回值的函数
fun main(array: Array<String>){
}
- 有返回值的函数
fun max (a:Int,b:Int) :Int{
return if (a>b) a else b
}
2.1.2 表达式体函数
fun max(a: Int, b: Int) = if (a > b) a else b
- 常规的把函数体写在有花括号的函数叫做:代码块体函数
-
Kotlin
中函数可以直接返回一个表达式,省略掉花括号的return
语句。这就叫表达式体函数 - 表达式体函数可以不声明返回类型,编译期可以分析表达式返回值的类型,将其作为函数的返回值。这叫**类型推导 **
注意
Kotlin
中,如果函数最后一个参数是lamada表达式,这个表达式可以放到括号外面。如果函数参数只有一个lamda
参数,则括号可以省略,具体如下
fun main(array: Array<String>){
//1 常规写法
array.map({})
//2 最后一参数为 lamda表达式时,可以这样写
array.map(){
}
//3 如果函数只有lamda表达式一个参数,括号可以省略
array.map {
}
}
2.1.3 函数的其他写法
var i = {a:Int,b:Int -> a+b}
var j:(Int,Int)->Int = {x,y->x+y}
2.1.4 函数的默认参数和具名参数
- 默认参数:在函数的参数列表中可以为某个参数指定默认值
- 具名参数:在调用函数时,如果默认参数不传值。则其他参数要要指明其参数名称,即具名参数
val Pi = 3.14159f //默认参数
fun getArea(PI: Float = Pi, radius: Float): Float {
return PI * radius * radius
}
fun main(args: Array<String>) {
println(getArea(radius = 2.5f))
}
2.2 变量
2.2.1 变量的声明
关键字(val
,var
) 变量名称 【变量类型】 变量的值
-
如果不指定变量的类型,编译期会将初始化值的类型作为变量的类型
val name = "m1Ku" val length :Int = 1 val mWidth = 23.8
-
如果变量没有初始化值,则必须显式的指明其类型
val age:Int age = 28
可变变量和不可变变量
val(value)
- 不可变引用。使用val声明的变量不能在初始化之后再赋值。相当于Java
的final
变量-
var(variable)
- 可变引用。这种变量的值是可以改变的。相当于普通的Java
变量//val不可变变量,重复赋值。会提示val cannot be reassigned val age = 29 age = 28 //var可变变量,可以重新赋值 var radius = 2.6 radius = 3.5
2.2.2 字符串模板
字符串模板:使用字符串模板可以在字符串中使用局部变量或者表达式的值。
-
引用局部变量
//变量名前加上 $符号 val name = "m1Ku" println("My name is $name!") >>> My name is m1Ku!
-
引用表达式
//引用表达式时,只需要将表达式用 花括号括起来 println("This Array is ${if (array.isNotEmpty()) "not empty" else "empty"}!") >>> This Array is empty! println("The first element of array is ${array[0]}!") >>> The first element of array is test!
2.3 类和属性
2.3.1 属性
属性值由关键字val
和var
声明。val
声明的属性值是只读的,var
声明的属性值是可变的。
class Animal(){
val name:String = ""
val sex:Boolean = false
var weight:Double = 0.0
private val speed:Int = 0
}
- 声明的属性必须要初始化,否则编译会报错
- 默认是被
public
修饰符修饰,如果要禁止其他类访问,可以使用private
修饰符修饰 - 编译期自动生成访问器,其中
var
属性既有getter
也有setter
,val
属性只有getter
访问器
属性的声明
虽然编译器可以默认生成访问器,但是上面属性定义并不是完整的属性声明。
完整的属性声明如下:
关键字 属性名:属性类型 初始化值
【getter】
【setter】
- 如下,代码中声明了
isSquare
这个属性。并且自定义了其getter
访问器
class Rectangle(val height: Int, val width: Int) {
val isSquare: Boolean
get() = height == width
}
2.3.2 Kotlin源码布局,目录和包
Kotlin
中也有Java
中包的概念,每个文件的开头都以package
语句开头,文件中定义的函数类和属性都在包中Kotlin
不区分导入的是类还是函数,它允许使用import
关键字导入任何种类的声明-
Kotlin
中可以把多个类放在同一个文件中,文件的名字可以随意选择
2.4 表示和处理选择:枚举和“when”
2.4.1声明枚举类
Kotlin
中枚举类用 enum class
声明
enum class Color{
RED,ORANGE,YELLOW,GREEN,BLUE,INDIGO,VIOLET
}
Kotlin
和Java
一样,可以给枚举声明属性和方法。如果声明了枚举常量的属性,则在声明每个枚举常量时,必须要提供该常量的属性值。如果给枚举类定义方法,需要用分号将常量列表和方法分隔开
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.4.2 使用“when”处理枚举类
when
是一个有返回值的表达式,因此也可以写成一个直接返回when
表达式的表达式体函数
fun getColorName(color: Color)=
when(color){
Color.RED -> "Richard"
Color.ORANGE -> "Of"
Color.YELLOW -> "York"
Color.BLUE,Color.GREEN -> "这是多个值合并的分支"
else -> "i dont know this color"
}
每个分支上不需要写break
语句。可以把多个值合并到一个分支中,只需要用逗号分开
2.4.3 在“when”结构中使用任意对象
Java
的switch
必须使用常量作为分支条件,而Kotlin
的when
可以使用任意对象作为分支条件
fun mix(c1:Color,c2: Color) : Color{
when(setOf(c1,c2)){
setOf(Color.RED,Color.YELLOW) -> return Color.ORANGE
setOf(Color.YELLOW,Color.BLUE) -> return Color.GREEN
else -> throw Exception("抱歉,这两种颜色不能混合")
}
}
>>println(mix(Color.RED,Color.YELLOW))
ORANGE
>>println(mix(Color.GREEN,Color.BLUE))
Exception in thread "main" java.lang.Exception: 抱歉,这两种颜色不能混合
2.4.4 使用不带参数的”when“
如果不给when
表达式提供参数,分支条件就是任意的布尔表达式。这样写不会创建额外的对象,但代码可读性变差了
fun mixColor(c1: Color, c2: Color) {
when {
(c1 == Color.RED && c2 == Color.YELLOW)
|| (c1 == Color.YELLOW && c2 == Color.RED) -> Color.ORANGE
(c1 == Color.YELLOW && c2 == Color.BLUE)
|| (c2 == Color.YELLOW && c1 == Color.BLUE) -> Color.GREEN
else -> throw Exception("抱歉,这两种颜色不能混合")
}
}
2.4.5 智能转换:合并类型检查和转换
Kotlin
中使用is
判断一个变量是否是某个类型,这类似于在Java
中使用instance of
检查变量类型,Java
中判断过类型变量后,还要显示的进行类型转换。但在Kotlin
中,当使用is
检查过变量类型后,我们不需要再显式的转换类型,因为编译期已经为我们执行了类型转换,这叫做智能转换
interface Expr
//定义num对象 实现Expr接口
class Num(val value: Int) : Expr
//定义Sum对象 实现Expr接口
class Sum(val left: Expr, val right: Expr) : Expr
//定义方法计算传入对象的值
fun eval(e:Expr):Int{
//is检查某一个变量是否时某种类型
if (e is Num){
val n = e as Num //冗余代码kotlin中不需要显示转换
return n.value
}
if (e is Sum){
val m = e as Sum
return eval(e.left) + eval(e.right)
}
throw IllegalArgumentException("未知表达式")
}
as
关键字是显式的类型转换,然而在上述的程序中是多余的,因为我们已经用is
检查过它的类型了
2.4.6 用“when”代替“if”
when
表达式不限于检查值是否相等,如下程序也可以检查实参的数据类型
fun evalWithWhen(e: Expr): Int =
when (e) {
is Num -> e.value
is Sum -> eval(e.left) + eval(e.right)
else ->
throw IllegalArgumentException("未知表达式")
}
2.4.7 代码块也可以作为if和when的分支
if
和when
都可以使用代码块作为分支体,代码块中最后一个表达式就是结果
fun evalWithLog(e:Expr):Int =
when(e){
is Num ->{
println("num = ${e.value}")
e.value
}
is Sum ->{
val m = evalWithLog(e.left)
val n = evalWithLog(e.right)
println("Sum = m + n= ${m + n}")
m + n
}
else ->
throw IllegalArgumentException("未知表达式")
}
2.5 区间和循环
2.5.1 区间
区间:区间是两个值之间的间隔,这两个值通常是数字:一个是起始值,一个是结束值。使用..
运算符来表示。
//闭区间
var nums:IntRange = 1..16
//前闭后开区间
var nums :IntRange = 1 until 16
2.5.2 循环
-
while
和do while
循环与在java
中的使用方式一致 -
kotlin
中没有java
中那种普通的for
循环,循环使用如下for ...in
循环
//使用in关键字
for (num in nums) {
println(num)
}
//step表示步长为2取值
for (num in nums step 2) {
println(num)
}
>> 输出为 1 3 5 7 ....
-
map
的赋值和遍历
//定义map集合
var maps = TreeMap<Char,Int>()
for (c in 'A'..'F'){
//给map元素赋值
maps[c] = c.toInt()
}
//遍历map集合
for ((key,value) in maps) {
println("$key $value")
}
-
in
运算符可以用来检查一个值是否在区间中
fun judge(c:Char) = when(c){
in '0'..'9' ->"this is a digit"
in 'a'..'z' ->"this is a letter"
else -> "this is nothing"
}
2.6 Kotlin中的异常
Kotlin
的异常处理和Java
以及其他语言的处理方式相似。一个函数可以正常结束,也可以在出现错误的情况下抛出异常。方法的调用者能捕获这个异常并处理它;如果没有被处理,异常会沿着栈再次抛出。
-
Kotlin
抛出异常使用throw
关键字,但不必使用new
关键字来创建异常的实例fun checkNum(num: Int) { if (num in 1..100) println("$num 是一个在1到100之间的数字") else throw IllegalArgumentException("输入的数字必须在1到100之间") }
-
Kotlin
的throw
结构是一个表达式,能作为另一个表达式的一部分使用fun checkNum2(num:Int):Int{ var result = if (num in 1..100) num else throw IllegalArgumentException("输入的数字必须在1到100之间") return result }
上面程序中,如果程序执行正常
result
的值将被num
初始化。否则会抛出异常,result
也不会被初始化
2.6.1 "try" "catch" 和 "finally"
Kotlin
中使用带有catch
和finally
的子句的try
结构如下所示
fun readNumber(reader: BufferedReader): Int? {
try {
val line = reader.readLine()
return Integer.parseInt(line)
} catch (e: NumberFormatException) {
return null
} finally {
reader.close()
}
}
而Java
中的写法如下
public int readNumber(BufferedReader reader) throws IOException{
try {
String str = reader.readLine();
return Integer.parseInt(str);
}
catch (NumberFormatException e){
return 0;
}
finally {
reader.close();
}
}
-
Kotlin
代码没有出现throw
子句,而java中必须显式在函数声明上写上throws IOException
,因为IOException
是一个受检异常,java中必须显式的处理,声明函数能抛出所有受检异常 -
Kotlin
不区分受检和未受检异常。不用指定函数抛出的异常,也可以不处理异常
2.6.2 try作为表达式
Kotlin
中的try``就像
if和
when`一样,引入了一个表达式,可以把它的值赋给一个变量。需要用大括号将主体语句括起来。如果主体语句包含多个表达式,那么整个try表达式的值就是最后一个表达式的值
fun readNum(reader: BufferedReader){
var result =
try {
Integer.parseInt(reader.readLine())
}
catch (e:NumberFormatException){
null
}
println(result)
}
readNum(BufferedReader(StringReader("33")))
>> 33
readNum(BufferedReader(StringReader("wewewe")))
>> null