文章目录- 一. Kotlin 基础知识
- 1.1 函数结构:
- 1.2 变量
- 1.3 when、循环语句
- 1.3.1 when
- 1.3.2 循环语句
- 1.4 Kotlin 异常处理
- 1.5 "?" 和 "!!"
- 1.6 重载调用函数
- 1.7 顶级函数和属性(静态的)
- 1.8 可变参数 和 展开操作符
- 1.9 中缀调用infix
- 1.10 本地函数
- 1.11 访问修饰符
- 1.1 函数结构:
- 1.2 变量
- 1.3 when、循环语句
- 1.3.1 when
- 1.3.2 循环语句
- 1.4 Kotlin 异常处理
- 1.5 "?" 和 "!!"
- 1.6 重载调用函数
- 1.7 顶级函数和属性(静态的)
- 1.8 可变参数 和 展开操作符
- 1.9 中缀调用infix
- 1.10 本地函数
- 1.11 访问修饰符
一. Kotlin 基础知识
1.1 函数结构:
区块式体:
代码语言:javascript复制fun max(a: Int, b: Int): Int {
return if (a > b) a else b
}
上式可以写成如下表达式体(Kotlin 会通过类型推导来得知该表达式的类型):
代码语言:javascript复制fun max(a:Int, b:Int) = if (a > b) a else b
1.2 变量
Kotlin 定义变量的语法为: var/val name:Type
- var (variable ),可以修改
- val ( value ),不能修改
var age : Int = 17 // 定义一个可以被修改的变量
val ID : String= "1000" // 定义一个不可修改的变量
// 还可以省略变量类型,Kotlin会类型推导出变量的类型
var age = 17
val id = "1000"
注意:val 表示该变量 引用不可变,但是对象里的内容可以变
1.3 when、循环语句
1.3.1 when
在 Java 中有 switch 语句,在 Kotlin 中使用 when 来代替 switch
代码语言:javascript复制when(参数){
参数1 -> {
//...
}
参数1 -> {
//...
}}
1.3.2 循环语句
Kotlin 中的 while 和 do…while 循环和 Java 没有区别:
代码语言:javascript复制while (condition) {
//
}
do {
//
} while (condition)
for 循环和 Java 中的是区别:
代码语言:javascript复制// Java for 循环
for (int i = 0; i <= 100; i ) {
System.out.println(i);
}
// 对应 Kotlin 版本
for(i in 0..100){
println(i)
}
1)、闭区间:使用 … 操作符 表示一个区间,该区间是闭区间,包含开始和结束的元素,然后使用 in 操作符来遍历这个区间 2)、半闭区间 :即只包含头部元素,不包含尾部:until
代码语言:javascript复制for(i in 0 until 100){
println(i)
}
3)、倒序遍历:downTo(闭区间)
代码语言:javascript复制for(i in 100 downTo 0){
println(i)
}
4)、步长:遍历的时候 步长(step) 默认是 1,可以通过 step 关键字来指定步长
代码语言:javascript复制for( i in 100 downTo 0 step 2){
println(i)
}
1.4 Kotlin 异常处理
1)、throw 关键字在 Kotlin 中是 表达式,有返回值
代码语言:javascript复制val percentage = if (number in 0..100)
number
else
throw IllegalArgumentException(
"A percentage value must be between 0 and 100: $number")
2)、Kotlin 中可以不必向上抛出异常(java中必须抛出,不然编译不过)
代码语言:javascript复制fun readNumber(reader: BufferedReader): Int?{
try {
val line = reader.readLine() // throws IOException
return Integer.parseInt(line) // throws NumberFormatException
} catch (e: NumberFormatException) {
return null
} finally {
reader.close() // throws IOException
}
}
java中则:
代码语言:javascript复制int readNumber( BufferedReader reader) throws IOException {
try {
String line = reader.readLine(); // throws IOException
return Integer.parseInt(line); // throws NumberFormatException
} catch (NumberFormatException e) {
return -1;
} finally {
reader.close(); // throws IOException
}
}
1.5 “?” 和 “!!”
1. 声明对象时(包括方法的参数):
- 把"?"跟在类名后面,表示这个对象允许为null;
- 把"!!"跟在类名后面,表示这个对象不允许为null; 2. 调用对象时:
- 把"?"跟在对象后面,表示如果为null,程序就会视而不见,不会空指针。
- 把"!!"跟在对象后面,表示如果为null,那么系统会报异常。
// 这是声明一个变量,问号跟在类名后面
var room: Room? = Room()
private fun checkRoom() {
// 因为加上了问号,所以可以任意的把room变成空
room = null
// 因为在调用时加上了问号,所以程序不会抛出异常
Log.d("TAG", "-->> room name = ${room?.roomName}")
}
方法参数也和声明一个变量类似
代码语言:javascript复制fun test(){
heat(null) //编译过
heat1(null) //编译不过
}
//参数str可以传null
fun heat(str: String?): String{
return str "热"
}
//参数str 不可以传null
fun heat1(str: String): String{
return str "热"
}
代码语言:javascript复制var str: String? = "ds";
var length = str?.length;
var str2: String? = null
var length2 = str2?.length;
var str1: String = "ds";
var length1 = str1!!.length;
var str3: String? = null
var length3 = str3!!.length;
//编译后
String str = "ds";
int length = str.length();
String str2 = (String)null;
Integer length2 = null;
String str1 = "ds";
int length1 = str1.length();
String str3 = (String)null;
Intrinsics.throwNpe();
int length3 = str3.length();
- 如果不用"?"
// 这样程序就默认的给room加上了!!,从此以后room不允许为null
var room: Room = Room()
private fun checkRoom() {
// 当把null赋给room时,编译不会通过
room = null
// 并且编译器建议把对象后面的问号删除,因为这个对象永远不为空
Log.d("TAG", "-->> room name = ${room.roomName}")
}
- 定义变量时"?"的使用
val room: Room? = Room() // 先实例化一个room,并且room可以为空
val room: Room? = null // 不实例化了,开始room就是空的
val room: Room = Room() // 实例化一个room,并且room永远不能为空
val room = Room() // 和上一行代码一样,是简写语法
- 使用了 “?” 就真的不会NullPointerException了么?
val roomList: ArrayList<Room>? = null
if (roomList?.size > 0) {
Log.d("TAG", "-->> 房间数不是0")
}
编译器会告诉我们:当roomList为null的时,它的size返回就是"null",但是"null"不可以和int值比大小,所以编译器建议我们写成roomList?.size!! > 0,但这样一定会报NullPointerException。
那是不是必须得在外面套一层if(roomList != null)这种Java常见语句才能避免异常吗? 不过Kotlin不会让程序出现这种啰嗦的代码,所以里面提供了对象A ?: 对象B表达式, ?:表示的意思是,当对象A值为null的时候,那么它就会返回后面的对象B,所以可以写为:
代码语言:javascript复制 val roomList: ArrayList<Room>? = null
if (roomList?.size ?: 0 > 0) { // 这一行添加了?:
Log.d("TAG", "-->> 房间数不是0")
}
就目前为止使,用上面的?和?:基本上能避免程序中出现的所有NullPointerException。
- 如果变量可以为null(使用操作符"?"),则编译后是包装类型
//因为可以为 null,所以编译后为 Integer
var width: Int? = 10
var width: Int? = null
//编译后的代码
@Nullable
private static Integer width = 10;
@Nullable
private static Integer width;
//再来看看方法返回值为整型:
//返回值 Int 编译后变成基本类型 int
fun getAge(): Int {
return 0
}
//返回值 Int 编译后变成 Integer
fun getAge(): Int? {
return 0
}
1.6 重载调用函数
假设我们有如下的函数:
代码语言:javascript复制fun <T> joinToString(collection: Collection<T>,
separator: String,
prefix: String,
postfix: String): String
1、调用(为参数值指定参数名称):
代码语言:javascript复制joinToString(collection, separator = " ", prefix = " ", postfix = ".")
2、为函数参数指定默认值:
代码语言:javascript复制fun <T> joinToString(collection: Collection<T>,
separator: String = ", ",
prefix: String = "",
postfix: String = "" ): String
3、重载调用(有默认值的情况下才可以)
代码语言:javascript复制joinToString(list)
joinToString(list, prefix = "# ")
1.7 顶级函数和属性(静态的)
在 Java 中我们需要把函数和属性放在一个类中,在 Kotlin 中我们可以把某个函数或属性直接放到某个 Kotlin 文件中,把这样的函数或属性称之为 顶级函数或属性。
例如在 join.kt 文件中:
代码语言:javascript复制package strings
fun joinToString(...): String {
...
}
1)、但是在 Java 代码中如何调用该方法呢? 因为 JVM 虚拟机只能执行类中的代码,所以 Kotlin 会生成一个名叫 JoinKt 的类,并且顶级函数是静态的,所以可以在 Java 中这样调用顶级函数:
代码语言:javascript复制JoinKt.joinToString(...)
2)、在Kotlin中如何调用,如果在不同的包,需要把这个顶级函数导入才能调用:
代码语言:javascript复制//相当于 import strings.JoinKt.joinToString
import strings.joinToString
//相当于 import strings.JoinKt.*
import strings.*
3)、顶级属性 同样也是 static 静态的 如果使用 var 来定义会生成对应的静态setter、getter函数 如果使用 val 来定义只会生成对应的静态getter函数
4)、Kotlin文件名被修改怎么办? 如果所在的Kotlin文件名被修改,编译生成的类名也会被修改,可以通过注解的方式来固定编译生成的类名:
代码语言:javascript复制@file:JvmName("StringFunctions")
package stringsfun joinToString(...): String {
...
}
代码语言:javascript复制 调用的时候:
代码语言:javascript复制import strings.StringFunctions;
StringFunctions.joinToString(list, ", ", "", "");
1.8 可变参数 和 展开操作符
1)、可变参数,可传递任意数量参数 java中使用…来声明可变参数,如:
代码语言:javascript复制public static <T> List<T> listOf(T... items) {
System.out.println(items.getClass()); //数组类型
return Arrays.asList(items);
}
Kotlin 和 Java 不一样,Kotlin 使用 vararg 关键来定义可变参数:
代码语言:javascript复制fun <T> listOf(vararg items: T): List<T> {
println(items.javaClass) //数组类型
return Arrays.asList(*items) // * spread operator
}
2)、展开操作符 通过上面的两段代码比较我们发现:Kotlin 需要显示的将可变参数通过 * 展开,然后传递给 asList 函数。这里的 * 就是 展开操作符,在 Java 中是没有 展开操作符 的。
- 不使用展开操作符时:
val intArr: Array<Int> = arrayOf(1, 2, 3, 4)
Arrays.asList(0, intArr).run {
println("size = $size")}
//输出结果:
size = 2
- 使用展开操作符时:
val intArr: Array<Int> = arrayOf(1, 2, 3, 4)
Arrays.asList(0, *intArr).run {
println("size = $size")}
//输出结果:
size = 5
1.9 中缀调用infix
使用关键字 infix 修饰的函数都能够 中缀调用, 被关键字 infix 修饰的函数只能有一个参数。
Kotlin 中的 to 就是一个中缀函数:
代码语言:javascript复制public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
下面我们来对比下 to 函数的常规调用和中缀调用:
代码语言:javascript复制1.to("one") //普通的函数调用
1 to "one" //函数的中缀调用
除了 to 函数,还有我们介绍 循环 的时候讲到的 until、downTo、step 也是中缀函数:
代码语言:javascript复制public infix fun Int.until(to: Int): IntRange {
if (to <= Int.MIN_VALUE) return IntRange.EMPTY
return this .. (to - 1).toInt()
}
public infix fun Int.downTo(to: Int): IntProgression {
return IntProgression.fromClosedRange(this, to, -1)
}
public infix fun IntProgression.step(step: Int): IntProgression {
checkStepIsPositive(step > 0, step)
return IntProgression.fromClosedRange(first, last, if (this.step > 0) step else -step)
}
//使用示例:
for(i in 0 until 100){}
for (i in 100 downTo 0 step 2) {}
1.10 本地函数
本地函数(local function) 是在函数里面定义函数,本地函数只能在函数内部使用 什么时候使用本地函数?当一个函数里的逻辑很多重复的逻辑,可以把这些逻辑抽取到一个本地函数。
代码语言:javascript复制fun saveUser(user: User) {
if (user.name.isEmpty()) {
throw IllegalArgumentException("Cannot save user ${user.id}: Name is empty")
}
if (user.address.isEmpty()) {
throw IllegalArgumentException("Cannot save user ${user.id}: Address is empty")
}
}
这个 saveUser 函数里面有些重复逻辑,如果 name 或 address 为空都会抛出异常 可以使用本地函数优化下:
代码语言:javascript复制fun saveUser(user: User) {
fun validate(value: String, fieldName: String) {
if (value.isEmpty()) {
throw IllegalArgumentException("Can't save user ${user.id}: " "$fieldName is empty")
}
}
validate(user.name, "Name")
validate(user.address, "Address")
}
本地函数避免了模板代码的出现。如果不使用本地函数,我们需要把 validate函数 定义到外面去,但是这个函数只会被 saveUser函数 使用到,从而污染了外面的全局作用域。通过本地函数使得代码更加清晰,可读性更高。 注意:虽然 Kotlin 允许在函数内部定义函数,但是不要嵌套太深,否则会导致可读性太差
1.11 访问修饰符
- 类访问修饰符如下:
- 类成员访问修饰符:
- 只有 ‘’protected‘’ 对应的 ‘’Kotlin可访问级别‘’ 有区别:只有子类可访问
1.12 声明类的几种方式
- class className : 这个类是 public final 的
class Person
编译后:
public final class Person {
}
- class className([var/val] property : Type…)
- 1、会生成一个构造方法,参数就是括号里的那些参数
- 2、会根据括号的参数生成对应的属性
- 3、会根据 val 和 var 关键字来生成 setter、getter 方法,var 表示该属性可以修改;val 表示该属性不能被修改
class Person(val name: String) //name属性不可修改
---编译后---
public final class Person {
//1. 生成 name 属性
@NotNull
private final String name;
//2. 生成 getter 方法
//由于 name 属性不可修改,所以不提供 name 的 setter 方法
@NotNull
public final String getName() {
return this.name;
}
//3. 生成构造函数
public Person(@NotNull String name) {
Intrinsics.checkParameterIsNotNull(name, "name");
super();
this.name = name;
}
}
- data class className([var/val] property: Type)
- 新建 bean 类的时候,常常需要声明 equals、hashCode、toString 等方法,我们需要写很多代码。
- 在 Kotlin 中,只需要在声明类的时候前面加 data 关键字就可以完成这些功能。
- 注意 : 哪些属性参与 equals、hashCode、toString 方法呢?primary constructor 构造函数里的参数,都会参与 equals、hashCode、toString 方法里。
- object className 这种方法声明的类是一个单例类,以前在Java中新建一个单例类,需要写一些模板代码,在Kotlin中一行代码就可以了(类名前加上object关键字)。
- 内部类
- 在 Kotlin 中内部类默认是静态的( Java 与此相反),不持有外部类的引用:
class OuterClass {
//在 Kotlin 中内部类默认是静态的,不持有外部类的引用
class InnerStaticClass{
}
//如果要声明非静态的内部类,需要加上 inner 关键字
inner class InnerClass{
}
}
编译后代码如下:
class OuterClass {
public static final class InnerStaticClass {
}
public final class InnerClass {
}
}
1.13 静态变量和静态方法