一、kotlin 主要特征<简洁/安全>
1、和 Java 一样是一种静态类型的编程语言;
2、kotlin 不需要在源码中显示的申明每个变量的类型,变量类型可根据上下文自动判断----类型推导;
3、kotlin 对可空类型的支持:类型标记为可空在其尾部加一个问号
4、kotlin 对函数类型的支持
5、lambda 表达式
6、kotlin 中的检查和转换被组合成一次操作,一旦检查通过,无需额外转换可直接引用当前类型成员;
二、kotlin基础
1 基本要素:变量、函数和类
- 关键字 fun 声明函数
- 参数的类型写在其名称后
- 函数可定义在文件最外层,无需放在它的类中
- 数组就是类,kotlin 没有声明数组类型的特殊语法
- println 语句打印 log
- 可省略代码后的分号
1.1、函数
- 语句和表达式:kotlin 中 if 是表达式而不是语句(表达式有值),kotlin 中赋值操作反而是语句;
- 表达式函数体:由单个表达式构成,且去掉花括号和 return 语句(函数体卸载花括号中则是代码块体);
- 只有表达式体函数的返回值可以省略
1.2、变量
- kotlin 中变量声明的类型可省略,如果变量没有初始化器,则需显式指定类型;
- 可变变量 var 和不可变变量 val ,默认情况下应尽可能使用 val 关键字;
- val 引用自身不可变,但它指向的对象可能可变; var 关键字允许改变自身的值但不能改变类型;
- 字符串模板:变量名称前加上字符 $ , 表达式的话在此基础上还需将表达式用花括号括起来;
1.3 类和属性
- kotlin 中 public 默认可见可省略;
- 调用构造方法不需要关键字 new ,且可直接访问属性
- 关于自定义访问器?
- 与 Java 一样,包申明用 package 语句开头,导入类库用关键字 import;
- kotlin 不区分导入的是类还是函数,可直接导入顶层函数的名称;
- kotlin 中包层级结构不需要遵循目录层级结构
2、表示和处理选择:枚举和 when
声明枚举
- 枚举在 kotlin 用 enum class 两个关键字,Java 中只用enum关键字; kotlin中enum是一个软关键字,只有出现在 class 前才有意义。
- kotlin 语法中唯一必须使用分号的情况:枚举类中定义任何方法,要用分号把枚举常量列表和方法定义分开。
处理枚举
- kotlin 中的 when 语句对应 Java 中的 switch语句,匹配成功只有对应分支执行,亦可将多个值何明到同意分支,用逗号隔开即可。
- when 允许使用任何对象为参数,若将参数改为不带参数的格式分支条件就是任意的布尔表达式,优点是不会创建额外的对象,但是更难理解。
3、 迭代事物: while 循环和 for 循环
while/do-while 与 Java 中的用法一样
for 循环 – Kotlin中没有常规的Java for 循环,使用了 区间 的概念 for-in :
- … 运算符表示区间,区间都是闭合的,始终包含第二个值;
- 带步长区间:step (降序:downTo, 其他如:until);
- 运算符 !in ;
4、kotlin 中的异常
kotlin 中的异常处理和 Java 相似(try - catch - finally), 除了 Kotlin不要求声明函数可以抛出异常。
- kotlin 中throw 结构是个表达式,能作为另一个表达式的一部分使用;
- kotlin 不区分受检异常和未受检异常, 异常可处理可不处理;
三、函数的定义和调用
1、 kotlin 没有定义自己的集合类,是在 Java 集合类的基础上提供了更丰富的API
val set = hashSetOf(1, 7, 53)// HashSet
val list = arrayListOf(1, 7, 53)//ArrayList
// to是一个普通函数
val map = hashMapOf(1 to "one", 7 to "seven", 53 to "fifty-three")//HashMap
println(set.javaClass)//javaClass 等价于 Java的getClass
val listStrings = listOf("first", "second", "fourteeth")
val numbers = setOf(1, 14, 2)
// kotlin 扩展函数
println(listStrings.last())//fourteenth
println(numbers.max())//14
2、 函数的参数和属性
2.1 命名参数
- 调用kotlin 定义的函数时,可显示标明参数名称(Java 中一般通过注释指明参数名称);
- kotlin 中指明了一个参数名称,为避免混淆,其之后所有参数都要标名称;
private fun login(username: String, password: String) {}
// 调用
val username ="";
val password ="";
login(username=username, password=password)
2.2 默认参数值
- kotlin 中声明函数时,指定参数的默认值,可避免创建重载的函数;
- 有默认参数的函数可以用所有参数调用,也可省略部分参数;
- 按常规调用时需按顺序传参,省略的只能时排在末尾的参数;若用命名参数则可省略中间的参数,并可按任意顺序传需要的参数。
- 因 Java 没有参数默认值的概念,所有 Java 调用 Kotlin 函数时,必须显式指定所有参数值;或者用 @JvmOverloads 注解函数,这个指示编译器生成 Java 重载函数,从最后一个开始省略每个参数。
// 带默认参数值的函数
private fun login(username: String="name", password: String="123456") {}
// 常规调用
login()// name , 123456
login("othername")// othername , 123456
login("othername", "12345678")//othername , 12345678
// 命名参数调用
login(password = "12345678")//name , 12345678
3、 顶层函数和属性
关于顶层的含义:函数和属性都可以直接在文件中声明,而不仅仅是在类中作为成员(比 Java 更灵活的代码结构)。
3.1 顶层函数
- 把函数直接放到代码文件的顶层,不用从属于任何类,若需从包外访问它直接 import 导入,不在需要额外包一层类名;
- 若Java调用顶层函数时,因编译 JVM 只能执行类中的代码,那kotlin 中是怎么实现编译的呢?
==》Kotlin 编译生成的类名对应包含函数文件的名称(如文件名为method.kt, 对应编译生成的类名为 MethodKt ),文件中所有顶层函数会被编译成这个类的静态函数( MethodKt.topMethod() )
- 修改 Kotlin 顶层函数生成的类的名称,给文件添加 @JvmName 注解,放置报名前面(调用函数通过MethodRename.topMethod() );
@file:JvmName("MethodRename")//注解制定包名
package com.example.practicedemo.kotlindemo.test
// 顶层函数
fun topMethod(){}
class Method {
}
3.2 顶层属性
- 和函数一样,顶层属性也是放到文件的顶层,在一个类的外面保存单独的数据片段不常用;
- 可用顶层属性定义常量,若用 const 修饰(适用于所有基本数据类型及 String 类型)则等同于 Java 中的 static final;
4、 扩展函数和属性
kotlin 可以用扩展函数和属性扩展任何类的API, 包括在外部库中定义的类,无需修改源码,亦无运行时开销。
4.1 扩展函数(实质上是静态函数)
- 接收者类型是由扩展函数定义,接收者对象是该类型的一个实例(接收者类型:要扩展的类或者接口的名称放到即将添加的函数前,对应类的名称则是接收者类型;接收者对象:用来调用这个扩展函数的对象称之为接收者对象。)
- 与类内部定义的方法不同的是扩展函数不能访问私有(private 修饰)或受保护成员(protected 修饰),但对于调用者来说,扩展函数和成员函数没有区别;
- 导入扩展函数:可用导入类一样的语法导入单个函数,也可用 * 导入, 还可用关键字 as 修改导入的类或函数名称(导入申明时,关键字 as 是解决命名冲突的唯一方式)
- 扩展函数不能重写,因为 kotlin 把它当作静态函数对待;
- 如果一个类的成员函数和扩展函数有相同签名,成员函数会被优先使用。
4.2 扩展属性
声明扩展属性
声明可变扩展属性
5、 处理集合的方式
- 可变参数的关键字 vararg ,可用来声明一个函数将可能由任意数量的参数;
- 中缀表示法(键值对处理):调用只有一个参数的函数时使代码更简洁;
- 解构声明(键值对处理):用来把单独的组合值展开到多个变量中;
5.1 中缀调用
val map = hashMapOf(1 to "one", 7 to "seven", 53 to "fifty-three")
- 单词 to 是一种特殊的函数调用,称之为中缀调用
- 其中没有额外的分隔符,函数名称是直接放在目标对象名称和参数之间的;
- 要允许使用中缀符号调用函数,需要使用 infix 修饰符标记
5.2 解构声明
?
6、 字符串和正则表达式的处理
分割字符串
正则表达式和三重隐含的字符串
多行三重引号的字符串
7、 局部函数和扩展:保持代码整洁的同时避免重复
提取局部函数避免重复
在局部函数中访问外层函数的参数
提取逻辑到扩展函数
四、类、对象和接口
Kotlin 的声明默认是 final 和 public 的;嵌套的类默认并不是内部类,其没有包含对外部类的隐式引用。
1、类和接口
1.1 kotlin 中的接口
- 使用关键字 interface 声明接口;
- kotlin 类名后用 冒号 代替 Java 中的 extends 和 implements 关键字,且和 Java
一样,一个类可实现多个接口但只能继承一个类;
- kotlin 中接口的方法可以有一个默认实现且不需任何特殊注解(Java8 中需在此实现上标注 default关键字),若实现此接口,对带默认实现的方法可重新定义亦可沿用,而普通方法声明需提供实现,且使用 override 修饰符——强制要求(作用与 Java 中的@Override 注解类似);
- 若在一个类中实现两个接口,且两接口都包含带默认实现的同名方法,将会使用哪个实现?
== 》答案是任何一个都不会使用,若未显示实现此方法,会编译失败,此时会强制要求复写自己的实现。
- kotlin 中调用一个继承的实现使用的是与 Java 相同的关键字 super, 但实现语法却不同,Java 中若是Clickable.super.show(),kotlin 中则要将基类名字放在尖括号中 super< Clickable>.show();
1.2 控制继承修饰符:open,final,abstract
- kotlin 中的类和方法默认都是 final 的,若想允许创建一个类的子类,需要使用 open
修饰符标识此类,且给每个可被重写的属性和方法也添加 open 修饰符;
- 若重写了一个类或接口成员,重写的同样默认是 open 的,若要组织此类的子类重写此类的实现,可显示的将重写的成员标注为 final;
- 声明为 abstract 的类不能被实例化,必须在子类重写抽象成员,所以抽象成员始终默认是 open 的,不许显示使用 open 修饰符;
以下通过表格的方式对修饰符整理做个总结:
修饰符 |
使用 |
final |
不能被重写,类中成员默认使用 |
open |
可被重写,需明确标识 |
abstract |
必须被重写 |
override |
重写父类或接口中的成员,若没有使用 final 标明,重写的成员默认是 open的 |
1.3 可见性修饰符:public、internal、protected、private
修饰符 |
类成员 |
public(默认) |
所有地方可见 |
internal |
模块中可见? |
protected |
子类中可见 |
private |
类中可见 |
通用规则:类的基础类型和类型参数列表中用到的所有类,或者函数的签名都有与这个类或者函数本身相同的可见性。
1.4 内部类和嵌套类——默认是嵌套类
- kotlin 中没有显示修饰符的嵌套类与 Java 中的 static 嵌套类是一样的,要变成内部类需要使用 inner 修饰符;
- 嵌套类不持有外部类的引用,而内部类持有;
- 引用外部类的实例需用 this@Outer 从 Inner 类去访问 Outer 类;
1.5 密封类——sealed 修饰符
- when 表达式处理所有 sealed 类的子类时不再需要提供默认分支;
- sealed 修饰符隐含的是个 open 类,不需要显示添加 open 修饰符;
- sealed 的所有子类必须是嵌套的,且子类不能创建为 data 类;
2、非默认属性和构造方法
Java 类中可以生命一个或多个构造方法,kotlin 中也一样,只不过其区分了主构造方法和从构造方法以及初始化语句块(init 关键字)。
2.1 主构造方法——被括号围起来的语句块
- 初始化语句块是和主构造函数一起使用,因主构造函数有语法限制,所以引入了初始化语句块;
- 若主构造函数没有注解或可见性修饰符,可以省略 constructor 关键字;
- 主构造方法初始化父类可通过在基类列表的父类引用中提供父类构造方法参数(与接口的区别:接口五构造方法,故在实现接口时不需要再父类型列表中的名称后加括号)
2.2 非默认构造方法——从构造方法
- 不要声明多个从构造方法来重载和提供参数的默认值,应直接标明默认值;
- 如果类没有主构造方法,那么每个从构造方法必须初始化基类或者委托给另一个这样做的构造方法;
2.3 非默认属性——实现接口中声明的属性
- 使用 field 标识符在访问其方法体中引用属性的支持字段;
- 访问器的可见性默认与属性的可见性相同;
3、数据类和类委托
- 数据类提供了编译生成 equals、hashCode、toString、copy等方法;
- 实现接口时,使用 by 关键字将接口的实现委托到另一个对象,类中所有的方法实现都消失,编译器会生成他们。
4、object 关键字
object 使用的不同场景:
- 对象声明是定义单例的一种方式;
- 伴生对象(companion 关键字)可以持有工厂方法和其他与这个类相关,但调用时并不依赖类实例的方法
- 对象表达式用来替代 Java 的匿名内部类
五、Lambda编程
1、Lambda 表达式和成员引用
2、集合的函数式API
3、惰性集合操作:序列
4、使用 Java 函数式接口
5、带接收者的lambda : with 和 apply
六、kotlin的类型系统
七、运行符重载及其他约定
八、高阶函数:Lambda作为形参和返回值
九、泛型
十、注解和反射
十一、DSL构建