var和val
可以理解为var声明变量,val声明常量。var是可读可修改,val只可读不可修改。
init{}类初始化块,相当于java中的静态代码块,在类被加载的时候就会执行。
空值
- kotlin 中定义方法时,默认接收的是 非 null 参数。
- 如果定义某个方法可以接收 null参数,则在声明方法参数时在参数后面加上?
fun test(str1:String?){ //String 后面的 ? 就表示该方法可以接收 null 作为参数
//DO STH
}
When表达式,类似于java中的switch:
when(变量){
分支A -> 表达式
else -> 表达式
}
带返回值的when表达式:
var result=when(变量){
分支A -> 表达式(要有返回值,最终将值赋给result)
else -> 表达式(要有返回值,最终将值赋给result)
}
for循环遍历list
/**
* for循环遍历list
*/
fun forFunction() {
val list = listOf("狗狗", "猫猫", "猪猪", "鹿鹿")
for ((a, b) in list.withIndex()) {
println(message = "TAG:$TAG 索引a:$a 值b:$b")
}
}
Map
声明格式:
var map=TreeMap<键类型,值类型>()
map[key]=value
Loop和Range
var nums1=1..100 //表示我们声明了一个闭区间数组,其中包含的数值为 1-100。 .. 表示闭区间
var nums2=1 util 100 //前闭后开区间,取值 1-99. util 表示前闭后开区间
示例:
/**
* for循环遍历list
*/
fun forFunction() {
val list = listOf("狗狗", "猫猫", "猪猪", "鹿鹿")
println(message = "TAG$TAG =========== 索引 值")
for ((a, b) in list.withIndex()) {
println(message = "TAG:$TAG 索引a:$a 值b:$b")
}
//list内容反转
list.reversed()
println(message = "TAG$TAG =========== list 反转 值")
for (s in list) {
println(message = "TAG$TAG value:$s")
}
var l = 1..10
var m = 1 until 10
println(message = "TAG$TAG =========== 闭区间 值")
for (i in l) {
println(message = TAG + i)
}
println(message = "TAG$TAG =========== 前闭后开 unit 值")
for (i in m) {
println(message = TAG + i)
}
/**
* 设置步长
*/
println(message = "TAG$TAG =========== 步长 值")
for (i in l step 2) {
println(message = TAG + i)
}
//数据反转
println(message = "TAG$TAG =========== 数据 反转 值")
val reversed = l.reversed()
for (i in reversed) {
println(message = TAG + i)
}
}
函数和函数表达式
- 函数的简化
//原函数:
fun sum(a:Int , b:Int):Int{
return a+b
}
//简化后
fun sum(a:Int , b:Int):Int=a+b
使用var声明函数
/**
* 使用var声明函数
* 函数表达式一
*/
var i = { x: Int, y: Int -> x + y }
/**
* 函数表达式二
*/
var j: (Int, Int) -> Int = { x, y -> x + y }
//调用
val i1 = i(2, 3)
println(message = "TAG$TAG 函数i的调用结果:$i1")
val j1 = j(4, 3)
println(message = "TAG$TAG 函数j的调用结果:$j1")
默认参数和具名参数
- 具有默认参数值的函数声明
val Pi=3.1415926
fun getRoundArea(PI:Float=Pi , radius:Float):Float{ //为变量PI赋予了默认值 Pi,这样,调用该方法时可以不再传递PI。但,如果我们想传入的值和默认值不一致时还是需要传入的
return PI*radius*radius
}
- 调用待用默认参数值的函数
var a=getRoundArea(3.14f,5.0f) //因为我们相传入的PI和默认值不一致,所以,需要将3.14f传入
- 具名参数的调用
所谓具名函数,就是调用函数时候指明参数给哪个变量
var a=getRoundArea(radius=5.0f) //我们需要的PI值与默认值一致,此时不需要再传入PI值。只需要通过 radius=5.0f 声明我们传入了半径值
字符串和数字的转换
var a="13"
a.toInt() //字符串转换为Int
var b=13
b.toString() //Int转换为字符串
根据用户输入的数字动态计算
fun main(array:ArrayList<String>){
println(“请输入第一个数值”)
var a=readLine() //读取键盘输入的字符串内容,并赋值给a
println(“请输入第二个数值”)
var b=readLine()
var num1=a!!.toInt() //readLine()得到的可能是null,所以此处通过 !! 断言输入不为null
var num2=b!!.toInt()
println("${num1}+${num2}=${num1+num2}")
}
异常处理
try{
//可能会出错的代码块
}catch(e:Excepiton){
//出错之后的处理逻辑
}
利用递归实现 阶乘函数
fun fact(a:Int):Int{
if(a==1){
return 1
}else{
return a*fact(a-1) //函数内调用函数本身就成为了递归
}
}
尾递归优化
- 尾递归的参详http://www.ruanyifeng.com/blog/2015/04/tail-call.html
递归本质上是一种循环操作。纯粹的函数式编程语言没有循环操作命令,所有的循环都用递归实现,这就是为什么尾递归对这些语言极其重要,循环可以用递归代替,而一旦使用递归,就最好使用尾递归。 - kotlin中尾递归关键字 tailrec
为什么需要尾递归优化
递归非常耗费内存,因为需要同时保存成千上百个调用记录,很容易发生"栈溢出"错误(stack overflow)。但 对于尾递归来说,由于只存在一个调用记录,所以永远不会发生"栈溢出"错误。
示例:
/**
* tailrec 是尾递归函数的关键字
* 尾递归函数是指,在该函数的最后一步操作中依旧是调用函数本身
* 为了实现尾递归,我们定义了该方法接收两个参数:num 是我们传入的需要计算累加值得的变量,total用来接收最终的返回值
*/
tailrec fun accumulation(num: Int, total: Int): Int {
return if (num == 1) {
total
} else {
//此时,该调用的含义是:先计算 total=num+total,然后计算 num=num-1
accumulation(num - 1, num + total)
}
}
面向对象入门——定义一个类并构建对象
//定义一个类,包含两个成员变量 height和width
class Rect(var height:Int,var width:Int)
fun main(args: Array<String>) {
var rect=Rect(5,10) //构建Rect对象,不需要new
println("矩形的宽${rect.width}高${rect.height}") //引用Rect类中成员变量
}
静态属性和动态行为/方法——为类定义成员方法
//定义一个类,包含两个成员变量 height和width.并定义一个成员方法
class Rect(var height: Int, var width: Int) {
fun getArea(a: Int, b: Int): Int = a * b
}
fun main(args: Array<String>) {
var rect = Rect(5, 10) //构建Rect对象,不需要new
println("矩形的宽${rect.width}高${rect.height}") //引用Rect类中成员变量
println("矩形的面积是${rect.getArea(rect.width, rect.height)}") //引用Rect类中成员变量
}
面向对象
案例:
/**
* 定义成员变量和成员方法
*/
class WashMachine(brand: String, size: Int) {
var isDoorOpen = false
var curMode = 0
fun openDoor() {
println("Wash 打开洗衣机的门 ")
isDoorOpen = true
}
fun closeDoor() {
println("Wash 关闭洗衣机的门 ")
isDoorOpen = false
}
/**
* 设置洗衣机的模式
*/
fun setMode(mode: Int) {
curMode = mode
when (mode) {
0 -> println("Wash 慢速模式")
1 -> println("Wash 标准模式")
2 -> println("Wash 快速模式")
else -> println("Wash 初始状态")
}
}
fun start() {
if (isDoorOpen) {
println("Wash 洗衣机的门还没有关闭,请先关闭洗衣机的门")
} else {
when (curMode) {
0 -> {
println("Wash 现在是慢速模式,发动机转速慢")
println("Wash 开始洗衣服")
}
1 -> {
println("Wash 现在是标准模式,发动机正常运行")
println("Wash 开始洗衣服")
}
2 -> {
println("Wash 现在是快速模式,发动机快速运行")
println("Wash 开始洗衣服")
}
else -> println("Wash 你还没有设置洗衣机的模式")
}
}
}
}
调用:
val washMachine = WashMachine("小天鹅", 20)
washMachine.setMode(2)
washMachine.openDoor()
washMachine.closeDoor()
washMachine.start()
面向对象——继承
- 一个对象直接使用另一个对象的属性或方法 —— 同Java
- 被继承的父类必须用 open 修饰,表示允许其他类继承该类
- 父类中的方法如果允许子类重写,也需要用 open 修饰
- 重写父类方法时需要用 override 修复重写后的方法
- 继承的格式:class 子类:父类()
父类:
open class Father { //用 open 修饰,允许被继承
var character = "性格内向"
open fun action() { //用open修饰,允许被重写
println("喜欢读书")
}
}
子类:
class Son : Father() { //继承。 Son 继承自 Father
override fun action() { //重写父类方法
//super.action()
println("儿子的性格是$character")
println("儿子不喜欢看书,但是喜欢唱歌")
}
}
抽象类及其实现
- 抽象的关键字 abstract —— 同Java
- 抽象类和方法不需要用 open 声明可以被继承/实现
抽象类:
abstract class Human (var name: String){ //定义抽象类,使用 abstract 修饰。包含成员变量name
abstract fun eat() //定义抽象方法, 使用 abstract 修饰
}
实现:
class Man(name: String) : Human(name) { //继承自Human抽象类
override fun eat() { //必须重写抽象方法
println("${name}是男人,是家中劳力,所以吃的多")
}
}
多态
- 同种功能,不同的表现形式 即为 多态
抽象类:
//定义抽象类,使用 abstract 修饰。包含成员变量name
abstract class Human(var name: String) {
//定义抽象方法, 使用 abstract 修饰
abstract fun eat()
//定义抽象方法
abstract fun pee()
}
子类Man:
class Man(name: String) : Human(name) {
override fun pee() {
println("${name}是男人,是站着尿尿的")
}
override fun eat() {
println("${name}是男人,是家中劳力,所以吃的多")
}
}
子类Woman:
class Woman(name: String) : Human(name) {
override fun eat() {
println("${name}是女人,饭量比较小")
}
override fun pee() {
println("${name}是女人,是蹲着尿尿的")
}
}
调用示例:
fun main(args: Array<String>) {
var man=Man("张三")
var woman=Woman("小花")
var list= listOf<Human>(man,woman) //定义集合
for (human in list){ //遍历集合
human.pee()
}
}
接口及其实现
- 接口--数据有进有出的交互方式、
- 接口关键字:interface —— 同Java
- 接口是事物的能力(代表某种事物的特性),抽象类是事物的本质(代表的是一类事物的共性)
- 子类实现接口时,接口名后面不需要()
定义接口IMan
interface IMan { //定义一个男人的接口
fun xiaodidi()
}
Man类实现IMan接口:
class Man(name: String) : Human(name) ,IMan{ //男人属于人,所以继承Human;男人有小弟弟,所以实现 IMan接口
override fun xiaodidi() {
println("这是重写IMan接口中的方法——男人有小弟弟")
}
override fun pee() {
println("${name}是男人,是站着尿尿的")
}
override fun eat() {
println("${name}是男人,是家中劳力,所以吃的多")
}
}
太监不能实现IMan接口:
class TaiJian(name: String) : Human(name) { //太监属于人,但是太监没有小弟弟,所以不能实现 IMan接口
override fun eat() {
println("太监也能吃饭")
}
override fun pee() {
println("太监也会尿尿")
}
}
代理和委托
- 委托,把自己不干的事情交给别人做
- 代理,做别人委托的事情
-
kotlin中接口代理关键字:by
场景说明
围裙妈妈只负责做饭,不负责洗碗
小头爸爸洗一次碗可以赚到10元
大头儿子洗一次碗可以赚到1元
小头爸爸承揽了洗碗的活,最终交给大头儿子做,中间赚了9元差价
代码实现——完全委托
- 定义洗碗的接口
interface IWashBow { //定义一个洗碗接口,包含一个洗碗方法
fun washBow()
}
- 大头儿子实现接口
class BigHeadSon:IWashBow { //被实现的接口后面不需要加()
override fun washBow() {
println("我是大头儿子,每次洗碗赚1元钱")
}
}
- 小头爸爸实现接口并委托事件给小头儿子
class SmallHeadFather:IWashBow by BigHeadSon(){ //委托关键字 by;被委托方(即代理方)如果不是单例类,则后面需要跟()
}
程序调用及输出结果
fun main(args: Array<String>) {
var father=SmallHeadFather()
father.washBow() //小头爸爸已经将洗碗的操作委托为小头儿子了,所以,此处本质是调用的小头儿子的洗碗操作
}
单例模式
-
单例关键字:object
我们在定义一个类时,使用object 替换 class 来修饰这个类,就表示,这是一个单例类
- 单例类作为代理人时,不需要()
场景说明
小头爸爸为了增进父子感情,想和小头儿子一起洗碗
小头爸爸重写接口方法 —— 未使用单例时的错误写法
class SmallHeadFather:IWashBow by BigHeadSon(){
override fun washBow() {
println("我是小头爸爸,我把洗碗事件委托给了大头儿子")
BigHeadSon().washBow() //委托方重写了事件之后,需要手动调用代理方的方法。但是,此处又通过()构建了一个小头儿子对象,已经不再是我们初始委托的那个大头儿子了。所以,此处是有问题的。
println("我是小头爸爸,大头儿子洗完碗之后,我赚了9元")
}
}
使用单例后的正确写法:
大头儿子单例类
object BigHeadSon:IWashBow { //单例关键字object,声明为单例类之后会立即在内存中创建单例对象,并一直存在
override fun washBow() {
println("我是大头儿子,每次洗碗赚1元钱")
}
}
小头爸爸委托事件给单例的大头儿子
class SmallHeadFather:IWashBow by BigHeadSon{ //被委托方(即代理方)是单例类,不需要通过()构建对象
override fun washBow() {
println("我是小头爸爸,虽然我把洗碗事件委托给了小头儿子,但是我要和他一起洗碗")
BigHeadSon.washBow() //委托方重写了事件之后,需要手动调用代理方的方法。由于 BigHeadSon是单例的,所以,这还是我们之前委托的那个儿子
println("我是小头爸爸,我和小头儿子洗完碗之后,我赚了9元")
}
}
外部调用
fun main(args: Array<String>) {
var father=SmallHeadFather()
father.washBow() //小头爸爸已经将洗碗的操作委托为小头儿子了,但因为重写了洗完事件,所以,本子是调用的父亲的洗完事件,父亲的洗完事件中有一部分是自己做的,另一部分是儿子做的
}
枚举--enum
示例代码:
enum class Week { //枚举关键字 enum
星期一, 星期二, 星期三, 星期四, 星期五, 星期六, 星期天
}
fun main(args: Array<String>) {
println(Week.星期一)
println("${Week.星期一}在Week中的索引是${Week.星期一.ordinal}")
}

