注释
单行注释和多行注释
kotlin和java一样支持当行(// 注释内容)和多行注释(/* 注释内容 */),不同的是kotlin的多行注释支持嵌套,例如:
private fun testComment(msg: String) {
//单行注释
/*多行注释开头
/*嵌套的注释内容*/
多行注释结尾*/
Log.d(TAG, "multilineComment: $msg")
}
文档注释
Kotlin的文档注释和java一样,使用/** 注释内容 */
但android studio的在方法上直接输入“/**”并回车,生成的注释没有参数和返回值,可以安装一个插件BugKotlinDocument解决
变量
分隔符
分号(;)
同一行内只有一条语句,可以不以分号结尾,但同一行内书写多条独立的语句,则前面语句就需要使用分号表示结束,例如:
super.onCreate(savedInstanceState);setContentView(R.layout.activity_main)
Kotlin表达式、调用方法、访问属性可以跨多行,字符串跨多行要以加号连接,例如:
var testStr = "test across " +
"multiple lines"
//表达式可以跨多行
testStr +=
" expression"
//调用方法时可以跨多行
var isMethod = testStr
.startsWith ("method")
//访问属性时也可以跨多行
var field = testStr
.length
花括号({})
定义代码块,类体部分、枚举、方法体需要放在花括号中定义,条件语句中的条件执行体和循环语句中的循环体通常也放在代码块里
方括号([])
索引运算符,用于访问数组元素、List集合元素和Map集合元素
var array = arrayOf(3, 2, 9, 5)
Log.d(TAG, "onCreate: ${array[2]}")
var list = listOf("a", "b", "c", "d", "e")
Log.d(TAG, "onCreate: ${list[2]}")
var map = mapOf("apple" to 5, "orange" to 8)
Log.d(TAG, "onCreate: ${map["apple"]}")
圆括号(())
定义函数、方法时用圆括号包含所有的形参声明,调用函数时用圆括号传入实参值,优先计算圆括号内的表达式
空格( )
使用空格合理地缩进代码,从而提供更好的可读性
圆点(.)
常用作类、结构体、枚举、实例和它的成员(包括属性和方法)之间的分隔符,表明调用某个类或某个实例的指定成员
标识符规则
标识符是用于给程序中的变量、类、枚举、函数等命名的名字。 标识符的长度没有限制,是区分大小写的
可以由字符、数字和下画线_组成,但不能以数字开头,此处的字符并不局限于英文字母,可以包含中文字符、日文字符等
标识符不能是Kotlin的硬关键字,在特定上下文中不能使用软关键字和修饰符关键字,但可以包含关键字
标识符不能包含空格
标识符只能包含下画线_,不能包含@、#等特殊
关键字
硬关键字:无论在什么情况下都不能用作标识符
软关键字:可以在它们不起作用的上下文中用作标识符
修饰符关键字:可以在代码中用作标识符
硬关键宇
硬关键宇 | 作用 |
---|---|
as | 类型转换或为 import 语句指定别名 |
as? | 类型安全 类型转换运算符 |
break | 中断循环 |
class | 声明类 |
continue | 忽略本次循环剩下的语句,重新开始下一次循环 |
do | 用于 do while 循环 |
else | 在 if 分支中使用 |
false | 在Boolean类型中表示假的直接量 |
for | 用于 for 循环 |
fun | 声 明函数 |
if | 在 if 分支中使用 |
in | 在 for 循环中使用,可作为双目运算符,检查一个值是否处于区间或集合内,也可在 when 表达式中使用,还可用于修饰泛型参数,表明该泛型参数支持逆变 |
!in | 可作为双目运算符 的反义词:!in 也可在 when 表达式中使用 |
is | 用于做类型检查(类似 java的instanceof) 或在 when 表达式中使用 |
!is | is 的反义词 |
null | 空的直接量 |
object | 声明对象表达式或定义命名对象 |
package | 前文件指定包 |
return | 函数的返回 |
super | 引用父类实现的方法或属性,或者在子类构造器中调用父类构造器 |
this | 表当前类的对象或在构造器中调用当前类的其他构造器 |
throw | 抛出异常 |
true | 在 Boolean 类型中表示真的直接量 |
try | 开始异常处理 |
typealias | 定义类型别名 |
val | 声明只读属性或变量 |
var | 声明可变属性或变量 |
when | 用于 when 表达式 |
while | 用于 while 循环或 do while 循环 |
软关键字
软关键字 | 作用 |
---|---|
by | 用于将接口或祖先类的实现代理给其他对象 |
catch | 在异常处理中用于捕捉异常 |
constructor | 声明构造器 |
delegate | 指定该注解修饰委托属性存储其委托实例的字段 |
dynamic | 主要用于在 Kotlin/JavaScript 中引用一个动态类型 |
field | 用于指定该注解修饰属性的幕后字段 |
file | 用于指定该注解修饰该源文件本身 |
finally | 异常处理中的 finally块 |
get | 用于声明属性的getter方法,或者用于指定该注解修饰属性的getter方法 |
import | 导包 |
init | 声明初始化块 |
param | 指定该注解修饰构造器参数 |
property | 指定该注解修饰整个属性(这种目标的注解对 Java 不可见,因为 Java并没有真正的属性) |
receiveris | 用于指定该注解修饰扩展方法或扩展属性的接收者 |
set | 用于声明属性的 se er 方法,或者用于指定该注解修饰属性的 se er 方法 |
setparam | 用于指定该注解修饰 setter 方法的参数 |
where | 用于为泛型参数增加限制。 |
修饰符关键宇
修饰符关键宇 | 作用 |
---|---|
abstract | 修饰抽象类或抽象成员 |
annotation | 修饰注解类 |
companion | 声明伴生对象 |
const | 声明编译时常量 |
crossinline | 用于禁止在传给内联函数的 Lambd 表达式中执行非局部返回 |
data | 声明数据类 |
enum | 声明枚举 |
external | 声明某个方法不由 Kotlin 实现(与 Java native 相似〉 |
final | 禁止被重写 |
infix | 声明该函数能以双目运算符的格式执行 |
inline | 声明内联函数, Lambda 表达式可在内联函数中执行局部返回 |
inner | 声明内部类,内部类可以访问外部类的实例 |
internal | 表示被修饰的声明只能在当前模块内可见 |
lateinit | 修饰 non-null 属性,用于指定该属性可在构造器以外的地方初始化 |
noinline | 禁止内联函数中个别 Lambda 表达式被内联化 |
open | 用于修饰类,表示该类可派生子类;或者用于修饰成员,表示该成员可以被重写 |
out | 修饰泛型参数,表明该泛型参数支持协变 |
override | 声明重写父类的成员 |
private | private 访问权限 |
protected | protected 访问权限 |
public | public 访问权限 |
reified | 修饰内联函数中的泛型形参,接下来在该函数中就可像使用普通类型样使用该类型参数 |
sealed | 声明密封类 |
suspend | 标识一个函数后 Lambda 表达式可作为暂停 |
tailrec | 用于修饰一个函数可作为尾随递归函数使用 |
vararg | 用于修饰形参,表明该参数是个数可变的形参 |
声明变量
kotlin是一门强类型的语言,变量必须先声明、后使用,声明变量时必须显式或隐式指定变量的类型,当显式和隐式指定都存在时,类型要保持一致,指定类型的变量也只能接受类型与之匹配的值
声明变量需要使用 var或val ,如下所示:
var | val 变量名[:类型][=初始值]
//通过":类型"的形式显式指定该变量的类型
var gender:String
//为该变量指定初始值,隐式指定变量的类型
val name = "小明"
//显式和隐式指定都存在时,类型要保持一致
var age: Int = 18
//var声明的变量值可变
age = 19
//val声明的变量值不可变,因此这里会编译报错
name = "小张"
其中使用 var 声明的变量是值可变的(可被多次赋值),使用 val 声明的变量是值不可变的(不能被重新赋值),相当于常量,Kotlin 的常量分为两种:
局部范围的常量
允许在声明时不指定初始值,只要在第 次使用之前指定初始值即可
类的常量属性
既可以在声明时指定初始值,也可以在类或结构体的构造器中指定初始值
整型
Kotlin 的整型与 Java 不同, Kotlin 的整型不是基本类型,而是引用类型(大致相当于 Java的包装类)
Kotlin是 null 安全的语言,因此Byte、Short、Int、Long 类型变都不能接受 null 值,如果要存储 null 值,则应该使用 Byte?、Short?、Int?、Long?类型
不带“?”后缀的整型变量将会映射成 Java 的基本类型,带“?”后缀的整型变量将会映射成基本类型的包装类
整型类型
整型类型 | 所占位数 | 表数范围 |
---|---|---|
Byte | 8位 | -128~127 |
Short | 16位 | -215~215-1 |
Int | 31位 | -231~231-1 |
Long | 64位 | -263~263-1 |
使用“===”比较相等,要求两个变量引用同一个对象
var value1: Int = 200 // value1 的类型是 Java int 类型
var value2: Int = 200 // value2 Java int 类型
Log.d(TAG, "onCreate: ${value1 == value2}") // 基本类型比较,输出 true
var obj1: Int? = 200 // obj1 的类型是 Java Integer 类型
var obj2: Int? = 200 // obj2 的类型是 Java Integer 类型
// 引用类型比较,输出 false,但如果obj1和obj2值都改为小于-128~127之间值相等的整数,则返回true
Log.d(TAG, "onCreate: ${obj1 == obj2}")
由于 Int 型是 Kotlin 最常用的整数类型,因此,如果声明一个常量或变量时没有显式指定数据类型,只是简单地指定了其初始值为整数值,那么 Kotlin 会自动判断该变量的类型为 Int
kotlin 的整数数值表示方式
Kotlin 不支持八进制的整数
- 十进制:普通的整数数值就是十进制的整数
- 二进制:以 0b或0B 开头的整数数值
- 十六进制:以 0x或0X 开头的整数数值,其中 10~15 分别以af(此处的af不区分大小写)来表示
//以0b或0B开头的整数数值是二进制的整数
var binValuel = 0b1010101
var binValue2 = 0B10101110
//以0x或0X开头的整数数值是十六进制的整数
var hexValuel = 0x13
var hexValue2 = 0XaF
kotlin允许为数值(包括浮点型)增加下画线作为分隔符
val value = 1_000_000
浮点型
浮点型数值可以包含小数部分,浮点型比整型的表数范围更大,可以存储比 Long 型更大或更小的数
浮点型类型
浮点型类型 | 位数 | 使用情况 |
---|---|---|
Float | 32位 | 精度要求不高时可以使用此种类型 |
Double | 64位 | 当程序要求存储很大或者高精度的浮点数时使 |
kotlin 的浮点数表示方式
十进制数形式:简单的浮点数 例如6.74、530.0、0.54,必须包含 数点,否则会被当成整数类型处理
科学计数形式:例如5.12e(即 5.12*10^2 ),e不区分大小写
只有浮点型的数值才可以使用科学计数形式表示,例如: 5000 是Int型的值,但 5E3是浮点型的值
如果声明一个常量或变量时没有指定数据类型,只是简单地指定了其初始值为浮点数,那kotlin会自动判断该变量的类型为Double
kotlin 还提供了3个特殊的浮点型数值:正无穷大、负无穷大和非数
例如,使用一个正数除以0.0将得到正无穷大数值,使用一个负数除以0.0将得到负无穷大数值,0.0除以0.0或对负数开方将得到个非数,所有的正无穷大数值都相等 所有的负无穷大数值都相等;而非数不与任何数值相等,甚至和非数自己都不相等
字符型
字符型通常用于表示单个的字符,必须使用单引号(’)括起来,Kotlin使用16 位的 Unicode 字符集作为编码方式,支持各种语言的字符,与Java 不同的是, Kotlin中 Char 型变量不能当成整数值使用
kotlin 的字符表示方式
- 通过单个字符来指定字符型值,如'A' 、'c'、'9'等
- 通过转义字符表示特殊字符型值,如'\n'、'\t'
- 直接使用 Unicode 值来表示字符型值,格式是'\uXXXX',其中 XXXX 代表一个十六进制的整数
数值型之间的类型转换
整型之间的转换
与Java 不同, Katlin 不支持取值范围小的数据类型隐式转换为取值范围大的类型
toByte(): 转换为 Byte 类型
toShort():转换为 Short 类型
tolnt(): 转换为 Int 类型
toLong():转换为 Long 类型
toFloat(): 转换为 Float 类型
toDouble(): 转换为 Double 类型
toChar(): 转换为 Char 类型
取值范围大的变量或表达式强制转换为取值范围小的类型时,可能发生溢出,例如将233 强制转换为 Byte 类型,从而变成了-23,如下图:
反码:正数的反码还是等于原码,负数的反码就是他的原码除符号位外,按位取反
补码:正数的补码还是等于原码,负数的补码等于反码+1
Kotlin 缺乏隐式转换,但 Kotlin 在表达式中可以自动转换,这种转换是基于上下文推断出来的,例如:
val byteValue: Byte = 17
val shortValue: Short = 32
val total1 = byteValue + shortValue
//将Byte类型和Short类型相加,不需要对它们进行强制转换,Kotlin将会自动把它们
//提升到 Int 类型之后再进行计算,因此整个表达式计算得到的结果是 Int 类型
Log.d(TAG, "onCreate: ${total1.javaClass}")
val value1 = 89
val value2 = 764
//下面表达式中的 value1 强制转换为 Long 类型,最高等级的操作数是 Long 类型,
//因此整个表达式计算得到的结果也是 Long 类型
val total2 = value1.toLong() + value2.toByte()
Log.d(TAG, "onCreate: ${total2.javaClass}")
Char 型值虽然不能被当成整数进行算术运算,但 Kotlin Char 类型提供了加、减运算支持,其计算规则如下:
Char 型值加、减一个整型值:先将该 Char 型值对应的字符编码进行加、减该整数,然后将计算结果转换为 Char 型值
两个 Char 型值进行相减:Kotlin 将用两个 Char 型值对应的字符编码进行减法运算,最后返回 Int 类型的值。两个 Char 型值不能相加。
var c1 = 'f'
var c2 = 'k'
Log.d(TAG, "onCreate: ${c1 + 4}")//输出j
Log.d(TAG, "onCreate: ${c1 - 4}")//输出b
Log.d(TAG, "onCreate: ${c1 - c2}")//输出-5
当进行类型转换时,应该尽量向表数范围大的数据类型转换,Kotlin 的各种数值型的表数范围由小到大的顺序为 Byte->Short->Int->Long->Float->Double
表达式类型的自动提升
当一个算术表达式中包含多个数值型的值时,整个算术表达式的数据类型将发生自动提升,规则如下:
- 所有的 Byte、Short 类型将被提升到 Int 类型
- 整个算术表达式的数据类型自动提升到与表达式中最高等级操作数同样的类型
如果表达式中包含了字符串:
//输出字符串Hello!a7
Log.d(TAG, "onCreate: ${"Hello!" + 'a' + 7}")
//输出字符串hHello!
Log.d(TAG, "onCreate: ${'a' + 7 + "Hello!"}")
Boolean 类型
用于表示逻辑上的“真”或“假”,Boolean 类型的值只能是 true或false
null 安全
非空类型和可空类型
var str="dhfjsd"
//由于str转换为Int会失败,会直接挂掉,
//Caused by: java.lang.NumberFormatException: For input string: "dhfjsd"
var num1: Int = str.toInt()
//str转换为Int有可能失败,num有可能没有值,因此不能使用Int来声明num的类型,会编译报错
var num2: Int = str.toIntOrNull()
//正确写法
var num3: Int? = str.toIntOrNull()
//这种写法也正确,编译器会推断出该类型为Int?
var num4 = str.toIntOrNull()
在类型的后面加上?就变成了可空类型
先判断后使用
Kotlin 对可空类型进行了限制 :如果不加任何处理,可空类型不允许直接调用方法、访问属性,但可以先判断该变量不为 null ,然后再调用该变量的方法或属性
//先判断str不为null,然后访length属性
var len = if (str != null) str.length else -1
对于可空的 Boolean 类型而言,它可以接受3个值,即 true、false、 null ,因此对 Boolean?类型变量进行判断时,需要使用 Boolean?变量显式与 true或者false 值进行比较,例如:
var b: Boolean? = null
if ( b == true ) {
......
}
如果将 if 分支改为如下形式,编译器会报错,因Kotlin if 条件必须是 Boolean 类型,而 Boolean? 与 Boolean 本质上是两种不同的类型
if ( b ) {
......
}
安全调用
使用“?.”进行安全调用,例如:
var str : String? ="dsfdsg"
Log.d(TAG, "onCreate: ${str?.length}") //输出6
str = null
Log.d(TAG, "onCreate: ${str?.length}") //输出null
如果str不为null,调用其length属性并返回,否则返回null
此外,安全调用还可与 let 全局函数结合使用
//定义一个元索可空的数组
val myArr: Array = arrayOf("fkit", "fkjava ", null, "crazyit")
for (item in myArr) {
//当 item 不为 null 时才调用 let 函数
item?.let {
Log.d(TAG, "onCreate: $item")
}
}
Elvis 运算
if else 的简化写法
var str: String? = "dsfdsg"
//使用 Elvis 运算符
var len = str?.length ?: -1
“?:”的含义是如果“?:”左边的表达式不为 null ,则返回左边表达式的值,否则返回“?:”右边表达式的值
此外,由于 kotlin的return、throw 都属于表达式,因此它们也都可以用在“?:”运算符的右边,例如:
var len = str?.length ?: throw RuntimeException("str is null")
强制调用
使用“!!.”进行强制调用,即可强制调用可空变量的方法或属性,这样强制调用可能引发空指针异常,例如
//定义一个元索可空的数组
val myArr: Array = arrayOf("fkit", "fkjava ", null, "crazyit")
for (item in myArr) {
//当 item 为 null 时会出现空指针异常
item!!.let {
Log.d(TAG, "onCreate: $item")
}
}
字符串
字符串类型
String允许通过形如s[i]的格式来访问字符串指定索引处的字符,也可通过 for循环遍历字符串中的每个字符,例如:
var str: String = "dsfdsg"
Log.d(TAG, "onCreate: ${str[2]}")//输出f
for (c in str) {
Log.d(TAG, "onCreate: ${c}")
}
字符串有两种字面值:
- 转义字符串: 可以包含转义字符
- 原始字符串:原始字符串内部没有转义,可以包含换行和任意文本,需要用3个引号引起来,
//转义字符串
var str = "fkjava. rq"
//原始字符串
val originText = """
val myArr: Array = arrayOf("fkit", "fkjava ", null, "crazyit")
for (item in myArr) {
//当 item 不为 null 时才调用 let 函数
item?.let {
Log.d(TAG, "onCreate: ")
}
}
""".trimMargin()
Log.d(TAG, "onCreate: $originText")
用trimMargin() 方法来去掉原始字符串前面的缩进,默认情况下,使用竖线(|)作为边界符,也就是说所有竖线(|)之前的内容都会被去掉,也可使用其他字符作为边界符, 此时就需要在trimMargin()方法中传入该边界符作为参数
字符串模板
kotlin允许在字符串中嵌入变量或表达式,只要将变量或表达式放入${}中即可,这样kotlin将会把该变量或表达式的值嵌入该字符串中
var str: String = "dsfdsg"
//在字符串模板中嵌入变量
var str1 = "str长度是:${str}"
//在字符串模板中嵌入方法调用
var str2 = "最大值是:${max(5, 28)}"
Katlin 字符串的方法
Kotlin String Java String 并不是同一个类,Kotlin Strin 类提供了更多的方法,
如一系列toXXX()方法将字符串转换成数值,例如:
str.toDouble()
str.toInt()
将字符串首字母大写、小写的方法:
str.capitalize()//大写
str.decapitalize()//小写
返回两个字符串相同的前缀、后缀的方法
str.commonPrefixWith ("crazyjava.org") //前缀
str.commonSuffixWith ("fkit.org") //后缀
Java的String.contains()方法不支持使用正则表达式,但 kotlin的String.contains()方法支持使用正则表达式匹配
var str = "java886"
//判断 str 是否包含3个连续的数字
println(str.contains(Regex("\\d{3}")))
类型别名
Kotlin 提供了 typealias 来定义类型别名,typealias 语句的语法格式为:
typealias 类型别名=已有类型
//为MutableMap>指定更短的别名:FileTable
typealias FileTable = MutableMap>
给内部类起别名
class A {
inner class Inner {}
}
typealias AInner = A.Inner
var aInner = A().AInner()
Java Lambda 表达式的类型是函数式接口,而 kotlin Lambda 表达式的类型直接就是函数类型,因此 kotlin允许为 Lambda 表达式的类型指定别名
//为(T)->Boolean 类型指定别名 Predicate
typealias Predicate = (T) -> Boolean
//使用Predicate定义变量变量,其值是 Lambda 表达式
val p: Predicate = { it.length > 4 }
//为 filter()方法传入p参数,只保留长度大于4的字符串
println(arrayOf("Java", "Objective", "Kotlin").filter(p))