Kotlin 基础语法
Kotlin 文件以 .kt 为后缀。
包声明
代码文件的开头一般为包的声明:kotlin的声明与java基本无差别:但是有一点得注意:
kotlin源文件不需要相匹配的目录和包,源文件可以放在任何文件目录。
以下例中 MainActivity 的全名是 com.example.workingspace.com.manco.sample.Main2Activity、class的全名是 com.example.workingspace.com.manco.sample。
如果没有指定包,默认为 default 包。
默认导入
有多个包会默认导入到每个 Kotlin 文件中:
- kotlin.*
- kotlin.annotation.*
- kotlin.collections.*
- kotlin.comparisons.*
- kotlin.io.*
- kotlin.ranges.*
- kotlin.sequences.*
- kotlin.text.*
函数定义
函数定义使用关键字 fun,参数格式为:参数 : 类型
表达式作为函数体,返回类型自动推断:
//sampleStart fun sum(a: Int, b: Int): Int { return a + b }
无返回值的函数(类似Java中的void):
fun sum(a:Int){ Log.e("TAG", "a==="+a) }
函数返回无意义的值:
Unit 返回类型可以省略:
//sampleStart fun printSum(a: Int, b: Int): Unit { Log.e("TAG", "a==="+a+"\nb==="+b) }
可变长参数函数
函数的变长参数可以用 vararg 关键字进行标识:
fun vars(vararg v:Int){ for(vt in v){ print(vt) } }
class Main2Activity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main2) vars(1,2,3,4,5) } }输出:
01-02 14:04:55.612 20995-20995/com.example.workingspace E/TAG: vars===1
01-02 14:04:55.612 20995-20995/com.example.workingspace E/TAG: vars===2
01-02 14:04:55.612 20995-20995/com.example.workingspace E/TAG: vars===3
01-02 14:04:55.612 20995-20995/com.example.workingspace E/TAG: vars===4
01-02 14:04:55.612 20995-20995/com.example.workingspace E/TAG: vars===5
lambda(匿名函数)
lambda表达式使用实例:
class Main2Activity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main2) val sumLambda: (Int, Int) -> Int = { x, y -> x + y } Log.e("TAG", "sumLambda==" + sumLambda(1, 2)) } }输出:
01-02 14:08:16.442 21122-21122/? E/TAG: sumLambda==3
定义常量与变量
可变变量定义:var 关键字
var <标识符> : <类型> = <初始化值>
不可变变量定义:val 关键字,只能赋值一次的变量(类似Java中final修饰的变量)
val <标识符> : <类型> = <初始化值>
常量与变量都可以没有初始化值,但是在引用前必须初始化
编译器支持自动类型判断,即声明时可以不指定类型,由编译器判断。
val a: Int = 1 val b = 1 // 系统自动推断变量类型为Int val c: Int // 如果不在声明时初始化则必须提供变量类型 c = 1 // 明确赋值 var x = 5 // 系统自动推断变量类型为Int x += 1 // 变量可修改
class Main2Activity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main2) val v1 = "测试内容" var v2 = "测试内容1" Log.e( "TAG", "v1:" + v1 + "\nv2:" + v2) v1 = "......" v2 = "......" Log.e( "TAG", "v1:" + v1 + "\nv2:" + v2) } }有v1,v2两个定义,其中v1为val定义,v2为var定义,此时运行报错出现:
Val cannot be reassigned(Val不能重新分配)
此时就是说,val定义的内容不可重新定义,var可以定义!
class Main2Activity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main2) val v1 = "测试内容" var v2 = "测试内容1" Log.e("TAG", "v1:" + v1 + "\nv2:" + v2) v2 = "......" Log.e("TAG", "v1:" + v1 + "\nv2:" + v2) } }输出:
01-02 14:15:44.504 21250-21250/com.example.workingspace E/TAG: v1:测试内容
v2:测试内容1
01-02 14:15:44.504 21250-21250/com.example.workingspace E/TAG: v1:测试内容
v2:......
注释
Kotlin 支持单行和多行注释,实例如下:
// 这是一个单行注释 /* 这是一个多行的 块注释。 */与 Java 不同, Kotlin 中的块注释允许嵌套。
//它可以正常运行,而java的则不行:
字符串模板
$ 表示一个变量名或者变量值
$varName 表示变量值
${varName.fun()} 表示变量的方法返回值:
class Main2Activity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main2) var a = 1 // 模板中的简单名称: val s1 = "a is $a" Log.e("TAG", "s1:" + s1) a = 2 // 模板中的任意表达式: val s2 = "${s1.replace("is", "was")}, but now is $a" Log.e("TAG", "s2:" + s2) } }输出:
01-02 14:25:25.883 21383-21383/com.example.workingspace E/TAG: s1: a is 1
01-02 14:25:25.904 21383-21383/com.example.workingspace E/TAG: s2: a was 1, but now is 2
NULL检查机制
Kotlin的空安全设计对于声明可为空的参数,在使用时要进行空判断处理,有两种处理方式,字段后加!!像Java一样抛出空异常,另一种字段后加?可不做处理返回值为 null或配合?:做空判断处理
//类型后面加?表示可为空 var age: String? = "23" //抛出空指针异常 val ages = age!!.toInt() //不做处理返回 null val ages1 = age?.toInt() //age为空返回-1 val ages2 = age?.toInt() ?: -1当一个引用可能为 null 值时, 对应的类型声明必须明确地标记为可为 null。
当 str 中的字符串内容不是一个整数时, 返回 null:
class Main2Activity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main2) Log.e("TAG", "parseInt: " + parseInt("0.1")) main(arrayOf("1","2")) main(arrayOf("a", "7")) main(arrayOf("a", "b")) } fun main(args: Array <String>) { if (args.size < 2) { print("Two integers expected") return } val x = parseInt(args[0]) val y = parseInt(args[1]) Log.e("TAG", "args[0]: " + args[0] + " args[1]: " + args[1]) Log.e("TAG", "x?: " + x + " y?: " + y) // 直接使用 `x * y` 会导致错误, 因为它们可能为 null. if (x != null && y != null) { // 在进行过 null 值检查之后, x 和 y 的类型会被自动转换为非 null 变量 Log.e("TAG", "x: " + x + " y: " + y) } } fun parseInt(str: String): Int? { return str.toIntOrNull() } }输出:
01-02 14:40:26.726 22295-22295/com.example.workingspace E/TAG: parseInt: null
01-02 14:40:26.726 22295-22295/com.example.workingspace E/TAG: args[0]: 1 args[1]: 2
01-02 14:40:26.726 22295-22295/com.example.workingspace E/TAG: x?: 1 y?: 2
01-02 14:40:26.726 22295-22295/com.example.workingspace E/TAG: x: 1 y: 2
01-02 14:40:26.726 22295-22295/com.example.workingspace E/TAG: args[0]: a args[1]: 7
01-02 14:40:26.726 22295-22295/com.example.workingspace E/TAG: x?: null y?: 7
01-02 14:40:26.726 22295-22295/com.example.workingspace E/TAG: args[0]: a args[1]: b
01-02 14:40:26.726 22295-22295/com.example.workingspace E/TAG: x?: null y?: null此时正常整数返回的就是正常整数,非正常整数返回的就是null
空安全:
可空类型与非空类型
Kotlin 的类型系统旨在消除来自代码空引用的危险,也称为《十亿美元的错误》。
许多编程语言(包括 Java)中最常见的陷阱之一,就是访问空引用的成员会导致空引用异 常。在 Java 中,这等同于 NullPointerException 或简称 NPE 。
Kotlin 的类型系统旨在从我们的代码中消除 NullPointerException 。NPE 的唯一可能的原因 可能是显式调用 throw NullPointerException() ;
使用了下文描述的 !! 操作符;
对于初始化,有一些数据不一致(如一个未初始化的 this 用于构造函数的某个地
方);
Java 互操作:
企图访问平台类型的 null 引用的成员;
用于具有错误可空性的 Java 互操作的泛型类型,例如一段 Java 代码可能会向 Kotlin 的 MutableList<String> 中加入 null ,这意味着应该使用 MutableList<String?> 来处理它;
由外部 Java 代码引发的其他问题。
在 Kotlin 中,类型系统区分一个引用可以容纳 null (可空引用)还是不能容纳(非空引 用)。 例如,String 类型的常规变量不能容纳 null :
var a: String = "abc"
a = null // 编译错误
如果要允许为空,我们可以声明一个变量为可空字符串,写作 String? :
var b: String? = "abc"
b = null // ok
现在,如果你调用 a 的方法或者访问它的属性,它保证不会导致 NPE ,这样你就可以放心 地使用:
val l = a.length
但是如果你想访问 b 的同一个属性,那么这是不安全的,并且编译器会报告一个错误:
val l = b.length // 错误:变量“b”可能为空
但是我们还是需要访问该属性,对吧?有几种方式可以做到。 在条件中检查 null
首先,你可以显式检查 b 是否为 null ,并分别处理两种可能:
val l = if (b != null) b.length else -1
编译器会跟踪所执行检查的信息,并允许你在 if 内部调用 length 。 同时,也支持更复杂 (更智能)的条件:
if (b != null && b.length > 0) {
Log.e("TAG","String of length ${b.length}")
} else {
Log.e("TAG","Empty string")
}
请注意,这只适用于 b 是不可变的情况(即在检查和使用之间没有修改过的局部变量 ,或 者不可覆盖并且有幕后字段的 val 成员),因为否则可能会发生在检查之后 b 又变为 null 的情况。
安全的调用
你的第二个选择是安全调用操作符,写作 ?. : b?.length
如果 b 非空,就返回 b.length ,否则返回 null ,这个表达式的类型是 Int? 。
安全调用在链式调用中很有用。例如,如果一个员工 Bob 可能会(或者不会)分配给一个部 门, 并且可能有另外一个员工是该部门的负责人,那么获取 Bob 所在部门负责人(如果有的 话)的名字,我们写作:
bob?.department?.head?.name
如果任意一个属性(环节)为空,这个链式调用就会返回 null 。
如果要只对非空值执行某个操作,安全调用操作符可以与 let 一起使用:
val listWithNulls: List<String?> = listOf("A", null)
for (item in listWithNulls) {
item?.let { println(it) } // 输出 A 并忽略 null
}
安全调用也可以出现在赋值的左侧。这样,如果调用链中的任何一个接收者为空都会跳过赋 值,而右侧的表达式根本不会求值:
// 如果 `person` 或者 `person.department` 其中之一为空,都不会调用该函数:
person?.department?.head = managersPool.getManager()
类型检测及自动类型转换
我们可以使用 is 运算符检测一个表达式是否某类型的一个实例(类似于Java中的instanceof关键字)。
class Main2Activity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main2) Log.e("TAG", "getStringLength:" + getStringLength("abc")) Log.e("TAG", "getStringLength:" + getStringLength("123")) } fun getStringLength(obj: Any): Int? { if (obj is String) { // 做过类型判断以后,obj会被系统自动转换为String类型 return obj.length } //在这里还有一种方法,与Java中instanceof不同,使用!is if (obj !is String) { return obj.toString().toInt() } // 这里的obj仍然是Any类型的引用 return null } }输出:
01-02 14:52:44.801 22793-22793/com.example.workingspace E/TAG: getStringLength:3 //返回 return obj.length
01-02 14:52:44.801 22793-22793/com.example.workingspace E/TAG: getStringLength:123 //返回 return obj.toString().toInt()
区间
区间表达式由具有操作符形式 .. 的 rangeTo 函数辅以 in 和 !in 形成。
区间是为任何可比较类型定义的,但对于整型原生类型,它有一个优化的实现。以下是使用区间的一些示例:
class Main2Activity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main2) for (i in 1..4) { Log.e("TAG", "for==one===" + i) if (i in 1..10) { // 等同于 1 <= i && i <= 10 Log.e(" TAG", " for== one== if= " + i) } } for (i in 4..1) { Log.e(" TAG", " for== two=== " + i) } // 使用 step 指定步长 数列迭代: for (i in 1..4 step 2) { Log.e(" TAG", " for== three=== " + i) } for (i in 4 downTo 1 step 2) { Log.e(" TAG", " for== foue=== " + i) } // 使用 until 函数排除结束元素 for (i in 1 until 10) { // i in [1, 10) 排除了 10 Log.e(" TAG", " for== five=== " + i) } } }输出:
01-02 17:09:55.481 23177-23177/com.example.workingspace E/TAG: for==one===1
01-02 17:09:55.481 23177-23177/com.example.workingspace E/TAG: for==one==if=1
01-02 17:09:55.481 23177-23177/com.example.workingspace E/TAG: for==one===2
01-02 17:09:55.481 23177-23177/com.example.workingspace E/TAG: for==one==if=2
01-02 17:09:55.481 23177-23177/com.example.workingspace E/TAG: for==one===3
01-02 17:09:55.482 23177-23177/com.example.workingspace E/TAG: for==one==if=3
01-02 17:09:55.482 23177-23177/com.example.workingspace E/TAG: for==one===4
01-02 17:09:55.482 23177-23177/com.example.workingspace E/TAG: for==one==if=4
01-02 17:09:55.487 23177-23177/com.example.workingspace E/TAG: for==three===1
01-02 17:09:55.487 23177-23177/com.example.workingspace E/TAG: for==three===3
01-02 17:09:55.487 23177-23177/com.example.workingspace E/TAG: for==foue===4
01-02 17:09:55.487 23177-23177/com.example.workingspace E/TAG: for==foue===2
01-02 17:09:55.487 23177-23177/com.example.workingspace E/TAG: for==five===1
01-02 17:09:55.487 23177-23177/com.example.workingspace E/TAG: for==five===2
01-02 17:09:55.487 23177-23177/com.example.workingspace E/TAG: for==five===3
01-02 17:09:55.487 23177-23177/com.example.workingspace E/TAG: for==five===4
01-02 17:09:55.487 23177-23177/com.example.workingspace E/TAG: for==five===5
01-02 17:09:55.487 23177-23177/com.example.workingspace E/TAG: for==five===6
01-02 17:09:55.487 23177-23177/com.example.workingspace E/TAG: for==five===7
01-02 17:09:55.487 23177-23177/com.example.workingspace E/TAG: for==five===8
01-02 17:09:55.487 23177-23177/com.example.workingspace E/TAG: for==five===9使用 in 运算符来判断集合内是否包含某实例:
class Main2Activity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main2) val items = setOf("apple", "banana", "kiwi") when { "orange" in items -> Log.e("TAG", "juicy") "apple" in items -> Log.e("TAG", "apple is fine too") } } }输出:
01-02 17:15:41.472 23311-23311/com.example.workingspace E/TAG: apple is fine too //判断数组里面只有apple而没有orange
使用 lambda 表达式来过滤(filter)和映射(map)集合:
class Main2Activity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main2) val fruits = listOf("banana", "avocado", "apple", "kiwi") fruits.filter { //a开头的内容 Log.e("TAG","filter: "+it.startsWith("a")) it.startsWith("a") }.sortedBy { //输出a开头的内容 Log.e("TAG","sortedBy: "+it) }.map { //输出大写 Log.e("TAG","map: "+it.toUpperCase()) }.forEach { //Kotlin中调用forEach函数时的特殊写法 Log.e("TAG","forEach: "+it) } } }输出:
01-02 17:19:18.717 23434-23434/com.example.workingspace E/TAG: filter: false
01-02 17:19:18.717 23434-23434/com.example.workingspace E/TAG: filter: true
01-02 17:19:18.717 23434-23434/com.example.workingspace E/TAG: filter: true
01-02 17:19:18.717 23434-23434/com.example.workingspace E/TAG: filter: false
01-02 17:19:18.718 23434-23434/com.example.workingspace E/TAG: sortedBy: apple
01-02 17:19:18.718 23434-23434/com.example.workingspace E/TAG: sortedBy: avocado
01-02 17:19:18.719 23434-23434/com.example.workingspace E/TAG: map: APPLE
01-02 17:19:18.719 23434-23434/com.example.workingspace E/TAG: map: AVOCADO
01-02 17:19:18.719 23434-23434/com.example.workingspace E/TAG: forEach: 17
01-02 17:19:18.719 23434-23434/com.example.workingspace E/TAG: forEach: 19
高阶函数:
高阶函数是将函数用作参数或返回值的函数。
一个不错的示例是集合的函数式风格的
fold
, 它接受一个初始累积值与一个接合函数,并通过将当前累积值与每个集合元素连续接合起来代入累积值来构建返回值:
class Main2Activity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main2) Higher("测试", combine = { Log.e("TAG","it=="+it) }) } fun Higher( initial: String, combine: (acc: String) -> Unit ): String { var accumulator: String = initial combine!!.invoke(accumulator) return accumulator } }输出:
01-02 17:35:58.507 23578-23578/com.example.workingspace E/TAG: it==测试
转载:https://blog.csdn.net/qq_36333309/article/details/112323265