参考文档
Kotlin是一种在Java虚拟机上运行的静态类型编程语言,被称为Android世界中的Swift,由JetBrains公司设计开发并开源。
Kotlin可以编译成Java字节码,也可以编译成JavaScript,方便在没有JVM的设备上运行。
Google I/O 2017年宣布Kotlin成为Android的官方开发语言。
为什么要选择Kotlin呢?
- 简洁 大大减少样板代码的数量
- 安全 避免空指针异常等整个类的错误
- 互操作性 充分利用JVM、Android和浏览器现有的库
- 工具友好 可以使用Java IDE或使用命令行构建
环境配置
- 下载JDK
https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
- 配置JDK
系统属性 > 高级 > 环境变量 > 系统变量
- 新建变量,变量名
JAVA_HOME
,变量值C:\jdk
。 - 新建变量,变量名
CLASSPATH
,变量值.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar
- 选择
Path
变量,新建%JAVA_HOME%\bin
和%JAVA_HOME%\jre\bin
打开cmd
命令行,输入命令验证。
$ java -version
java version "1.8.0_221"
Java(TM) SE Runtime Environment (build 1.8.0_221-b11)
Java HotSpot(TM) Client VM (build 25.221-b11, mixed mode, sharing)
$ javac -version
javac 1.8.0_73
- 下载安装IntelliJ IDEA开发工具
https://www.jetbrains.com/idea/download/index.html
输入激活码:试用版教育激活码 仅供试用
YZVR7WDLV8-eyJsaWNlbnNlSWQiOiJZWlZSN1dETFY4IiwibGljZW5zZWVOYW1lIjoiamV0YnJhaW5zIGpzIiwiYXNzaWduZWVOYW1lIjoiIiwiYXNzaWduZWVFbWFpbCI6IiIsImxpY2Vuc2VSZXN0cmljdGlvbiI6IkZvciBlZHVjYXRpb25hbCB1c2Ugb25seSIsImNoZWNrQ29uY3VycmVudFVzZSI6ZmFsc2UsInByb2R1Y3RzIjpbeyJjb2RlIjoiSUkiLCJwYWlkVXBUbyI6IjIwMTktMTEtMjYifSx7ImNvZGUiOiJBQyIsInBhaWRVcFRvIjoiMjAxOS0xMS0yNiJ9LHsiY29kZSI6IkRQTiIsInBhaWRVcFRvIjoiMjAxOS0xMS0yNiJ9LHsiY29kZSI6IlBTIiwicGFpZFVwVG8iOiIyMDE5LTExLTI2In0seyJjb2RlIjoiR08iLCJwYWlkVXBUbyI6IjIwMTktMTEtMjYifSx7ImNvZGUiOiJETSIsInBhaWRVcFRvIjoiMjAxOS0xMS0yNiJ9LHsiY29kZSI6IkNMIiwicGFpZFVwVG8iOiIyMDE5LTExLTI2In0seyJjb2RlIjoiUlMwIiwicGFpZFVwVG8iOiIyMDE5LTExLTI2In0seyJjb2RlIjoiUkMiLCJwYWlkVXBUbyI6IjIwMTktMTEtMjYifSx7ImNvZGUiOiJSRCIsInBhaWRVcFRvIjoiMjAxOS0xMS0yNiJ9LHsiY29kZSI6IlBDIiwicGFpZFVwVG8iOiIyMDE5LTExLTI2In0seyJjb2RlIjoiUk0iLCJwYWlkVXBUbyI6IjIwMTktMTEtMjYifSx7ImNvZGUiOiJXUyIsInBhaWRVcFRvIjoiMjAxOS0xMS0yNiJ9LHsiY29kZSI6IkRCIiwicGFpZFVwVG8iOiIyMDE5LTExLTI2In0seyJjb2RlIjoiREMiLCJwYWlkVXBUbyI6IjIwMTktMTEtMjYifSx7ImNvZGUiOiJSU1UiLCJwYWlkVXBUbyI6IjIwMTktMTEtMjYifV0sImhhc2giOiIxMTA1NzI3NC8wIiwiZ3JhY2VQZXJpb2REYXlzIjowLCJhdXRvUHJvbG9uZ2F0ZWQiOmZhbHNlLCJpc0F1dG9Qcm9sb25nYXRlZCI6ZmFsc2V9-rsJR5mlJcjibqRu1gQAMUCngMe8i+AOWIi+JZkNFYPET2G1ONcLPcIzoATTRi6ofkDm5l+3Y4HXjBPjVU6bHDdMBAzCnUqpXKsCknwSYyPSU0Y5pzuLvw6O9aPlQ46UBoTEC2BL5W6f11S7NlAq7tTbDuvFUynqSGAmTEfuZtKmzRmp20ejTPuMlSO7UqSkZvkg6YvSTrax1d2K+P9SAmVGZ9iC7AzBs4AwTf84QB9qHvE/Nh0oELSHWGG9hsZZ7sVghI/39/jPQFTp8GLFsl36ZPybPhGDam721zxS9H++/eJk23Jz3nxaRluE4dWmpHrDg1qBHp8qVpSFejg2QYw==-MIIElTCCAn2gAwIBAgIBCTANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBMB4XDTE4MTEwMTEyMjk0NloXDTIwMTEwMjEyMjk0NlowaDELMAkGA1UEBhMCQ1oxDjAMBgNVBAgMBU51c2xlMQ8wDQYDVQQHDAZQcmFndWUxGTAXBgNVBAoMEEpldEJyYWlucyBzLnIuby4xHTAbBgNVBAMMFHByb2QzeS1mcm9tLTIwMTgxMTAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxcQkq+zdxlR2mmRYBPzGbUNdMN6OaXiXzxIWtMEkrJMO/5oUfQJbLLuMSMK0QHFmaI37WShyxZcfRCidwXjot4zmNBKnlyHodDij/78TmVqFl8nOeD5+07B8VEaIu7c3E1N+e1doC6wht4I4+IEmtsPAdoaj5WCQVQbrI8KeT8M9VcBIWX7fD0fhexfg3ZRt0xqwMcXGNp3DdJHiO0rCdU+Itv7EmtnSVq9jBG1usMSFvMowR25mju2JcPFp1+I4ZI+FqgR8gyG8oiNDyNEoAbsR3lOpI7grUYSvkB/xVy/VoklPCK2h0f0GJxFjnye8NT1PAywoyl7RmiAVRE/EKwIDAQABo4GZMIGWMAkGA1UdEwQCMAAwHQYDVR0OBBYEFGEpG9oZGcfLMGNBkY7SgHiMGgTcMEgGA1UdIwRBMD+AFKOetkhnQhI2Qb1t4Lm0oFKLl/GzoRykGjAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBggkA0myxg7KDeeEwEwYDVR0lBAwwCgYIKwYBBQUHAwEwCwYDVR0PBAQDAgWgMA0GCSqGSIb3DQEBCwUAA4ICAQAF8uc+YJOHHwOFcPzmbjcxNDuGoOUIP+2h1R75Lecswb7ru2LWWSUMtXVKQzChLNPn/72W0k+oI056tgiwuG7M49LXp4zQVlQnFmWU1wwGvVhq5R63Rpjx1zjGUhcXgayu7+9zMUW596Lbomsg8qVve6euqsrFicYkIIuUu4zYPndJwfe0YkS5nY72SHnNdbPhEnN8wcB2Kz+OIG0lih3yz5EqFhld03bGp222ZQCIghCTVL6QBNadGsiN/lWLl4JdR3lJkZzlpFdiHijoVRdWeSWqM4y0t23c92HXKrgppoSV18XMxrWVdoSM3nuMHwxGhFyde05OdDtLpCv+jlWf5REAHHA201pAU6bJSZINyHDUTB+Beo28rRXSwSh3OUIvYwKNVeoBY+KwOJ7WnuTCUq1meE6GkKc4D/cXmgpOyW/1SmBz3XjVIi/zprZ0zf3qH5mkphtg6ksjKgKjmx1cXfZAAX6wcDBNaCL+Ortep1Dh8xDUbqbBVNBL4jbiL3i3xsfNiyJgaZ5sX7i8tmStEpLbPwvHcByuf59qJhV/bZOl8KqJBETCDJcY6O2aqhTUy+9x93ThKs1GKrRPePrWPluud7ttlgtRveit/pcBrnQcXOl1rHq7ByB8CFAxNotRUYL9IF5n3wJOgkPojMy6jetQA5Ogc8Sm7RG6vg1yow==
入门程序
$ vim main.kt
fun main(args:Array<String>){
println("hello world")
}
- Kotlin文件是以
.kt
为后缀 -
fun
表示函数声明,为固定写法。 -
main
表示主方法,是程序的入口。 -
args:Array<String>
表示接受的参数名称为args,数据类型为字符串。 -
println
表示向控制台打印字符串,分号可以省略。
交互式编程
Kotlin命令行编译工具 https://github.com/JetBrains/kotlin
$ git clone https://github.com/JetBrains/kotlin.git
Kotlin和Java都是基于JVM的,使用Kotlin命令行编译工具前首先需要配置环境变量。
下载Kotlin命令行编译工具,解压到指定目录后,将bin
目录添加到系统环境变量Path
中。bin
目录下包含编译和运行Kotlin所需的脚本。
$ kotlinc -version
info: Kotlin Compiler version 1.1.2-2
使用命令行工具Kotlin编译器编译Kotlin源文件为.jar
包文件
$ kotlinc main.kt -include-runtime -d main.jar
-
-include-runtime
表示让jar
文件包含Kotlin运行库,从而可以直接运行。若需要生成的jar
包是准备供其他Kotlin程序使用的,可以无需包含Kotlin的运行库,即可以省略此选项。需要注意的是,这样生成的jar
文件不含Kotlin的运行库,所以需要确保使用时,运行时在classpath
上的。 -
-d
表示设置编译输出的名称,可以是.class
或.jar
文件,也可以是目录。
使用Java命令运行.jar
文件
$ java -jar main.jar
hello world
使用kotline
命令运行Kotlin生成的.jar
文件。
$ vim user.kt
class User(val name:String){
fun output(){
println("hello $name")
}
}
fun main(args:Array<String>){
User("junchow").output()
}
$ kotlinc user.kt -d user.jar
$ kotlin -classpath user.jar User
error: 'main' method not found in class User
main.jar main.kt
变量
存储数据
计算机程序是跟数据打交道的,编写绝大多数程序都是在处理或记忆数据,Kotlin是如何声明和记忆数据的呢?
变量观点是培养程序设计能力的基础,在程序中的数据必须通过变量来处理,处理不同的数据必须使用不同的变量类型。
变量可以分为可变变量和不可变变量(常量)
可变变量
使用var
关键字声明
var <变量标识符>:<数据类型> = <初始化值>
类型推导
不同的数据类型使用不同的的容器保存,Kotlin会通过类型推导自动推断出数据类型,当然最好是直接显式地指定数据类型。
例如:
fun main(args:Array<String>){
//声明变量定义
var id:Int = 100;
println(id);
id = 200;
println(id);
//系统自动推断变量类型
var name = "junchow";
println(name);
name = "jc";//变量值可以修改但变量类型不可以修改
println(name);
//声明时若未初始化则必须提供变量类型
var state:Byte;
state = -128;//明确赋值
println(state);
state = 127;
println(state);
}
不可变变量(常量)
使用val
关键字声明不可变变量,不可变变量表示只能赋值一次的变量,类似Java中使用final
修改的变量。可作为局部变量使用。
val <变量标识符>:<数据类型> = <初始化值>
例如:
fun main(args:Array<String>) {
//声明不可变变量
val pi:Float = 3.1415926f;
println(pi);//3.1415925
//声明不可变变量
val e:Double;
e = 2.17828184;
println(e);//2.17828184
}
变量和常量都可以没有初始化值,但在引用前必须初始化。编译器支持自动类型判断,即声明时可以不指定数据类型,由编译器自动判断。
数据类型
Kotlin中一切皆对象,可以在任何变量上调用成员函数和属性。对于基本类型如数字、字符、布尔值可以在运行时表示为原生类型值。
Kotlin的基本数据类型包括
数字
Kotlin处理数字在某种成都上接近Java,但并不完全相同。
Kotlin中数字没有隐式类型转换,而Java却可以。比如Java支持将int
类型隐式转换为long
。
整数
-
Byte
位宽度为8,表示整数,数据存储范围-128~127。
fun main(args:Array<String>){
val byteMaxValue:Byte = Byte.MAX_VALUE
val byteMinValue:Byte = Byte.MIN_VALUE
println("byte max value is $byteMaxValue")//byte max value is 127
println("byte min value is $byteMinValue")//byte min value is -128
}
-
Short
位宽度为16,表示整数,短整型,数据存储范围-32768~32767。 -
Int
位宽度为32,表示整数,标准整型,数据存储范围-2147483648~2147483647。 -
Long
位宽度为64,表示整数,长整型,数据存储范围-92233720368547775807~9223372036854775808,使用时需使用L
进行标记。
fun main(args:Array<String>){
val longMaxValue:Long = Long.MAX_VALUE
val longMinValue:Long = Long.MIN_VALUE
println("Long max value is $longMaxValue")//Long max value is 9223372036854775807
println("Long min value is $longMinValue")//Long min value is -9223372036854775808
}
浮点数
-
Float
位宽度为32,表示小数,单精度浮点型,精确位数6位,使用时需使用f
或F
表记。 -
Double
位宽度为64,表示小数,双精度浮点型,精确位数15~16位,浮点数的默认类型。
数值的字面常量
Kotlin不支持八进制,仅支持二进制、十进制、十六进制。默认为十进制。
- 采用二进制方式赋值需要添加
0b
,其中b
表示binary
二进制意思。
fun main(args:Array<String>){
//采用二进制方式赋值需要添加0b,其中b表示binary二进制意思。
val binInt:Int = 0b0011
println("binInt value is $binInt")//binInt value is 3
}
数字字面值中的下划线
Kotlin中可以添加下划线_
,使数字常量更加易于阅读。
fun main(args:Array<String>) {
val oneMillion = 1_000_000;
println(oneMillion);//1000000
}
数字比较
Kotlin中没有基础类型,只有封装数字类型,每定义一个变量其实Kotlin会帮你封装了一个对象,这样就可以保证不会出现空指针。
当比较两个数字时,会比较数据大小和两个对象是或否相同。
Kotlin中使用两个==
比较两个值的大小,使用三个===
比较对象的地址。
fun main(args:Array<String>) {
val pi:Double = 3.1415926;
//使用对象比较符===,比较对象的地址是否相同,返回布尔值。
println(pi === pi);//true
}
表示方式
在Java中数字是物理存储在JVM中的原生类型,当需要一个可空的引用(如Int?
)或泛型时,系统会将数字装箱,需要注意的是数字装箱不一定会保留相同性。
fun main(args:Array<String>) {
val i:Byte = 1;
//println(i == 1);//Error:(3, 13) Kotlin: Operator '==' cannot be applied to 'Byte' and 'Int'
println(i == i);//true
println(i === i);//true
val boxedVal:Byte? = i;
val boxedValue:Byte? = i;
println(boxedVal == boxedValue);//true
println(boxedVal === boxedValue);//true
}
类型转换/显式转换
由于不同的表示方式,较小类型并非较大类型的子类。
fun main(args:Array<String>) {
//声明一个装箱的Int,即java.lang.Integer
val i:Int? = 1;
//无法编译
//隐式转换产生了一个装箱的Long,即java.lang.Long
val l:Long? = i;//Error:(6, 19) Kotlin: Type mismatch: inferred type is Int? but Long? was expected
}
Kotlin中并不能隐式地将较小的类型转换为较大的类型。这也就意味着如果不进行显示转换的话,是不能够将Byte
类型赋值给Int
变量的。
fun main(args:Array<String>) {
val b:Byte = 1;//字面值会自动进行静态类型检测
//val i:Int = b;//Error:(3, 17) Kotlin: Type mismatch: inferred type is Byte but Int was expected
//可是使用toInt方法对其进行显式地类型转换
val i:Int = b.toInt();
println(b);//1
println(i);//1
}
显示类型转换方法
toByte():Byte
toShort():Short
toInt():Int
toLong():Long
toFloat():Float
toDouble():Double
toChar():Char
toString():String
缺乏隐式类型转换很少会引起开发者的注意,因为类型会从上下文中推断出来,但是算术运算会重载而做适当转换。
在某些情况下也会进行自动类型转换,不过前提条件是可以根据上下文环境推断出正确的数据类型,而且数学操作符会做相应地重载。
fun main(args:Array<String>) {
val l = 1L + 3;
println(l);//4
}
字符
与Java不同的是Kotlin中的字符Char
是不能够直接和数字进行操作的,字符类型的字面量必须使用单引号''
包裹起来。简单来说,Kotlin中的字符不能直接当作数字。
需要注意的是,不同Java的是字符不属于数值类型,它是一种独立的数据类型,也就是说在Kotlin中字符不是数字。
fun main(args:Array<String>) {
var c:Char = 'a';
println(c == 96);//Error:(3, 13) Kotlin: Operator '==' cannot be applied to 'Char' and 'Int'
}
特殊字符可以使用反斜杠\
进行转义,Kotlin中支持的转义字符包括:
\t
\b
\n
\r
\'
\"
\\
\$
当编码其它字符时需要使用Unicode转移序列
例如:将字符数字显式地转换为Int
数字
fun decimalDigitValue(c:Char):Int{
if(c !in '0'..'9'){
throw IllegalArgumentException("Out of range");
}
return c.toInt() - '0'.toInt();
}
fun main(args:Array<String>) {
val c:Char = '1';
println(decimalDigitValue(c));//1
}
字符串
- Kotlin中使用
String
表示字符串 - 字符串必须使用双引号引起来
- 字符串无存储长度限制
- 字符串是不可变的
- 字符串的元素是字符,可使用索引运算符访问。
val name:String = "jc";
println(name[0]);//j
println(name.length);//2
//println(name[name.length]);//Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range
println(name[name.length - 1]);//c
- 字符串的元素字符可使用
for
循环迭代
字符串拼接
- 字符串可使用
+
操作符进行连接,+
连接符也适用于连接字符串和其他类型的值,但是需要表达式中的第一个元素是字符串。 - 多数情况下请优先使用字符串模板而非字符串连接符。
fun main(args:Array<String>) {
val name:String = "jc";
println(name[0]);//j
println(name.length);//2
//println(name[name.length]);//Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range
println(name[name.length - 1]);//c
for(c in name){
println(c);//j c
}
val str:String? = "xxx";
println(str);
//println(str[0]);//Error:(6, 13) Kotlin: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?
var age:String = "18";
age = age + 1;
println(age);//181
var nick:String = "jc";
println("nick = $nick");
println("$.nick.length is ${nick.length}");
var msg:String = "hello world\nwelcome";
println(msg);
}
原始字符串
原始字符串使用三个引号
"""
作为分界符进行包裹,其内部没有转义并且可以包含换行以及任何其他字符。去除原始字符串的前导空格
trimMargin()
,trimMargin
默认使用|
作为边界前缀,可选择其他字符作为参数传入。
字符串模板
- 字符串字面值可以包含模板表达式,也就是一些小段代码,会求值并将结果合并到字符串中。
- 模板表达式以美元符号
$
开头,由简单的名字构成。 - 模板表达式可使用花括号
{}
包裹任意表达式 - 原始字符串与转义字符内部都支持模板
fun main(args:Array<String>){
val i:Int = 100;
println("i = $i");//i = 100
val str:String = "junchow"
println("str = $str $str.length = ${str.length}")//str = junchow junchow.length = 7
val price:Double = 9.9
val msg = """
i = $i
str = $str
strlen = ${str.length}
price = ${'$'}$price
msg = ${str.replace("jun","jay")}
""".trimIndent()
println(msg)
}
字符串比较
- 在Java中使用
==
比较两个字符串其实是比较字符串的内存地址 - 在Kotlin中使用
==
比较两个字符串实际上比较的是字符串的内容,相当于Java中的equals
方法。
双等号==
- 如果作用于基本数据类型的变量则直接比较其存储的值是否相等。
- 如果作用域引用类型的变量,则比较的是所指向的对象的内存地址。
三等号===
- 如果作用于基本数据类型,只要数据类型不同结果就不等。如果是同类型比较则与
==
保持一致,即直接比较基本数据类型所存储的值是否相等。 - 如果作用于引用类型,则于
==
保持一致,比较的是变量对指向的内存地址。
fun main(args:Array<String>){
val i:Int = 10
val l:Long = 10
//==和===对于类型不同直接报错
//println("i == l : ${i==l}")//Error:(6, 25) Kotlin: Operator '==' cannot be applied to 'Int' and 'Long'
//println("i === l : ${i===l}")//Error:(8, 26) Kotlin: Operator '===' cannot be applied to 'Int' and 'Long'
println("i == l.toInt() : ${i == l.toInt()}")//i == l.toInt() : true
println("i.toLong() == l : ${i.toLong() == l}")//i.toLong() == l : true
val str1 = "hello"
val str2 = "hello"
val str3 = "hell"
//==作用于基本数据类型时比较的是存储值
println("str1 == str2 : ${str1 == str2}")//str1 == str2 : true
println("str1 == str3 : ${str1 == str3}")//str1 == str3 : false
//===作用于基本数据类型是,同类型比较效果与==相同,对于字符串比较的都是值。
println("str1 === str2 : ${str1 === str2}")//str1 === str2 : true
println("str1 === str3 : ${str1 === str3}")//str1 === str3 : false
//String类对equals方法进行了重写,比较的是字符串的值。
println("str1.equals(str2) = ${str1.equals(str2)}")//str1.equals(str2) = true
println("str1.equals(str3) = ${str1.equals(str3)}")//str1.equals(str3) = false
}
equals
方法
-
equals
方法不能作用于基本数据类型的变量,只能作用于引用类型的变量。 - 如果没有对
equals
方法进行重写,则比较的是引用类型变量所指向的对象地址。 - 比如在Kotlin中
String
、Date
类对equals
方法重写后比较的是对象所指向的内容。
data class User(var name:String, var age:Int){
override fun equals(other:Any?):Boolean{
if(other is User){
return this.age == other.age && this.name == other.name
}
return false
}
}
fun main(args:Array<String>){
val u1:User = User("alice", 18)
val u2:User = User("alice", 18)
val u3:User = User("lice", 18)
val u4:User = u1
println("u1 == u2 : ${u1 == u2}")//u1 == u2 : true
println("u1 == u4 : ${u1 == u4}")//u1 == u4 : true
println("u1.equals(u2) : ${u1.equals(u2)}")//u1.equals(u2) : true
println("u1.equals(u3) : ${u1.equals(u3)}")//u1.equals(u3) : false
println("u1.equals(u4) : ${u1.equals(u4)}")//u1.equals(u4) : true
}
字符串转换
如果要将Kotlin中的字符串转换为数字,前提条件是必须保证字符串是数字类型,不能出现数字和字符混合的字符串。转换时可使用toInt()
方法进行字符串数字的转换。
如果要将数字转换成字符串,可以使用数字的toString()
方法。
fun main(args:Array<String>) {
var str = "10"
var num = 100
//将数字转换为字符串
str = num.toString()
println(str)//100
//将字符串转换为数字
num = str.toInt()
println(num)//100
}
布尔
布尔类型使用Boolean
表示,它只具有两个值true
和false
。
fun main(args:Array<String>){
println(Math.pow(2.0, 100.0) > Math.pow(3.0, 75.0))//false
}
import kotlin.math.sqrt
fun main(args:Array<String>){
// 根号运算比较
print(sqrt(5.0)- sqrt(4.0) < sqrt(4.0) - sqrt(3.0))//true
}
若需要可空引用布尔则会被装箱
Kotlin中内置的布尔运算符
-
||
短路逻辑或 -
&&
短路逻辑与 -
!
逻辑非
fun main(args:Array<String>){
var flag:Boolean = true
var status:Boolean = false
println("flag && status = ${flag && status}")//flag && status = false
println("flag || status = ${flag || status}")//flag || status = true
println("!flag = ${!flag}")//!flag = false
}
空值处理
Kotlin对比Java的一个最大的区别在于致力于消除【空引用】带来的危险。在Java中如果尝试访问一个【空引用】的成员可能会导致【空指针异常】NullPointerException(NPE)
的出现。
在Kotlin中类型系统将可空类型和非空类型进行了区分。例如String
字符串为不可空类型,String?
则为可空类型,如果将不可空类型赋值为null
则会导致编译无法通过。简单来说,可空类型主要是为了从编译层面上尽可能的减少NPE
。
可空操作符
Kotlin编译器默认变量不能为null
,可使用可空操作符?
表示变量为null
。
fun main(args:Array<String>){
// var str1:String = null //Error:(2, 23) Kotlin: Null can not be a value of a non-null type String
var str2:String? = null
println(str2)// null
}
关闭空检查
fun main(args:Array<String>){
var str:String? = null
str.toInt()//Error:(3, 8) Kotlin: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?
}
可使用!!
关闭Kotlin的null
空检查
fun main(args:Array<String>){
val str1:String? = null
//println(str1!!.toInt())//Exception in thread "main" kotlin.KotlinNullPointerException at TestKt.main(test.kt:3)
val str2:String? = ""
//println(str2!!.toInt())//Exception in thread "main" java.lang.NumberFormatException: For input string: ""
val str3:String? = "0"
println(str3!!.toInt())//0
}
空安全调用符
使用空安全调用符?
表示空变量可以调用方法
fun main(args:Array<String>){
val str1:String? = null
println(str1?.toInt())//null
val str2:String? = ""
//println(str2?.toInt())//Exception in thread "main" java.lang.NumberFormatException: For input string: ""
val str3:String? = "0"
println(str3?.toInt())//0
}
Elvis操作符
fun main(args:Array<String>){
val str1:String? = null
println(str1?.toInt()?:-1)//-1
val str2:String? = ""
//println(str2?.toInt()?:-1)//Exception in thread "main" java.lang.NumberFormatException: For input string: ""
val str3:String? = "0"
println(str3?.toInt()?:-1)//0
}
Kotlin中有两种类型分别是非空引用类型、可空引用类型。对于可空引用如果希望调用它的成员变量或成员函数,直接调用会出现编译错误,有三种方式可以解决:
- 调用前需要先检查因为可能为
null
- 使用
varname?.length
形式调用,如果varname
为null
则直接返回varname.length
。 - 使用
varname!!.length
形式调用,如果varname
为null
则抛出空指针异常,否则返回varname.length
。
对于可空引用,调用其成员变量或函数必须先做检查否则编译器会报错。
fun main(args:Array<String>){
val str:String? = ""
if(str != null && str.isNotEmpty()){
println("String of length is ${str.length}")
}else{
println("Empty string")//Empty string
}
}
fun main(args:Array<String>){
val str:String = ""
//Condition 'str != null' always 'true'
if(str != null && str.isNotEmpty()){
println("String of length is ${str.length}")
}else{
println("Empty string")//Empty string
}
}
使用非空引用必须保证它非空,使用可空类型每次必须做检查。为了简单使用,可空引用使用了一种安全的调用方式,即使用?.
问号+点的方式进行调用。
fun main(args:Array<String>){
// 如果`str`为空引用则直接返回`null`否则返回`str.length`
val str1:String? = null
println(str1?.length)//null
var str2:String? = ""
println(str2?.length)//0
}
可空引用的成员调用也才采用空安全调用符!!
fun main(args:Array<String>){
val str1:String? = ""
val result1 = str1!!.length
println(result1)// 0
val str2:String? = null
val result2 = str2!!.length// Exception in thread "main" kotlin.KotlinNullPointerException at TestKt.main(test.kt:8)
println(result2)
}
条件控制
Kotlin中的控制流主要分为if
、when
、for
、while
表达式。
If 表达式
- Kotlin中一切都是表达式,也就是说一切都有返回值。
- Kotlin中
if
是一个表达式,所以会返回一个值,因此无需使用三元运算符条件?满足:不满足
。
fun max(x:Int, y:Int):Int = if (x>y) x else y
fun main(args:Array<String>){
println(max(1, 3))//3
}
If语句
一个
if
语句包含一个布尔表达式和一条或多条语句,如果if
条件没有异常,可以以if
语句使用。Kotlin中
if
的分支可以是代码块,以最后的表达式作为该代码块的值。如果if
中的 一个分支返回了Unit
那整个表达式也将返回为Unit
。
实例:使用in
运算符来检测某个数字是否在指定区间中,区间格式为begin..end
。
fun main(args:Array<String>){
val x = 2
println(if(x in 0..9 ) true else false)//true
}
When 表达式
when
即可被当作表达式和也可以被当作语句使用,当做表达式时符合条件的分支的值就是整个表达式的值,当作语句使用时则忽略别的分支的值。when
将其参数和分支条件顺序比较,直到某个分支满足条件。
-
when
类似其他语言中的switch
操作符,when
中else
类似switch
语句中default
,如果其他分支都不满足条件将会求值else
分支。
fun main(args:Array<String>) {
val x = 1
when(x){
1->println("x == 1")//x == 1
2->println("x == 2")
else -> {
println("x is not 1 and 2")
}
}
}
- 如果很多分支需要使用相同的方式进行处理,则可以将多个分支条件放在一起,使用逗号分隔。
fun main(args:Array<String>) {
val x = 1
when(x){
0,1->println("x == 0 or x == 1")//x == 0 or x == 1
2->println("x == 2")
else -> {
println("x is not 1 and 2")
}
}
}
-
when
可以检测一个值在in
或不在!in
一个区间或者集合中
fun main(args:Array<String>) {
val validNumbers = arrayOf(10, 20, 30)
val x = 10
when(x){
in 0..9 -> println("x is in the range 0 to 9")
!in 0..99 -> println("x is outside the range 0 to 99")
in validNumbers -> println("x is valid")//x is valid
else -> {
println("none of the above")
}
}
}
fun main(args:Array<String>) {
val item = "boss"
val items = setOf("boss", "manager", "worker")
when{
item in items -> println("success")
else -> println("error")
}
}
-
when
可以检测一个值是is
或不是!is
一个特定类型的值,需要注意的是,由于智能转换可以访问该类型的方法和属性而无需任何额外的检测。
fun hasPrefix(x:Any, prefix:String) = when(x){
is String->x.startsWith(prefix)
else -> false
}
fun main(args:Array<String>) {
println(hasPrefix("web_admin", "web_"))//true
}
-
when
可以用来取代if-else if
链,如果不提供参数则所有分支条件都是简单的布尔表达式,当一个分支的条件为真时则执行该分支。
fun isOdd(x:Int):Boolean = if(x%2 == 1) true else false
fun isEven(x:Int):Boolean = if(x%2 == 0) true else false
fun main(args:Array<String>) {
val x = 3
when{
isOdd(x)->print("x is odd")
isEven(x)->print("x is event")
else->println("x is funny")
}
}
循环控制
Loop和Range
Kotlin循环中所使用的区间定义和Java中有很大的不同
fun main(args:Array<String>) {
val items = 1..100// [1,100]
var result = 0
for(item in items){
result += item
}
println(result)//5050
}
区间定义
开区间使用(begin, end)
表示,开区间不包含两端的点。闭区间使用[begin, end]
表示,闭区间包含两端的点。
- 使用
1..100
表示[1,100]
的闭区间,包含1和100。 - 使用
1 until 100
表示前开后闭区间[1, 100)
,不包含100。
fun main(args:Array<String>) {
val items = 1 until 100
var result = 0
for(item in items){
result += item
}
println(result)//4950
}
跨步遍历
Kotlin中使用step
指定每次遍历是以多少个为单位
fun main(args:Array<String>) {
val items = 1 until 100
var result = 0
for(item in items step 2){
result += item
}
println(result)//2500
}
反转集合
fun main(args:Array<String>) {
val items = 1 until 100
for(item in items.reversed() step 2){
println(item)
}
}
集合个数
fun main(args:Array<String>) {
val items = 1 until 100
println(items.count())//99
}
For 循环
Kotlin中for
循环可以对任何提供迭代器iterator
的对象进行遍历,相当于C#中的foreach
循环。
Kotlin中的for
循环可以便利任何提供了迭代器的对象
- 有一个成员函数或扩展函数
iterator()
的返回类型 - 有一个成员函数或扩展函数
next()
且 - 有一个成员函数或扩展函数
hasNext()
并返回布尔值Boolean
。
iterator
、next
、hasNext
三个函数需要标记为operator
。
for(item in collection) print(item)
for
循环体可以是一个代码块
for(item:Int in items){
}
正序遍历
fun main(args:Array<String>) {
for(item in 0 until 9 step 2){
println(item)//0 2 4 6 8
}
}
倒序遍历
Kotlin的for
循环如果需要在数字区间上迭代,可使用区间迭代器。
fun main(args:Array<String>) {
for(item in 9 downTo 0 step 2){
println(item)//9 7 5 3 1
}
}
通过索引遍历数组和列表
fun main(args:Array<String>) {
val array = arrayOf(2,4,6,8, 10)
for(index in array.indices){
println("index = $index value = ${array[index]}")
}
}
在区间上遍历会被编译成优化的实现而非创建额外对象,看使用库函数withIndex
。
fun main(args:Array<String>) {
val array = arrayOf(2,4,6,8, 10)
for((index, value) in array.withIndex()){
println("index = $index value = $value")
}
}
fun main(args:Array<String>) {
val list = listOf<String>("alice", "ben", "carl")
for(item in list){
println(item)
}
//Error:(6, 27) Kotlin: Destructuring declaration initializer of type String must have a 'component1()' function
for((index, value) in list.withIndex()){
println("index = $index value = $value")
}
}
While 循环
-
while
循环是最基本的循环,表达式结构
while(布尔表达式){
//循环内容
}
-
do..while
循环对于while
语句而言,如果不满足条件则不能进入循环。当需求即使不能满足,也至少执行一次。
do {
//代码语句
}while(布尔表达式)
例如:
fun main(args:Array<String>) {
var x = 5;
while(x > 0){
println(x--)
}
println("------------------------------")
var y = 5
do{
println(y--)
}while(y>0)
}
Repeat 循环
fun main(args:Array<String>) {
repeat(5){
it->println("$it")
}
}
Kotlin相对于Java新加入的特性,取代for(int i=0; i<5; i++)
这种简单的重复工作。
fun main(args:Array<String>) {
repeat(5){
println("$it")
}
}
函数
- 计算机程序中的函数是程序执行的小片段,这些小片段可以有机的组合在一起,完成复杂的业务功能。
fun 函数名(参数名: 参数类型): 返回值类型{
函数体
}
主函数main
函数是Kotlin程序的入口函数,是Kotlin程序运行认寻找的第一个函数。
fun main(args:Array<String>):Unit {
// 函数体
}
-
fun
声明函数的关键字 -
main
函数名 -
args
参数名 -
Array<String>
参数类型,表示字符串数组。 -
Unit
返回值类型,表示没有返回值,可省略。
函数声明
- Kotlin中函数使用关键字
fun
声明,函数的参数格式为参数:数据类型
。
fun echo(message:String):Unit{
println(message)
}
fun double(x: Int): Int{
return x * 2;
}
fun sum(x:Int = 10, y:Int = 10):Int{
return x + y;
}
fun main(args:Array<String>){
echo(double(10).toString())//20
echo(sum().toString())//20
}
函数参数
函数的参数使用Pascal表示法定义,即name:type
,其中参数使用逗号分隔,
,每个参数必须有显示类型。
import kotlin.math.pow
fun powOf(number:Int, exponent:Int): Double {
return number.toDouble().pow(exponent.toDouble())
}
fun main(args:Array<String>){
println(powOf(2, 3))//8.0
}
Kotlin中函数的参数可以分为两种分别是默认参数和具名参数。
默认参数
Kotlin提供了在函数定义中制定默认参数的工具,如果调用函数但不传递任何参数,则会使用默认参数作为函数定义的参数。当使用参数调用函数时,传递参数将用作函数定义中的参数。
fun run(id:Int = 0, name:String = ""){
println("parameter in function definition $id and $name")
}
fun main(args:Array<String>) {
//函数调用时不传递参数
run()//parameter in function definition 0 and
//函数调用时传递部分参数
run(1)//parameter in function definition 1 and
//函数调用时传递所有参数
run(2, "jc")//parameter in function definition 2 and jc
}
函数参数可以拥有默认值,当省略相应的参数时会使用默认值,这样做的好处可以减少重载次数。
Kotlin中函数默认值通过类型后面的
=
以及给出的值来定义覆盖方法会使用与基类型方法相同的默认参数值,当覆盖一个带有默认参数值的方法时,必须从签名中深谷额默认参数值。
class User(val username:String="guest", val nickname:String) {
fun out(){
println("username = $username nickname = $nickname")
}
fun output(username:String="nobody", nickname:String="nothing"){
println("username = $username nickname = $nickname")
}
}
fun main(args:Array<String>){
User(nickname="jc").out()//username = guest nickname = jc
User(nickname="jc").output()//username = nobody nickname = nothing
User(nickname="jc").output("junchow", "jun")//username = junchow nickname = jun
}
具名参数
Kotlin函数中的具名参数又称为命名参数,具名参数是一个参数,在函数调用中定义参数的名称,定义函数调用的参数名称,并检查匹配函数定义中的名称然后分配给它。
fun run(id:Int = 0, name:String = ""){
println("parameter in function definition $id and $name")
}
fun main(args:Array<String>) {
run(name="junchow")//parameter in function definition 0 and junchow
}
具名参数可以在调用函数时使用命名的函数参数,当一个函数中存在大量参数或默认值时使用具名参数将会非常方便。另外,使用具名参数将使代码更加具有可读性。
位置参数
当一个函数调用混用位置参数和具名参数时,所有位置参数都要放在第一个命名参数之前。
可变参数
可以使用*
星号操作符将可变数量参数vararg
以命名形式传入函数。
函数返回值
- 单表达式函数是指可以使用表达式作为函数体,返回类型将会进行自动推断。
fun sum(x:Int, y:Int) = x + y
fun main(args:Array<String>){
println(sum(100, 200))//300
}
- 若函数无返回值,函数返回值类型可使用
Unit
类型,也可以省略,因此是可选的。
Kotlin中的Unit
相当于Java中的void
,Unit
是一种只有一个值Unit
的类型,这个值表示函数无需显式返回。
- 函数的变长参数使用
vararg
关键字进行标识
fun vars(vararg arg:Int){
for(item in arg){
println(item)
}
}
fun main(args:Array<String>){
vars(1,2,3,4,5)
}
函数表达式
Kotlin函数表达式实际上指的是函数式的表达式,函数表达式可分为两种分别是单表达式函数和Lambda表达式。
单表达式
- 当函数返回单个表达式时可以省略花括号并且在
=
等号之后的指定函数体。
fun main(args:Array<String>) {
println(double(2))//4
}
fun double(x:Int):Int = x * 2
- 当函数返回值类型可以由编译器推断时,可以省略返回类型,即显式声明返回类型是可选的。
fun main(args:Array<String>) {
println(double(2))//4
}
fun double(x:Int) = x * 2
Lambda 表达式
Lambda表示一个没有名字的函数,Lambda使用花括号{}
定义,将变量作为参数和函数体,函数体在变量之后写入,后跟->
运算符。
{variable -> body_of_function}
例如:求两个数字相加之和
普通函数式写法
fun sum(x:Int, y:Int):Int{
return x+y
}
fun main(args:Array<String>) {
println(sum(1, 2))//3
}
Lambda写法
fun main(args:Array<String>) {
val sum = { x: Int, y: Int -> x + y }
println(sum(1, 2))//3
}
Lambda可以声明一个函数后赋值给一个变量
fun main(args:Array<String>) {
val sum:(Int, Int)->Int = {x,y -> x+y}
println(sum(1, 2))//3
}
Lambda表达式的完整语法即函数类型的字面量的形式是
fun main(args:Array<String>) {
val double = {x:Int -> x * 2}
println(double(2))//4
val sum = {x:Int, y:Int -> x + y}
println(sum(1, 2))//3
val total:(Int, Int) -> Int = {x,y -> x+y}
println(total(10, 20))
}
- Lambda表达式总是被大括号
{}
包裹 - Lambda表达式完整语法形式的参数声明应当放在括号内,并有可选的类型标注。
- Lambda表达式的函数体跟在一个
->
符号后,若推断出Lambda的返回类型不是Unit则主体的最后一个表达式会视为是返回值。
集合
Kotlin中的集合分为两种分别是可变集合类Mutable
和不可变集合类Immutable
,集合类存放的都是对象的引用而非对象本身。集合类型主要包括列表List
、集合Set
、映射Map
三种。
列表、集合、映射三者之间的共性是什么呢?
- 都是接口并非实际的类
- 都继承自
Collection<out E>
接口,Collection<out E>
又继承自Iterable<out T>
接口,Collection
几乎只实现了isEmpty()
、get()
、contains()
等方法和size
属性,这一点和Java类似。 - 三类集合类型分别存在
MutableList<E>
、MutableSet<E>
、MutableMap<K, V>
接口,接口中提供了修改和操作集合的方法,比如add()
、clear()
、remove()
等函数。
在定义集合类型变量的时候,如果使用了List<E>
、Set<E>
、Map<K, V>
声明时该集合则是不可变集合。使用MutableList<E>
、MutableSet<E>
、MutableMap<K,V>
的时候集合才会转变成可变类型的集合。
列表
Kotlin的列表是一个接口合同通用的元素集合,列表接口继承自Collection <T>
类的形式,是不可变的,其方法只支持读取数据功能。若想要使用列表接口,首先需要使用listOf()
和listOf<T>()
函数,List
元素上哪找顺序插入,包含于数据相同的索引值。
接口声明
public interface List<out E>:Collection<E> (source)
一个接口是不能够直接被实例化的,Kotlin提供了相应的标准库对列表进行初始化。
- 声明并初始化不可变列表的集合:使用
listOf()
函数
fun main(args:Array<String>) {
val list = listOf("alice", "bob", "carl")//read only, fix-size
for(element in list){
println(element)
}
}
- 声明并初始化可变列表的集合:使用
mutableListOf()
函数
不可变列表
创建
Kotlin中使用listOf
函数创建一个不可变(只读)的列表,listOf
构建函数包含三个重载函数。
-
listOf()
用于创建没有元素的空列表
@kotlin.internal.InlineOnly
public inline fun <T> listOf(): List<T> = emptyList()
fun main(args:Array<String>) {
val list:List<Int> = listOf()
println(list)//[]
}
-
listOf(element:T)
用于创建只有一个元素的列表
@JvmVersion
public fun <T> listOf(element:T):List<T> = java.util.Collections.singletonList(element)
fun main(args:Array<String>) {
val list:List<Int> = listOf(0)
println(list)//[0]
}
-
listOf(vararg elements:T)
用于创建拥有多个元素的列表
public fun <T> listOf(varargs elements:T):List<T> = if(elements.size >= 0) elements.asList() else emptyList()
fun main(args:Array<String>) {
val list:List<Int> = listOf(0, 2, 4, 6, 8)
println(list)//[0, 2, 4, 6, 8]
}
查找
按索引获取元素
fun main(args:Array<String>) {
val list = listOf(1, 2, 3, 4)
println(list)//[1, 2, 3, 4]
//获取列表元素个数
println("list.size = ${list.size}")
println("list.count() = ${list.count()}")
//根据索引获取列表元素
println("list[0] = ${list[0]}")
println("list[list.size - 1] = ${list[list.size - 1]}")
println("list[list.count() - 1] = ${list[list.count() - 1]}")
//使用get函数指定索引获取列表元素
println("list.get(0) = ${list.get(0)}")
println("list.get(list.size - 1) = ${list.get(list.size - 1)}")
println("list.get(list.count() - 1) = ${list.get(list.count() - 1)}")
println("list.getOrNull(list.count()) = ${list.getOrNull(list.count())}")
println("list.getOrElse(list.count(), {it}) = ${list.getOrElse(list.count(), {it})}")
println("list.elementAt(1) = ${list.elementAt(1)}")
println("list.elementAt(list.size - 1) = ${list.elementAt(list.size - 1)}")
println("list.first() = ${list.first()}")
println("list.last() = ${list.last()}")
}
获取列表中的部分产生子列表
fun main(args:Array<String>) {
val list = (0..9).toList()
println(list)//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
val sublist = list.subList(0, 3)
println(sublist)//[0, 1, 2]
}
查找元素位置
线性查找
fun main(args:Array<String>) {
val seq = 1..9
println(seq)//1..9
val list = seq.toList()
println(list)//[1, 2, 3, 4, 5, 6, 7, 8, 9]
var item = list.indexOf(2)
println(item)//1
item = list.indexOfFirst { it%2 == 0 }
println(item)//1
item = list.indexOfLast{it%2 == 1}
println(list)//[1, 2, 3, 4, 5, 6, 7, 8, 9]
item = list.lastIndexOf(2)
println(item)//1
}
在有序列表中二分查找
Comparator二分查找
比较函数二分查找
可变列表
可变列表MutableList
中,除了继承列表List
中的函数外,另外新增了add
、addAll
、remove
、removeAll
、removeAt
、set
、clear
、retainAll
等更新修改的操作函数。
创建
fun main(args:Array<String>) {
val mlist = mutableListOf<Int>(1,3,5,7,9)
println(mlist)//[1, 3, 5, 7, 9]
mlist.add(0, 0)
println(mlist)//[0, 1, 3, 5, 7, 9]
}
使用toMutableList
函数可以将不可变列表转化为可变列表
fun main(args:Array<String>) {
val list:List<Int> = listOf<Int>(2,4,6,8)
println(list)//[2, 4, 6, 8]
//将可不变列表转换为可变列表
val mlist = list.toMutableList()
mlist.add(0, 0)
println(mlist)//[0, 2, 4, 6, 8]
}
写入
创建
更新
删除
排序
映射
Map类似一个词典,词典是有索引的,词典会根据索引查询对应的具体内容。
Kotlin中Map映射集合是一个接口和通用的元素集合,Map接口以键值对的形式保存数据,映射的键是唯一的,每个键只保留一个值。键和值可以是不同类型的一对儿,例如<Int, Int>
、<Int, String>
、<Char, String>
等。Map映射接口是不可变的,大小固定,方法只支持只读访问。若要使用Map接口需要使用mapOf()
或mapOf<k, v>()
函数。
接口声明
Interface Map<K, out V>(source)
接口属性
-
abstract val entries:Set<Entry<K, V>>
返回当前Map中Set接口的所有键值对 -
abstract val keys:Set<K>
返回当前Map接口中所有的键 -
abstract val values:Collection<V>
返回当前Map接口中所有值得读取集合,集合中可能包含重复值。
分类
Kotlin中Map可以分为两种分别是只读Map和可变Map,其中可变Map又可以分为三种分别是MutableMap、HashMap、LinkedMap。
Map<K,V>
类型集合和List和Set是有差别的,Map分为可变Map类型和不可变Map类型:
- 可变Map类型集合 初始化使用
mutableMapOf()
函数
fun main(args:Array<String>) {
val mmap = mutableMapOf("k1" to 1, "k2" to 2)
println(mmap)//{k1=1, k2=2}
}
- 不可变(只读)Map类型集合 初始化使用
mapOf()
函数
fun main(args:Array<String>) {
val map = mapOf("k1" to 1, "k2" to 2)
println(map)//{k1=1, k2=2}
}
当Map的键存在重复时,集合会过滤掉之前重复的元素。
创建
-
mapOf()
创建不可变的映射集合,接受0个或多个键值对作为Map的元素。 -
mutableMapOf()
创建可变的MutableMap集合,接受0个或多个键值对作为Map的元素。 -
hashMapOf()
创建可变的HashMap集合,接受0个或多个键值对作为Map的元素。 -
linkedMapOf()
创建可变的LinkedHashMap集合,接受0个或多个键值对作为Map的元素。 -
sortedMapOf()
创建可变的TreeMap集合,接受0个或多个键值对作为Map的元素。
标准IO
人机交互
- 意念交互
Brain Interface
- 语音交互
Voice Interface
- 眼动追踪
Eye Interface
- 体感交互`Body Interface
Kotlin中执行标准输入操作将会把字节流从输入设备(键盘)传递到主),执行标准输出操作将会从主存中把字节流传递到输出设备(屏幕)。
标准输出
Kotlin使用标准输出方法print()
和println()
函数执行输出操作,print()
和println()
函数将会分别在内部调用System.out.print()
和System.out.println()
方法。
print()
与prntln()
方法之间的区别在于print()
方法用于打印提供参数的值,而println()
方法用于打印提供参数的值后并将光标移动到下一行开头。
标准输入
Kotlin中使用标准库函数readLine()
将从标准输入流中读取字符串输入行并返回读取的行或null
。
fun main(args:Array<String>) {
print("name:")
val name:String? = readLine()
print("age:")
val age:Int = Integer.valueOf(readLine())
println("name:$name age:$age")
}
Kotlin中使用readLine
函数时返回一个可为null
的字符串,默认情况下是不能直接将可空字符串转换成为数字的,因此这里采用!!
关闭空检查,从而能够计算出结果。
fun main(args:Array<String>) {
println("请输入:")
val input1:String? = readLine()
println("请输入:")
val input2:String? = readLine()
val number1:Int = input1!!.toInt()
val number2:Int = input2!!.toInt()
println("$number1 + $number2 = ${number1+number2}")
}
使用readLine()
函数时String
字符串之外的输入行将显式转换为对应的类型,若要输入其他数据类型而非字符串,则需要使用Java标准库中的java.util.Scanner
类的Scanner
对象。
import java.util.*
fun main(args:Array<String>) {
val input = Scanner(System.`in`)
print("age:")
val age = input.nextInt()
println("age = $age")
}
Java标准库中Scanner
类的nextInt()
是用采用整数输入并存储在整形变量中的方法,其他数据类型支持
- Boolean布尔:
nextBoolean()
- Float浮点数:
nextFloat()
- Double双精度:
nextDouble()
- Long长整形:
nextLong()
异常处理
异常是程序中发生的运行时问题,会导致程序终止。异常可能是由于内存空间不足、数组越界、条件除零等引发的,异常处理就是要在程序执行期间处理这种类型的问题。
fun main(args:Array<String>) {
val percent = 101
if(percent !in 0..100){
//Exception in thread "main" java.lang.IllegalArgumentException: throw an exception at TestKt.main(test.kt:5)
throw IllegalArgumentException("throw an exception")
}
}
异常处理有助于提高程序的容错性和可读性,从功能的实现上和错误代码分离,因此即使出现了异常,程序也不会崩溃掉。
Kotlin中的异常处理与Java或其他语言极为相似,当一个函数以正常方式结束或当错误发生时会抛出异常,函数调用者捕获并处理它,如果没有则会重新在调用栈中向上抛。
捕获异常
Kotlin使用try...catch...finally
子句处理异常,Kotlin并不会去区分检查异常和非检查异常。当无需指定一个函数可能会抛出的异常,甚至可能会无需处理任何异常。
import java.io.BufferedReader
import java.io.StringReader
fun read(reader: BufferedReader):Int?{
try{
val line = reader.readLine()
return Integer.parseInt(line)
}catch(ex:Exception){
return null
}finally{
reader.close()
}
}
fun main(args:Array<String>) {
val buf = BufferedReader(StringReader("1234"))
println(read(buf))//1234
}
Kotlin中如果在函数内部抛出了异常,或在函数内部调用其它函数时抛出异常。此时函数将在抛出异常的地方结束。比如不希函数结束,那此时就必须捕获并处理这个异常。
异常处理中使用了四种不同的关键字,它们分别是try
、catch
、finally
、throw
。
- try 表示代码块中可能会出现异常代码
- catch 表示对try中可能出现的异常做处理,可以存放在多个cache代码块,
- finally 表示用于回收try中打开的资源,异常处理机制保证finanlly中的代码总被助兴。即使
try...catch
中执行了return
,也仍然会执行,但需要执行`System.exit(!)退出虚拟机就不能执行。
所需注意的是
抛出异常
Kotlin中所有异常类都是Throwable
类的子类,抛出异常对象则使用throw
表达式。对于每个异常来说,不仅仅包括异常的信息,还可以选择性包括异常的原因
# 使用throw表达式抛出异常
throw MyException("throw an exception")
Kotlin中创建异常实例时无需使用new
关键字
Kotlin中的throw
结构是一个表达式,能作为另一个表达式的一部分使用。