数据类型
基本数据类型
Int、Long、Float、Double、Boolean、Char、String
基本数据类型转换:调用toInt()、toString()等带to的方法类型转换。
数组
申明:var arr:Array<Int> = arrayOf(1,2,3) 或者
var arr:IntArray= intArrayOf(1,2,3)
数组基本操作:size长度、get、set等方法
字符串:
常用方法:indexOf、subString、replace、spit(特定字符子串分割子串)
子串拼接:$变量、${变量运算}
特殊字符转义:${'***'},外层${''}为转义申明,内层***为要转义的子串
容器
kotlin三个基本容器:Set、List、Map,然后每类容器又分为只读和可变。
可变容器:MutableSet、MutableList、MutableMap,只有可变容器才可以对元素增删改操作。
初始化:以此类推
var list:List<String> = listOf("a","b")
var list:MutableList<String> = mutableListOf("a","b")
遍历
for-in循环
迭代器遍历
forEach遍历:
var desc = ""
goodsMutSet.forEach{"desc = ${desc}名称:${it}\n"}
控制语句
条件分支
简单分支:if/else
多路分支:when/else
类型判断:is
循环处理
遍历循环
for/in:
for(item in list)
for(i in list.indices)遍历list的下标
迭代:
条件循环
关键字:util、step、downTo
for(i in 11 util 66):左闭右开,值包括11、不包括66
for(i in 11..66 step 4):每次默认增加1,这里改为4
for(i in 50 downTo 7):默认递增,这里递减
上面这几个都不太好,一般用when或者do/when
跳出多重循环
continue:继续下个循环
break:跳出循环
在外层循环前加个名字outSide的标记
例如:outSide@ while(){},在内层循环里break@outSide会跳出外层循环
空安全
?(如何为空返回null)
?:(Elvis运算符,设置默认值,如果为空返回默认值)
!!(既然运算符,强行放弃非空判断)
等式判断
==,!=(结构相等) 基本数据类型判断的是值是否相等,凡事实现了equals函数的类,变量均可通过结构相等判断。
===,!==(引用相等):基本数据类型结构相等跟引用相等没区别,引用数据类型比较的是内存地址。
s和!s:判断变量是否属于某种类型
in和!in:判断数组中是否含有某个元素
函数运用
函数的基本应用
函数声明方式
输入参数的格式
输出参数的格式
输入参数的变化
默认参数:允许定义函数时直接指定输入函数的默认值
命名参数:如果不满意参数的默认值,也可在调用函数时输入新的值
可变参数:kotlin新增vararg关键字,其后面的参数是不固定的vararg args:String,kotlin会把可变参数当做一个数组。
几种特殊的函数
泛型函数
内联函数
因为类的成员函数依赖于类,只有泛型类(又称模板类) 才能拥有成员泛型函数,而普通类是不允许定义泛型函数的,否则编译器会直接报错。不过有个例外情况,如果参数类型都是继承自某种类型,那么允许在定义函数时指定从这个基类泛化开,凡是继承自该基类的子类,都可以作为输入参数进行函数调用,反之则无法调用函数。
例如fun <reified T : Number>
简化函数
可将函数当作一种特殊的变量,既然变量通过等号赋值,那么函数也允许使用等号对输出参数赋值。具体地说,如果一个函数的表达式比较简单,一两行代码就可以搞定,那么Kotlin允许使用等号代替大括号。
例如递归函数:fun factorial(n: Int) = if n else n*factorial(n-1)
4.3.4 尾递归函数
4.33小节的阶乘函数只是一个普通的递归函数,
尾递归函数
Kotlin体系还存在一种特殊的递归函数,名叫尾递归函数,它指的是函数末尾的返回值重复调用了自身函数。此时要在fun前面加上关键字tailrec,告诉编译器这是一个尾递归函数,则编译器会相应进行优化,从而提高程序性能。
比如求余弦不动点,即可通过尾递归函数来实现,下面是具体的实现代码例子:
//如果函数尾部递归调用自身,那么可加上关键字tailrec表示这是一个尾递归函数
//此时编译器会自动优化递归,即用循环方式代替递归,从而避免栈溢出的情况
//比如下面这个求余弦不动点的函数就是尾递归函数
tailrec fun findFixPoint(x: Double = 1.0): Double
= if (x == Math.cos(x)) x else findrixPoint(Math.cos(x))
高阶函数
既然可以将函数当成变量,那么也可以将函数作为另一个函数的入参,这种为高阶函数。
例如:fun <T> max(array : Array<T> , greater(T,T) -> Boolean) : T?{}
增强系统函数
扩展函数
使用Java开发时,虽然系统自带的类已经提供了许多方法,然而经常还是无法完全满足业务需求,此时开发者往往要写一个工具类 (比如字符串工具类StringUtil、日期工具类DateUil等) 来补充相关的处理功能,长此以往,工具类越来越多,也越来越难以管理。
基于以上情况,Kotlin推出了扩展函数的概念,扩展函数允许开发者给系统类补写新的方法,而无须另外编写额外的工具类。比如系统自带的数组Array提供了求最大值的max方法,也提供了进行排序的sort方法,可是并未提供交换数组元素的方法。于是我们打算给Array数组类增加新的交换方法,也就是添加一个扩展函数swap。与普通函数定义不司的是,要在swap函数名称前面加上前缀“Array<lnt>”,表示该函数扩展自系统类Array<In>。下面是用于交换数组元素的swap函数定义代码:
fun Array<Int>.swap(pos1:Int,pos2:Int){}
扩展高阶函数
日期时间函数
单利对象
Object类用来声明单例对象
类和对象
类的构造
类的简单定义
(1) Kotlin省略了关键字public,缘于它默认就是开放的。
(2) Kotlin用冒号“:”代替extends,也就是通过冒号表示继承关系
(3) Kotlin进行继承时,父类后面多了括号“()”。
创建实例时的初始化在 init{} 中
类的构造函数
Kotlin引入了主构造函数与二级构造函数的概念。 跟在类名后面的参数是主构造函数的入参,同时init方法是主构造函数的内部代码,当然了也可以不要主构造函数。至于二级构造函数,则可以在类内部直接书写完整的函数表达式。
(1)二级构造函数没有函数名称,只用关键字constructor表示这是一个构造函数。
(2) 二级构造函数需要调用主构造函数。“this(context, name)”,在Kotlin中以冒号开头补充到输入参数后面,这意味着二级构造函数实际上是从主构造函数派生而来的,也可看作二级构造函数的返回值是主构造函数。
由此看来,二级构造函数从属于主构造函数,如果使用二级构造函数声明该类的实例,系统就会先调用主构造函数的init代码,再调用二级构造函数的自身代码。
constructor(context : Context, name : String , age : Int) : this(context , name) {}
带默认参数的构造函数
如果想让java也能识别kotlin构造函数的默认参数,需要再类名后加关键字@JvmOverLoads constructor
例如:class AnimalDefault @JvmOverLoads constructor(context : Context , age : Int) {}
类的成员
成员属性
主构造函数中的参数变量前加var或者val,那么就会有同名的成员属性。
成员方法
伴生对象
kotlin中取消了关键字static,利用伴生对象实现静态成员的功能。
companion object WildAnimal{}
关键字companion表示伴随、object表示对象、WildAnimal表示伴生对象的名称
静态属性
类的继承
开放性修饰符
Kotlin默认每个类都不能被继承(相当于Java类被fnal修饰了) ,如果要让某个类成为基类,就需把该类开放出来,也就是添加关键字open作为修饰符。
public:对所有人开放。Konin 的类、函数、变量不加开放性修饰符的话,默认就是 oublic 类型
intemal:只对本模块内部开放,这是Konin 新增的关键字。对于 app开发来说,本模块是指本App
protected:只对自己和子类开放
private:只对自己开放,即私有
到底open跟这4个开放性修饰符是什么关系? 其实很简单,open不控制某个对象的访问权限,只决定该对象能否繁衍开来。只有带有open的类,才允许作为基类派生出子类来。
普通类继承
抽象类
也是abstract关键字,无法创建抽象类的实例。
因为抽象类不能直接使用,所有构造函数不必给默认参数赋值。
接口
关键字interface,接口不能定义构造函数。
与java不同的是,kotlin接口内部允许实现方法。
接口代理
Kotlin引入了接口代理的技术,即一个类先声明继承自某个接口,但并不直接实现该接口的方法,而是把已经实现该接口的代理类作为参数传给前面的类,相当于告诉前面的类:“该接口的方法我已经代替你实现了,你直接拿去用便是”。
//只有接口才能够使用关键字by进行代理操作
//如果by的对象是个类,编译器就会报错”Only interfaces can be delegated to"class
WildFowl(name:String, sex:Int=MALE behavior:Behavior) : Bird(name, sex) , Behavior by behavior {}
Behavior是一个接口,WildFowl实现Behavior接口,但是WildFowl不直接实现,而是把已经实现该接口的代理类作为参数传给前面的类。
几种特殊类
嵌套类
个类可以在单独的代码文件中定义,也可以在另一个类内部定义,后一种情况叫作嵌套类,即A类嵌套在B类之中。乍看过去,这个嵌套类的定义似乎与Java的嵌套类是一样的,但其实有所差别。Java的嵌套类允许访问外部类的成员,而Kotlin的嵌套类不允许访问外部类的成员。
调用嵌套类时,得在嵌套类的类名前添加外部类的类名
个人感觉跟静态内部类相似
内部类
既然Kotlin限制了嵌套类不能访问外部类的成员,那还有什么办法可以实现此功能呢?针对该问题,Kotlin另外增加了关键字inner表示内部,把inner加在嵌套类的class前面.于是嵌套类华丽地转变为了内部类,这个内部类比起嵌套类的好处是能够访问外部类的成员。所以,Kotlin的内部类就相当于Java的嵌套类,而Kotin的嵌套类则是加了访问限制的内部类。
枚举类
java中的枚举
enum Season ( SPRING,SUMMER,AUTUMN, WINTER)
上面的枚举类型定义代码看起来仿佛是一种新的数据类型,特别像枚举数组。可是枚举类型实际上是一种类,开发者在代码中创建enum类型时,Java编译器会自动生成一个对应的类,并且该类继承自java.ang.Enum。
Kotlin中的枚举,摒弃了“枚举类型”那种模糊不清的说法,转而采取“枚举类”这种正本清源的提法。具体到编码上,是将关键字enum作为class的修饰符,使之名正言顺地成为一种类一一枚举类。 按此思路将前面Java的枚举类型Season改写为Kotlin的枚举类,改写后的枚举类代码如下所示:
enum class SeasonType {
SPRING,SUMMER,AUTUMN, WINTER
}
枚举类内部的枚举变量除了可以直接拿来赋值之外,还可以通过枚举值的几个属性获得对应的信息,例如ordinal属性用于获取该枚举值的序号,name属性用于获取该枚举值的名称。枚举变量本质上还是该类的一个实例,所以如果枚举类存在构造函数,枚举变量也必须调用对应的构造函数。这样做的好处是,每个枚举值不但携带唯一的名称,还可以拥有更加个性化的特征描述。 比如下面的枚举类SeasonName,其代码通过构造函数能够给枚举值赋予更加丰富的含义: enum class SeasonName (val seasonName: String) ( SPRING("春天") SUMMER("夏天"), AUTUMN(“秋天"), WINTER("冬天”) 下面的代码分别演示如何使用枚举类SeasonType和SeasonName:
enum class SeasonName (val seasonMame: String) (
SPRING("春天")
SUMMER("夏天”)
AUPUMN(“秋天“),
WINTER("冬天")}
密封类
为解决枚举值判断的多余分支问题,Kotlin提出了“密封类”的概念,密封类就像是一种更加严格的枚举类,它内部有且仅有自身的实例对象,所以是一个有限的自身实例集合。定义密封类的时候,需要在该类的class前面加上关键字sealed作为标记。外部使用when语句判断密封类型值时便无须指定else分支了。
数据类
数据类说神秘也不神秘,它的类定义代码极其简单,只要开发者在class前面增加关键字“data”,并声明拥有完整输入参数的构造函数,即可无缝实现以下功能:
(1) 自动声明与构造函数入参同名的属性字段
(2)自动实现每个属性字段的get/set方法
(3)自动提供equals方法,用于比较两个数据对象是否相等。
(4)自动提供copy方法,允许完整复制某个数据对象,也可在复制后单独修改某几个字段的值。
(5) 自动提供toString方法,用于打印数据对象中保存的所有字段值。
//数据类必须有主构造函数,且至少有一个输入参数
//并且要声明与输入参数同名的属性,即输入参数前面添加关键字va1或者var
//数据类不能是基类也不能是子类,不能是抽象类,也不能是内部类,更不能是密封类
data class Plant(var name:String, var stem:String, var leaf:String, varflower:String, var fruit:String, var seed:String)
模版类
模版类就是java中的泛型类。