Kotlin基础
一种在Java虚拟机上运行的静态类型编程语言
可以和java代码相互与运作
容易在Android项目中替代Java或者同Java一起使用
*kt
会被Kotlin编译器编程编译成.class的字节码文件,然后被归档成.jar,最后呢由各平台打包工具输出最终的原因程序
上图不难理解*kt
最终会被编译成Java的字节码文件,那为什么在最后一步还需要一个Kotlin运行时呢?
这是因为,我们用Java来写的程序所有的实现都会有标准的Java类库来做支撑,比如:java.lang.*, java.util.*,但Kotlin中的类库是不在标准的Java类库中的,所以,Kotlin应用程序需要在最后一步借助Kotlin运行时来支撑这些Java标准类库没有的实现。
数据类型
Kotlin 的基本数值类型包括 Byte、Short、Int、Long、Float、Double 等。不同于 Java 的是,字符不属于数值类型,是一个独立的数据类型。
1 | fun baseType() { |
数组
数组的创建方式:
1 | fun arrayType() { |
数组的遍历方式:
1 | fun arrayFor() { |
集合
Kotlin 标准库提供了一整套用于管理集合的工具,集合是可变数量(可能为零)的一组条目,各种集合对于解决问题都具有重要意义,并且经常用到。
List 是一个有序集合,可通过索引(反映元素位置的整数)访问元素。元素可以在 list 中出现多次。列表的一个示例是一句话:有一组字、这些字的顺序很重要并且字可以重复。
Set 是唯一元素的集合。它反映了集合(set)的数学抽象:一组无重复的对象。一般来说 set 中元素的顺序并不重要。例如,字母表是字母的集合(set)。
Map(或者字典)是一组键值对。键是唯一的,每个键都刚好映射到一个值,值可以重复。
我们需要注意到,集合分为可变集合和不可变集合两种方式
而数组则是可变的数组,我们可以看到Array
中
1 | public class Array<T> { |
这里我们看下集合的排序Api
1 | fun listMap() { |
方法
在Java中对象是一等公民,而在Kotlin中方法是一等公民
所有的方法是可以直接定义在文件里面的,而java中方法必须定义类中
这也说明了Kotlin是以方法为一等的
方法声明
主要看下有哪些方法
成员方法
1 | fun main() { |
类方法
也就是我们在java中常说的静态方法
在kotlin中可以有几种方式实现
- companion object 实现的类方法
1 | fun main() { |
- 静态类
1 | // 工具类的类方法 |
全局静态
也就是Kotlin文件中定义的一些方法,它们可以在任何地方被调用
单表达式方法
也就是方法返回的是单个的表达式,可以省略花括号并且在=
号后指定代码体
1 | fun double(x: Int): Int = x * 2 |
方法参数
主要有具体参数,默认参数,可变数量的参数
具体参数就是平常的那种
默认参数指的是参数可以有默认值
1 | // 可以看到off的数值为0 len的数值为数组大小 |
补充知识,在方法中最后一个参数是Lambda表达式的话,表达式可以在括号外传入。
可变数量的参数(Varargs
)
方法的参数(通常是最后一个)可以用 vararg
修饰符标记:
1 | fun append(vararg str: Char): String { |
可变参数的要求:
只有一个参数可以标注为 vararg
;
如果 vararg
参数不是列表中的最后一个参数, 可以使用具名参数语法传递其后的参数的值,或者,如果参数具有方法类型,则通过在括号外部传一个 Lambda。
当我们调用 vararg
方法时,我们可以一个接一个地传参,例如 append('h', 'e', 'l', 'l', 'o')
,或者,如果我们已经有一个数组并希望将其内容传给该方法,我们使用伸展(spread)操作符(在数组前面加 *):
1 | val world = charArrayOf('w', 'o', 'r', 'l', 'd') |
方法作用域
方法作用域为文件顶层声明,局部方法
我们主要看下没见过的局部方法
1 | fun magic(): Int{ |
方法进阶
高阶方法
高阶函数就是将函数作为参数或返回值的函数。Kotlin
支持高阶函数,这是Kotlin
函数式编程的一大特性。
一般有函数作为参数和函数作为返回值两种方式
作为参数比较简单
1 | fun List<Int>.sum(callback: (Int) -> Unit): Int { |
作为返回值比较不好明白
1 | // 需求:实现一个能够对集合元素进行求和的高阶函数,并且返回一个 声明为(scale: Int) -> Float的函数 |
闭包
方法与闭包的特性可以说是Kotlin语言最大的特性了
闭包可以简单理解为能够读取其他方法内部变量的方法。例如在JavaScript中,只有方法内部的子方法才能读取局部变量,所以闭包可以理解成“定义在一个方法内部的方法“。在本质上,闭包是将方法内部和方法外部连接起来的桥梁。
闭包的特性:
- 方法可以作为另一个方法的返回值或者参数,还可以作为一个变量的值
- 方法可以嵌套定义,即在一个方法内部可以定义另一个方法
闭包的好处:
- 加强模块化:闭包有益于模块化编程,它能以简单的方式开发较小的模块,提高开发速度和程序的可复用性
- 抽象:闭包是数据和行为的组合,这可以使得闭包具有较好的抽象能力
- 灵活:闭包的应用使得编程更加灵活
- 简化代码
1 | //需求:实现一个接受一个testClosure方法,该方法要接受一个Int类型的v1参数, |
解构声明
指的是把对象解构成很多的变量
1 | var result = Result("success", 0) |
很清晰的语法方式
匿名方法
就是没有方法名的方法
1 | //这种就是匿名方法 |
我们在闭包的例子中返回的方法就是匿名方法
这就是一个简易的语法罢了
方法的字面值
我认为这个就是变量可以是一个方法的官方解释
1 | //定义了一个变量 tmp,而该变量的类型就是 (Int) -> Boolean |
看到tmp
变量,他的变量类型是什么?是方法!
类与接口
构造方法
在Kotlin中一个类可以有一个主构造方法和多个次构造方法
我们先看主构造方法
主构造方法是类头的一部分,跟在类名后面。
1 | class KotlinClass constructor(name: String) { |
如果主构造方法没有任何注解或者可见性修饰符可以省去
constructor
1 | class KotlinClass (name: String) { |
主构造方法不能包含任何代码,初始化代码可以放到init关键字的初始化块中。
初始化块的顺序按照在类体中的顺序执行,和属于初始化器交织在一起
声明属性的构造方法
1 | //构造方法的参数作为类的属性并赋值, |
次构造方法
我们可以在类体内通过constructor
声明类的次构造方法
1 | class KotlinClass constructor(name: String) { |
类有主构方法的时候,每个次构造方法都要委托给主构造方法处理
初始化代码块中的代码是主构造方法的一部分,所以初始化代码会在次构造方法执行前执行【init
代码块】
继承与覆盖
和Java不同,Kotlin中所有类都默认为final
,如果他需要被继承,我们需要使用open
声明
1 | // 打开继承 |
在Kotlin中所有类都有共同的超类
Any
,Any
有三个方法equals()
,hashCode()
,toString()
在Kotlin中继承用:如需继承一个类,请在类头中把超类放到冒号之后:
1 | //派生类有柱构造方法的情况 |
如果派生类有一个主构造方法,其基类必须用派生类主构造方法的参数初始化。
如果派生类没有主构造方法,那么每个次构造方法必须使用 super
关键字初始化其基类型。
1 | //派生类无柱构造方法的情况 |
覆盖规则
主要是两种,覆盖方法和覆盖属性
1 | // 打开继承 |
也就是说,无论是属性还是方法都是在类中不允许被覆盖的
必须显式的声明这些可以被覆盖,子类中也必须说明我们覆盖了这些
属性
属性的声明
Kotlin类的属性可以用关键字var声明可变,也可以用关键字val声明为可读的
Getters
与Setters
声明一个属性的完整语法是
1 | var <propertyName>[: <PropertyType>] [= <property_initializer>] |
其中初始器,getter
,setter
都是可选的。
如果属性类型可以从初始器(或者getter
)中推断出来,可以省略属性类型
1 | // 继承 |
我们需要知道getter
和setter
的特点
定义了
getter
,每次访问属性的时候都会调用它定义了
setter
,每次赋值都会调用它
延迟初始化属性
通常属性声明为非空类型必须在构造方法中初始化。
然后这不利于依赖注入来初始化或者单元测试的setup
方法初始化
为了处理这种情况,可以使用lateinit
来延迟初始化
1 | data class Shop(val name: String, val location: String) |
在这种延迟初始化方式中,我们在未初始化的情况下去访问属性的时候会抛出异常
我们可以通过属性的if(::shop.isInitialized)
来检测
抽象类与接口
抽象类
1 | abstract class Printer { |
接口
Kotlin中的接口即可包含抽象方法的声明,也可包含实现。
与抽象类不同的是,接口无法保存状态,它可以由属性,但是必须把声明为抽象或提供访问器实现
1 | interface Study { |
数据类
数据类指的是我们只保存数据的一些类
1 | data class Address(val name: String, val number: Int) { |
数据类的要求:
主构造方法需要至少一个参数
主构造方法都需要标记为val var
对象表达式与对象声明
在Kotlin
提供对象表达式来方面我们需要对一个类轻微改动并创建它的对象,而不是为之显式声明新的子类。
对象表达式
要创建一个继承自某个类型的匿名类的对象,我们会这么写:
1 | open class Address2(name: String) { |
匿名对象可以用作只在本地和私有作用域中声明的类型
对象声明是指我们把class
变为object
,变成了对象声明
1 | object DataUtil { |
注意看,这里是我们使用object
来使得这个变为静态的
我们调用方法不用实例化
- 本文标题:kotlin
- 创建时间:2022-02-16 23:57:06
- 本文链接:2022/02/16/2022/kotlin/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!