印章类/密封类 (Sealed Class)
印章类/密封类 (Sealed Class)
印章类的特点
- 子类类型有限的类成为 印章类/密封类
- 印章类使用 sealed 作为修饰符
- 印章类本身没有构造方法
印章类与枚举的区别
- 都是有限的数据
- 枚举更注重具体的数据
- 印章类更注重数据的类型
印章类示例代码
场景说明
假设你家有一头公驴、一头母驴、一头公马。那么,
它们可能会生出一头小驴,
也可能会生出一头小骡子。
代码示例:
声明印章类:
sealed class Son { //使用 sealed 声明 Son 为印章类/密封类
class SmallMule() : Son() //声明小骡子 SmallMule 为 Son的子类。
class SmallDonkey() : Son() //声明小驴子 SmallDonkey 为 Son的子类
fun sayHello(son: Son) {
if (son is SmallMule) { //判断是不是XX的实例的关键字 is
println("小骡子对大家说大家好")
} else if (son is SmallDonkey) {
println("小驴子对大家说大家好")
}
}
}
调用印章类:
fun main(args: Array<String>) {
var mule = Son.SmallMule()
var donkey = Son.SmallDonkey()
var list = listOf<Son>(mule, donkey)
for (son in list) {
son.sayHello(son)
}
}

网友评论