Kotlin零基础入门到精通(精选)
Kotlin零基础入门到精通(精选)
- 一. Kotlin课程概述
- 1.1 课程安排:
- 1.2 什么是Kotlin?
- 1.3 Kotlin的发展历程
- 1.4 学习目标
- 1.5 必备知识
- 1.6 参考资料
- 1.7 Hello,world
- 二. 数据类型
- 2.1 本章目标
- 2.2 Boolean类型
- 2.3 Number类型
- 2.4 Char类型
- 2.5 类与对象
- 2.6 空类型和智能类型转换
- 2.7 包
- 2.8 区间
- 2.9 数组
- 三. 程序结构
- 3.1 常量与变量
- 3.2 函数
- 3.3 Lambda表达式
- 3.4 类成员
- 3.5 运算符
- 3.6 表达式
- 3.7 循环
- 3.8 捕获异常
- 3.9 各种类型参数
- 3.10 导出为可执行程序
- 四. 面向对象
- 4.1 面向对象-抽象类与接口
- 4.2 继承
- 4.3 可见性
- 4.4 Object类
- 4.5 伴生对象与静态成员
- 4.5 方法重载与默认
- 4.6 扩展成员
- 4.7 属性代理
- 4.8 数据类
- 4.9 内部类与匿名内部类
- 4.10 枚举
- 4.11 密封类
- 五. 高阶函数
- 5.1 高阶函数的基本概念
- 5.2 常用高阶函数
- 5.3 尾递归优化
- 5.4 闭包
- 5.5 中缀表达式
- 5.6 Currying 科理化
- 5.7 偏函数
- 5.8 小案例: 统计字符串个数
- 六. 领域特定语言: DSL
- 6.1 DSL的特点
- 6.2 HTML DSL
- 6.3 Gradle Kotlin脚本编写
- 七、协程
- 7.1 协程的基本概念
- 7.2 了解协程
- 7.3 kotlin.coroutine框架介绍
- 八. Kotlin与Java混合开发
- 8.1 基本互操作
- 8.2 SAM转换
- 8.3 正则表达式
- 8.4 集合框架
- 8.5 IO操作
- 8.6 拆箱与装箱
- 8.7 注解处理器
- 九. Kotlin的未来与展望
- 9.1 Kotlin的应用场景
- 9.2 Kotlin-Script脚本编写
- 9.3 创建SpringBoot项目
- 9.4 Kotlin-Natie项目开发
- 9.5 结束
一. Kotlin课程概述
1.1 课程安排:
- 课程介绍、Kotlin介绍、开发环境搭建
- 基本语法:基本类型、空安全类型、智能转换、类与对象同步、数组与区间
- 程序结构:常量与变量、函数、Lambda、类成员、条件表达式、循环语句、运算符、异常捕获
- 面向对象:抽象类和接口、Object、伴生对象、扩展方法、属性代理、数据类、内部类、枚举与密封类
- 高阶函数:基本概念、常见高阶函数、尾递归优化、闭包、函数复合、科里化、偏函数
- DSL:基本概念、案例开发、Gradle脚本
- 协程:基本概念、协程的使用、封装协程库、协程原理分析;
- 与Java混编:基本互操作、正则表达式、集合框架、IO操作、装箱与拆箱、NoArg插件、AllOpen插件、注解处理器
- 应用与展望:前景与展望、编写脚本、服务端、前端、Android、Native
1.2 什么是Kotlin?
- Kotlin就是一门可以运行在Java虚拟机、Android、浏览器上的静态语言,它与Java 100%兼容,如果你对Java非常熟悉,那么你就会发现Kotlin除了自己的标准库之外,大多仍然使用经典的Java集合框架;
- 总结来说:
- Android官方开发语言
- 100%兼容Java
- Kotlin-Js 前端开发
- Kotlin-Jvm 服务端开发
- Kotlin-Native 本地执行程序
Kotlin 是一门全栈语言
1.3 Kotlin的发展历程
- 2010年立项
- 2011.6对外公开
- 2012.2开源
- 2013.8 支持 Android Studio
- 2014.6全新的开源web站点和域名 Kotlinlang.org
- 2016.2 发布1.0
- 2016.9 发布1.0.4, 支持apt
1.4 学习目标
- 学会使用Kotlin
- 熟悉Java生态
- 了解一些特性的背后实现
1.5 必备知识
- 熟悉计算机基础、操作系统相关的知识
- 了解Java及其生态
- 了解Java工程组织的常用工具
- 熟悉IntelliJ Idea
1.6 参考资料
- 官方文档:https://kotlinlang.org/docs/reference
- Kotlin源码:https://github.com/JetBrains/kotlin
- Kotlin官博:https://blog.jetbrains.com/kotlin/
- Kotlin微信公众号:Kotlin
1.7 Hello,world
安装Kotlin插件,如图所示:
安装后要重启一次才能生效;
创建一个Kotlin工程,如图所示:
3. 根据实际,填写项目的groupId,ArtifactId,以及Version,如图所示:
一路默认下去即可,然后成功创建项目,然后在此项目中创建Kotlin包,如图所示:
紧接着创建包net.println.kotlin(根据实际来,此处可不一致),然后创建HelloWorld.kt,如图所示:
编写Hello,world代码如下:
fun main(args:Array<String>){println("Hello World") }
执行,如图所示:
执行成功后,控制台会打印Hello,world字样,说明运行成功,如图所示:
点击println进去,可以看到源码,其打印操作是调用的Java的System.out.println,如图所示:
二. 数据类型
2.1 本章目标
- 认识基本类型
- 初步认识类及其相关概念
- 认识区间和数组
简单来说就是看懂如图例子:
2.2 Boolean类型
- Boolean 值 只有true或者false两个值,它无处不在,相当于Java类型的boolean
- 示例代码:
val aBoolean : Boolean = true val anotherBoolean : Boolean = false
> var是可变变量(可读可写),val是可读变量(只能读);同时 : 后面的Boolean 是指它的类型,前面的aBoolean或anotherBoolean是它的变量名称,=号后面的true或false是它的值;
2.3 Number类型
数字类型如下:
int,Long类型的最大值和最小值
// 2147483647 val maxInt: Int= Int.MAX_VALUE // -2147483648 val mintInt: Int=Int.MIN_VALUE val maxLong : Long=Long.MAX_VALUE val minLong: Long=Long.MIN_VALUE val aFolat: Float=20F val maxFolat: Float=Float.MAX_VALUE val minFolat: Float=- Float.MAX_VALUE val maxShort : Short=Short.MAX_VALUE val minShort : Short=Short.MIN_VALUE val maxByte: Byte= Byte.MAX_VALUE val minByte : Byte= Byte.MIN_VALUE
val后面的为变量名,可替换为实际名称;Long类型的长整形后面可以加个L; Float类型后面必须加F,f;Float是浮点类型,有精度问题,计算钱相关的不要用这个;
装箱和拆箱
- 在Kotlin中不区分装箱和拆箱;
2.4 Char类型
特点:
- 字符对应Java的Character
- 占两个字节,表示一个16位的Unicode字符
- 字符用单引号’‘引起来,例如: ‘a’,‘0’,’\n’
Char类型转义字符如图:
不可隐式转换
- 在java中,一个int类型与Long相加,原本的Int类型会自动隐式转换为Long类型,这在Kotlin中是不允许的;
比较相等
- ==表示equals 值得相等比较
- === 三个等号表示引用地址的比较,即比较两个值是否是同一个引用地址;
字符串模板
- 代码如图:
val arg1: Int =0 val arg2: Int =1 // java款式的加法 println(""+arg1+"+"arg2+"="+(arg1+arg2)) // kotlin款式的加法 println("$arg1+ $arg2=${arg1+arg2}") // 如果要打印美元 $这个符号,则再加一个$ val salary: Int = 1000 println("$"+"$salary") // 或者使用转义符号 println("\$salary") // 三个引号,转义会失效,支持换行 val txt:String =""" hello , world """ // 打印它的字符数量 println(rawString.length)
- 代码如图:
2.5 类与对象
什么是类?
- 类,一个抽象的概念
- 具有某些特征的事物的概括
- 不特定指代任何一个具体的事物
- 举例:
- 人、车、书
- 写法:
class<类名>{<成员>}
什么是对象?
- 是一个具体的概念,与类相对
- 描述某一种类的具体个体
- 举例:
- 某些人、领导的车、你手里的那本书
类和对象的关系?
- 一个类通常可以有很多个具体的对象
- 一个对象本质上只能从属于一个类
- 某一个人,他是工程师,但本质上还是属于人这一类
对象也经常被称作“类的对象”或者“类的实例”
- 比如 类: 城市 --> 上海、深圳(对象)
类的继承
- 提取多个类的共性得到一个更抽象的类,即父类;
- 子类拥有父类的一切特征
- 子类也可以自定义自己的特征
- 所有类都最终继承自Any
类与对象的实例图示:
如果在类中加了init字段,则在创建过程中会自动执行其中的方法,如图所示:
当构造只有一个的时候可以进行省略,如果有多个则不可以,如图所示:
子类继承了父类中的一些方法,如图所示:
Any是一切类的父类,它拥有equals,hashCode,toString方法,则说明在Kotlin中的其他所有类,都拥有这些方法,如图所示:
2.6 空类型和智能类型转换
- 任意类型都有可空和不可空两种
- val notNull:String = null // 错误,不能为空。 如果为空会抛出异常
- val nullable:String ?= null // 正确,可以为空。 如果为空,则被赋值的nullable的值为null
- notNull.length // 正确,不为空的值可以直接使用
- nullable.length // 错误,可能为空,不能直接获取长度
- nullable!!.length // 正确,强制认定nullable不可空(如果在这段代码前进行了if判断,比如不为空的时候才执行的这段代码,就没有问题。我们已经认定了这个nullable变量不为空)
- nullable?.length // 正确,若nullable为空,返回空
- Java Style类型转换
val sub: SubClass = parent as SubClass
- 类似于Java 的类型汉族那换,失败则抛出异常;
这里的含义是,判断parent变量为SubClass的子类,若为其子类,则sub是SubClass类型,如不为则直接抛出异常;
- 安全类型转换
- val sub: SubClass? = parent as? SubClass
- 如果转换失败,返回null,不抛异常
> 如果parent不是SubClass的子类,则类型转换失败,sub不能编程SubClass类型,则sub的值为null;
2.7 包
- 概述:
- 包就是命名空间
- 包的声明必须在非注释代码的第一行
- 类的全名:
- net.println.kotlin,chapter2.HelloWorld
- 包即类的全名,import字段进行导入,import同时可以对此包进行命名,可以以另外一个名称进行替代和调用。如图所示:
package net.println.koltin.DemoTest import net.println.kotlin.HelloWord as Hellofun main(args:Array<String>){val sayHello:Hello=Hello(); }
这里导入的Hello,就是HelloWorld类。通过创建Hello,就相当于创建了一个HelloWord,所以sayHello看似是对Hello实例化,实际上是对HelloWorld进行了实例化;
2.8 区间
- 概述:
- 一个数学上的概念,表示范围
- ClosedRange的子类,IntRange最常用
- 基本写法:
- 0…100表示[0,100]
- 0 until 100 表示 [0,100)
- i in 0…100判断 i 是否在区间 [0,100]中
- 示例代码:
// 前后都闭区间 [0,1024] val range:IntRange=0..1024 // 前闭后开的区间 [0,1024) = [0,1023] val range_exclusive:IntRange= 0 until 1024 val empty_Range:IntRange=0..-1fun main(args:Array<String>){// true 是为空,因为此范围中没有值println(emptyRange.isEmpty())// true 此范围中包含了50println(range.contains(50))// true 检查50是否在range的范围中println(50 in range) }// 下面打印出来的结果为: 0,1,2,3,4,5,6,7,8,9,10... for(i in range_exclusive){println("$i,") }
用i in IntRange可以作范围判断,以及辅助遍历等操作
2.9 数组
数组是什么?
- 对应英文单词Array:
An impressive display or range of a particular type of thing or an ordered arrangement,in particular
- 跟数一点关系没有
- 就是一系列对象,这个对象可以是各类型的数字,字符,字符串,或者自定义对象等;
- 对应英文单词Array:
基本写法
- val array:Array arrayOf(…)
基本操作:
- println array[i] 输出第i个成员
- array[i] 指定数组的第i个成员值;我们可以通过此进行赋值或者获取值
- array.length 数组的长度
为了避免不必要的装箱和拆箱,基本类型的数组是定制的,如图所示:
数组示例代码:
- 创建对象,如图所示:
- 创建示例代码如下:
// 创建一个int数组 val arrayOfint: IntArray = intArrayOf(1,3,5,7) // 创建一个Char数组 val arrayOfChar: CharArray = charArrayOf("H","e","l","o","W","o","r","l","d") // 创建一个自定义对象数组,根据上面创建的类 val arrayOf书记: Array<市委书记> = arrayOf(市委书记("张"),市委书记("赵"),市委书记("黄"))fun main(args: Array<String>){// 打印 int数组println(arrayOfInt.size)for(int in arrayOfInt){println(int)} }println(arrayOf书记[1]) // 这里将方书记赋值给了数组的1 索引处。此处1索引原来的对象会变成新替换的对象,所以重新打印1索引处的书记会变成新的方书记; arrayOf书记[1] = 市委书记("方") println(arrayOf书记[1])// 这里的joinToString("")表示每个字符之间不用什么连接。 传入制定参数就以指定值进行连接。如果不传,默认以,号连接,比如: H,e,l,l,o... 此处代码打印结果为: HelloWorld println(arrayOfChar.joinToString(""))
- 创建对象,如图所示:
三. 程序结构
3.1 常量与变量
什么是常量?
- val= value, 值类型
- 类似Java 的final
- 不可能重复复制;
- 举例:
- 运行时常量: val x=getX()
- 编译期常量(Java中的静态常量 final): const val x=2
什么是变量:
- var = variable
- 举例:
- var x =“HelloWorld” //定义变量
- x= “HiWorld” // 再次赋值
类型推导- 编译器可以推导量的类型
- val String =“Hello” // 推导出String类型
- val int =5 // Int类型
- var x =getString() + 5 // String类型
var是可变量,val 是不可变,它是常量;
val虽然是不可变,但是它不是静态的,如果需要在编译期时就加在,可以在前面加一个const字段;
3.2 函数
- 什么是函数?
- 以特定功能组织起来的代码块
- fun函数名:[返回值类型]{[函数体]}
- fun函数名=[表达式]
- 举例:
- fun sayHi(name:String){println(“Hi,$name”)}
// 简写的方式: - fun sayHi(name:String)=println(“Hi,$name”)
- fun sayHi(name:String){println(“Hi,$name”)}
- 以特定功能组织起来的代码块
- 任何函数都是以fun 开头,然后后面为它的名字,括号内为它的参数若有范围值,在后面用:Any ,Any代表返回值类型;
- 比如我们的Main函数:
fun main(args: Array<String>){println("Hello,world")// 打印args数组的索引为1 的值println(${args[0]})if (args.size !=2){} }
如果没有传参,会报数组索引异常。这里main函数没有为args赋值;如果启动时,传入了参数就可以打印了;同时如果有返回值,则需要在main(…)这个括号后面加 :返回值类型来定义返回值类型;
- 代码示例:
fun main(args: Array<String>){checkArgs(args)val arg1=args[0].toInt()val arg2=args[1].toInt()println("$arg1+$arg2=${sum(arg1,arg2)}") }fun checkArgs(args: Array<String>){if(args.size != 2){println("请传入两个整形参数,例如 1, 2")System.exit(-1)} }fun sum(arg1: Int ,arg2:Int):Int{return arg1+arg2 }
3.3 Lambda表达式
什么是lambda表达式?
- 匿名函数
- 写法:
{[参数列表]->[函数体,最后一行是返回值]}
- 举例:
val sum ={a:Int,b:Int-> a+b}
Lambda的类型表示举例:
// 无参,返回值为Unit ()->Unit // 传入整型,返回一个整型 (Int)->Int // 传入字符串、Lambda表达式,返回Boolean (String,(String)->String)->Boolean
Lambda表达式的调用
- 用()进行调用
- 等价于invoke()
- 举例:
val sum ={a:Int,b:Int->a+b} sum(2,3) sum.invoke(2,3)
Lambda表达式的简化
- 函数参数调用时最后一个Lambda可以移出去
- 函数参数只有一个Lambda,调用时小括号可省略
- Lambda只有一个参数可默认为it
- 入参,返回值与形参一致的函数可以用函数引用的方式作为实参传入;
使用for 我们用foreach也可以进行遍历;
3.4 类成员
什么是类成员?
- 属性:或者说成员变量,类范围内的变量
- 方法:或者说成员函数,类范围内的函数
函数和方法的区别?
- 函数强调功能本身,不考虑从属
- 方法的称呼通常是从类的角度出发
- 叫法不同而已,不要纠结;
函数如何定义方法?
- 写法与普通函数一致,函数如果写在类中,它就是方法
- 举例:
class Hello{fun sayHello(name:String)=println("Hello,$name") }
定义属性
- 构造方法参数中val/var的都是属性
- 类内部也可以定义属性
- 举例:
class Hello(val aField:Int,notAField:Int){val anotherFIeld:Float=3f }
属性的访问规则:
- 属性可以定义getter/setter
- 举例如下:
val a:Int=0 get()=field var b:Float=0f set(value){field=value}
get(){return field} 其中field指代了此变量
属性初始化
- 属性的初始化尽量在构造方法中完成
- 无法在构造方法中初始化,尝试降级为局部变量
- val用lateinit延迟初始化,val用lazy
- 可空类型谨慎用null直接初始化;
- 举例:
class X class A{ // 使用lateinit延迟初始化,不需要立即给值 lateinit var c: String val e: X by lazy{X() } }
3.5 运算符
- 任何类可以定义或者重载父类的基本运算符
- 通过运算符对应的具名函数来定义
- 对参数个数作要求,对参数和返回值类型不作要求
- 不能像Scala一样定义任意运算符
3.6 表达式
中缀表达式
- 只有一个参数,且用infix修饰的函数
- 举例:
class Book{infix fun on(place: String){...} } if(Book() on "My Desk"){...}
if 表达式
- 举例:
if(a==b)... else...
在if表达式中,我们既可以用来做条件判断,也可以类似java三元运算符一样直接用来当一个值进行使用。比如
val b=2 // 当b等于2时,则a等于3 否则等于5 val a= if(b==2) 3 else 5
- 表达式完备性:
- 当我们用于类似三元运算符的操作时,必须要有else
- 举例:
When 表达式
- 加强版Switch,支持任意类型
- 支持纯表达式条件分支(类似if)
- 表达式与完备性
- 举例:
fun main(args: Array<String>){val x=5when(x){is Int-> 逻辑代码...in 1..100-> 逻辑代码...!in 1..100-> 逻辑代码...args[0].toInt()-> 逻辑代码...} }
这里的when处传入值,下面任意满足条件且按先后只执行第一个符合条件的代码逻辑;
- 同时还有when不带括号的方式:
var str="今天是周末;"val biaodian = when {str.contains(";") -> {";"}str.contains(";") -> {";"}str.contains(",") -> {","}else -> {","}
含义可以参考此java代码:
var str="今天是周末;" val biaodian = if (str.contains(";")){";"}else if (str.contains(";")){";"}else if (str.contains(",")){","}else{","}
3.7 循环
for循环
- 基本写法
for(element in elements)...
- 代码示例:
fun main(args: Array<String>){// 遍历一for(arg in args){println(arg)}// 遍历二for((index,value) in args.withIndex()){println("$index-> $value")}// 遍历三for(indexedValue in args.withIndex()){println("${indexedValue.index} -> ${indexedValue.value}")} }
- 给任意类实现Iterator方法
可网上找
- 基本写法
While循环
- 古董级语法
- do … while(…)…
- while(…)…
- 代码示例:
fun main(args:Array<String>){var x=5while(x>0){println(x)x--}do {println(x)x--}while(x>0) }
跳过和终止循环
- 跳过当前循环用continue
- 终止循环用break
- 多层循环嵌套的终止结合标签使用
3.8 捕获异常
- catch 分支匹配异常类型
- 表达式,可以用来赋值
- finally: 无论代码是否抛出异常都会执行
- 注意下面的写法:
return try(x/y)catch(e:Exception){0}finally{...}
异常的捕获及处理与java类似,不过其可以作为值进行使用;
3.9 各种类型参数
具名参数
- 给函数的实参附上形参
- 举例:
fun sum(arg1:Int, arg2:Int)= arg1+arg2 sum (arg1=2 ,arg2=3)
变长参数
- 某个参数可以接收多个值
- 可以不为最后一个参数
- 如果传参时有歧义,需要使用具名参数
Spread Operator
- 只支持展开Array
- 只用于变长参数列表的实参
- 不能重载
默认参数:
- 为函数参数指定默认值
- 可以为任意位置的参数指定默认值
- 传参时,如果有歧义,需要使用具名参数
代码示例:
fun main(vararg args: String){var array= intArrayOf(1,3,4,5)// string="Hello" 是一个默认参数// *array表示将array数组展开,将一个个元素传入,如: hello(3.0,1,3,4,5,string="Hello")hello(3.0,*array,string="Hello") }fun hello(double:Double, vararg ints:Int, string:String){ints.forEach(::println)println(string) }
3.10 导出为可执行程序
- 在可执行的类上加上如下字段:
apply plugin:'application'
mainClassName="net.println.kotlin.chapter3.CalcKt"
然后gradle会下载一些相关插件,完成后会在Gradle的窗口中,在Tasks-> distribution-> InstallDist 中执行InstallDist. 如图所示:
- 会各生成一个windows和Linux下的脚本,如图所示:
四. 面向对象
4.1 面向对象-抽象类与接口
面向对象的基本概念
- 本质上就是解决如何用程序描述世界的问题
- 讨论如何把实际存在的东西映射成程序的类和对象
- 一种程序设计的思路、思想、方法
类实例:
// 定义一个类 class Demo{// 定义一个可读变量val i=4// 定义一个方法fun out(i: int){println(i)} }// 定义一个接口 interface chouxianglei{// 定义一个接口方法fun hello() }
继承一个接口的时候,使用类名(),实现一个接口的时候,使用类名即可,在Kotlin 中是单继承,多实现;如图所示:
什么是接口?
- 接口,直观理解就是一种约定。 Kotlin的接口与Object-C的Protocol比较类似
- 举例,输入设备接口:
interface InputDevice{fun input(event: Any) }
接口与抽象类的区别:
- 接口:
- 不能有状态
- 必须由类对其进行实现后使用
- 抽象类:
- 实现了一部分协议的半成品
- 可以有状态,可以有方法实现
- 必须由子类继承后使用
- 共性:
- 比较抽象,不能直接实例化
- 有需要子类(实现类)实现的方法
- 父类(接口)变量可以接受子类(实现类)的实例赋值
- 接口:
4.2 继承
继承(实现)语法要点
- 父类需要open才可以被继承
- 父类方法、属性需要open才可以被覆写
- 接口、接口方法、抽象类默认为open
- 覆写父类(接口)成员需要override关键字
Class D: A(),B,C
- 注意继承类时实际上调用了父类的构造方法
- 类只能单继承,接口可以多实现
class Manager(driver: Driver): Driver by driver
- 接口方法实现交给代理类实现
接口方法冲突
- 接口方法可以有默认实现
- 签名一致且返回值相同的冲突
- 子类(实现类)必须覆写冲突方法
4.3 可见性
- 可见性Java与Kotlin对比,如图所示:
4.4 Object类
- 特点:
- 只有一个实例的类
- 不能自定义构造方法
- 可以实现接口、继承父类
- 本质上就是单例模式最基本的实现
使用object类可以创建一个最简单的单例类
4.5 伴生对象与静态成员
- 伴生对象的特点:
- 每个类可以对应一个伴生对象
- 伴生对象的成员全局独一份
- 伴生对象的成员类似Java的静态成员
> 伴生对象就相当于静态变量和静态方法的整体;
- 使用伴生对象需要注意的地方:
- 静态成员考虑用包级函数、变量替代
- @JvmField 和 @JvmStatic的使用
- 示例代码:
class Latitude private constructor(val value: Double){companion object{@JvmStaticfun ofDouble(double:Double):Latitude{return Latitude(double)}fun ofLatitude(latitude: Latitude): Latitude{return Latitude(latitude.value)}@JvmFieldval TAG: String="Latitude"} }
这里@JvmStatic和@JvmField不加此注解,在companion object代码块中均可作为静态方法或静态属性,但是如果不加不能被java代码所识别。如果涉及到java代码调用此静态方法或静态属性,则需要加@JvmStatic或@JvmField注解;它们分别修饰方法和属性;
4.5 方法重载与默认
方法重载的特点:
- Overloads
- 名称相同、参数不同的方法
- Jvm函数签名的概念: 函数名、参数列表
- 跟返回值没有关系
默认参数的特点:
- 为函数参数设定一个默认值
- 可以为任意位置的参数设置默认值
- 函数调用产生混淆时用具名参数
重载与默认:
- 二者的相关性以及@JvmOverloads
- 避免定义关系不大的重载
- 代码示例如图:
方法重载能够用默认参数来解决,所以能不使用就尽量不使用。@JvmOverloads能用于被java代码所识别,如果只在Kotlin中使用可以不用加这个注解;
4.6 扩展成员
- 在Java中,一些java库的一些方法不够全面,往往我们会自己定义一些Utils,在Kotlin中,我们可以对一些现有的类进行扩展;
- 特点:
- 为现有成员添加方法、属性:
// 添加方法: fun X.y():Z{...} // 添加属性 val X.m 注意扩展属性不能初始化,类似接口属性
- Java调用扩展成员类似调用静态方法
- 代码如图所示:
- 为现有成员添加方法、属性:
4.7 属性代理
如图所示,此懒加载就是一个代理:
特点:
- 定义方法:
val/var <property name>: <Type> by <expression>
- 代理者需要实现相应的setValue/getValue方法
实际上就是说,当我们使用了 val/var xxx by yyy()的属性代理时,其代码执行逻辑主要看yyy()了。如果是var方法我们需要实现setValue和getValue,如果是val则只需要实现getValue方法即可,因为val是可读常量;
- 定义方法:
代码示例:
其中定义了存值和赋值的get,setValue方法;当我们创建时则会自动执行setValue,当取值时则会调用getValue;
4.8 数据类
特点:
- 再见,JavaBean
- 默认实现的copy、toString等方法
- componentN方法
- allOpen和noArg插件
代码示例:
同时,我们可以直接在类中直接定义构造参数值,如图所示:
此时不需要传入构造参数,便可将构造参数值外传并执行其他操作,如打印,如图所示:
数据类有一个问题,就是它没有空的默认的构造方法。编译生成的类是一个final的且无空构造方法,所以不是JavaBean,在有些时候使用是有问题的。我们可以通过noarg和allopen插件解决:
- 引入依赖:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200611225801792.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3MTI4MDQ5,size_16,color_FFFFFF,t_70)
- 定义并应用插件:
- 对指定的数据类使用@PoKo注解,如图所示:
- 引入依赖:
4.9 内部类与匿名内部类
特点:
- 定义在类内部的类
- 与类成员有相似的访问机制
- 默认是静态内部类,非静态用inner关键字
- this@Outter,this@Inner的用法
匿名内部类:
- 没有定义名字的内部类
- 类名编译时生成,类似Outter$1.class
- 可继承父类、实现多个接口,与Java注意区别
定义一个内部类(非静态):
非静态内部类,需要在 类的class 前面加inner关键字
定义一个内部类(静态):
静态内部类。默认内部类为静态内部类;调用时,外部类.内部类()即可;静态内部类无法获取非静态的外部类的属性和方法,因为它是先被编译加载的;而非静态内部类可以持有外部类的非静态属性和方法;
当调用外部类的属性时,我们可以直接调用,也可以用this.@外部类名.属性或方法来执行,如图所示:
匿名内部类实现方法示例:
4.10 枚举
特点:
- 实例可数的类,注意枚举也是类
- 可以修改构造,添加成员
- 可以提升代码的表现力,也有一定的性能开销
- 枚举的属性和方法之间必须要用 ; 号隔开,这里可能是Kotlin中唯一强制要求使用 ; 号的地方
定义一个枚举类,如图所示:
$name 是它的名称 $ordinal 是它括号里的值
调用示例:
// 打印指定的枚举类 1,DEBUG println(LogLevel.DEBUG.getTag()) // 打印指定枚举类的序号 1 println(LogLevel.DEBUG.ordinal) // 打印指定枚举类的实例 ERROR,4 println(ERROR,4)
4.11 密封类
特点:
- 子类可数:
- Kotlin版本小于1.1时,子类必须定义为密封类的内部类
- 在1.1之后,子类只需要与密封类在同一个文件中
- 仔细体会与枚举的不同
- 子类可数:
密封类代码示例:
使用枚举适用于没有参数的情况下,而使用密封类可以用在多参数的情况下,每个类的参数都不尽然相同,同时又想保护此类,不让其他fun方法返回此类,就可以使用密封类;
密封类在class前面加 sealed关键字
五. 高阶函数
5.1 高阶函数的基本概念
- 高阶函数就是把函数作为参数或者返回值的函数
- 传入或者返回函数的函数
- 函数引用 ::prinltn
> 其他都以此延伸,调用与此类似
-带有Receiver的引用 pdfPrinter::println - 示例:
第一种方式 ::println 说明任何对象(Any)都可以调用此方法,所以才能这样写;
5.2 常用高阶函数
map: 一对一映射处理
flatMap: 最细化分离
- 第一个是可以跟map一样进行一一映射
- 第二是可以把集合中的集合进行打散;
joinToString(",") 字符串连接
taskWhile 符合条件立即结束,返回符合条件之前被校验的数据
.let 调用者不为空时,则执行后面的逻辑;
常见高阶函数
- map/flatMap
- fold/reduce
- filter/takeWhile
- let/apply/with/use
具体可百度详细学习
5.3 尾递归优化
- 函数在调用自己之后没有任何操作,这就是尾递归;
- 使用tailrec可以检查是否是尾递归函数;
- 它是递归的一种特殊形式
- 调用自身后无其他操作
- tailrec关键字提示编译器尾递归优化
- 尾递归与迭代的关系;
5.4 闭包
- 概念:
- 函数运行的环境
- 持有函数的运行状态
- 函数内部可以定义函数
- 函数内部也可以定义类
- 代码示例:
- 定义类的闭包函数:
fun main(args: Array<String>){val add5=add(5)println(add5(2)) }fun add(x: Int):(Int)->Int{data class Person(val name:String,val age:Int)return fun(y:Int):Int{return x + y} }
- 定义闭包函数,内部的count能够一直累加,而不会被释放
fun makeFun():()->Unit{var count=0return fun(){println(++count)} }fun main(args: Array<String>){val x=makeFun()x()x()x()... }
- 定义类的闭包函数:
5.5 中缀表达式
- infix 关键字可以简化操作函数,具体可以百度;
- P1,P2,R 表示:参数1,参数2,返回值
- 代码图1:
- 代码图2:
- 函数复合:
- f(g(x))
- 如何实现函数复合
- 回顾:infix的使用
5.6 Currying 科理化
概述:
- 理解Currying的概念
- 简单说就是多元函数变换成一元函数调用链(就是将多个参数的函数变成一个参数的函数的调用链)
- 了解Currying的实现方法
- 有科理化就有反科化,就是将一元函数调用链变成多元函数
- 理解Currying的概念
代码图片示例:
这里是打印参数的例子;
5.7 偏函数
- 把函数中其中一个或多个值固定为某值的函数
- 概述:
- 理解函数的概念:
- 传入部分参数得到的新函数
- 仔细体会与Currying的不同
- 了解偏函数的实现方法
- 理解函数的概念:
- 图示:
5.8 小案例: 统计字符串个数
- 图示:
- 总结:本章节主要讲解到了高阶函数的一些简单概念及基本使用,为了解及熟悉高阶函数打开了大门,在日常使用过程中,熟悉这些高阶函数,能够让我们更快的写入Kotlin的代码,让程序更加简洁优雅;
六. 领域特定语言: DSL
6.1 DSL的特点
- 概述: 它是一门计算机编程语言,具有语言的表达能力,但是它的表达能力有限,通常只关注某个特定的领域,与java可以写web,可以写安卓可以写桌面程序不一样,它的适用面仅限于特定领域,作用范围更小;
6.2 HTML DSL
- 通过写Kotlin代码能生成处Html代码;
- 代码如图所示:
6.3 Gradle Kotlin脚本编写
概述:我们项目工程是以Gradle编写的,可以用Kotlin脚本编写;
好处:
- 带提示
- 更简洁
- 能使用Kotlin语法
- 功能更丰富
一些区别:
- Kotlin脚本需要将build.gradle改名为build.gradle.kts
- 改名后需要重启
- Kotlin脚本中需要将单引号改为双引号
改造前:
改名后,全部爆红:
重启之后,按照Kotlin写法改正后:(可以与原来代码比对,以学习kotlin脚本语法)
改写之后能够正常编译、打包、运行操作,与之前一样;
七、协程
7.1 协程的基本概念
什么是协程
- 协作程序,解决异步问题
- 应用层完成调度
- 支持协程的语言例如:
协程的特点:
- 协程是协同作事情、Java的多线程是抢占的
- 协程是以同步的代码作异步的活;
- 协程消耗资源更少,只需要记录位置,和结束标记等;
- 协程的图片示例:
协程要解决什么问题
- 异步代码像同步代码一样直观
- 简化异步代码异常处理
- 轻量级的并发方案
- 它从1.1开始支持,是实验性质的API,后面可能有一定的变化,但目前已经变化很小了;
如何支持协程
- 编译器对suspend函数的编译支持
- 标准库的基本API支持
- kotlinx.coroutine应用级支持
本章目标
- 掌握协程标准库API的使用方法
- 了解协程的运行原理
- 了解 kotlinx.coroutine框架
7.2 了解协程
- enqueue: 表示异步处理
- 协程是没有异步能力的,这需要我们手动去操作;
- 协程的基本API
- createCoroutine:创建协程
- startCoroutine:启动协程
- suspendCoroutine:挂起协程
- Continuation接口: 运行控制类,负责结果和异常的返回
- CoroutineContext接口:运行上下文,资源持有,运行调度
- ContinuationInterceptor接口
- 协程控制拦截器
- 可用来处理协程调度
- 执行流程:
- 携程被编译成状态机
- suspend函数即状态转移,如图所示:
- 详细来说,就是正常的结果通过resume返回,异常通过resumeWithException抛出,如图所示:
这里的圈可能会转很多次,取决于调用多少次suspend函数;
7.3 kotlin.coroutine框架介绍
- 主要模块,如图所示:
八. Kotlin与Java混合开发
8.1 基本互操作
- 属性读写:
- Kotlin自动识别Java Getter/Setter
- Java操作Kotlin属性通过Getter/Setter
- 空安全类型
- Kotlin有空安全
- Java没有,所以可能会涉及Platform Type,我们可以通过@Nullable和@NotNull来弥补java的不足
- 几类函数的调用:
- 包级函数: 静态方法
- 扩展方法:带Receiver的静态方法
- 运算符重载: 带Receiver的对应名称的静态方法
- 几个常见注解的使用:
- @JvmField: 将属性编译为Java变量
- @JvmStatic: 将对象的方法编译成Java静态方法
上面这两个都是加上之后与java的静态变量|静态方法 没有差别,否则不能被java所识别;
- @JvmOverloads: 默认参数生成重载方法
标注这个注解后能被java识别,它是一个默认参数,java中没有这个,标注后就可以使用了
- @file:JvmName : 指定Kotlin文件编译后的类名
- NoArg与AllOpen
- NoArg为被标注的类生成无参构造;
- 支持JPA注解,如@Entity
- AllOpen为被标注的类去掉final,允许被继承
- 支持Spring注解,如@Component
- 支持自定义注解类型,例如:@PoKo
- NoArg为被标注的类生成无参构造;
- 泛型
- 通配符Kotlin的*对应于Java 的 ?
- 协变和逆变 out/in
- ArrayList
- 没有Raw类型
- Java的List->Kotlin的List<*>
8.2 SAM转换
- 概述:
- Single Abstract Method
- SAM转换的条件
- Java的接口,单一接口方法
- 注意转换后的实例变化
8.3 正则表达式
概述:
- 用Raw字符串定义正则表达式(就是不需要转义符,然后***三个)
- Java的Pattern
- Kotlin的Regex
代码如图:
8.4 集合框架
概述:
- Kotlin到Java的类型映射
- 不可变与可变集合接口
- 部分接口优化
代码如图:
集合类型of 表示为不可变集合,初始化之后就不能进行操作了;而Mutable集合类型表示为可变集合
- 代码如图:
- 代码如图:
8.5 IO操作
- Java版本读取操作:
- Kotlin读取操作:
- 使用use关键词能自动关闭流,如图所示:
- 小文件可以用Kotlin的扩展方法:
- 总结IO操作:
- File、Stream、Reader、Writer的扩展方法
- 使用use扩展自动关闭资源
- 小文件一次性读取操作
8.6 拆箱与装箱
- 了解Kotlin基本类型到Java的映射关系
- 注意规避基本类型相关的问题
- 代码示例:
如果遇到这种问题,可以定义java代码或者中间通过java代码进行处理
8.7 注解处理器
- 首先添加插件:
apply plugin: "kotlin-kapt"// 添加生成路径 sourceSets{main.kotlin.srcDirs+= "build/generated/source/kapt/main" }
然后更新Gradle,通过Gradle右侧Build命令,使用IDEA的Build不能编译,IDEA还不支持
- 如图所示:
九. Kotlin的未来与展望
9.1 Kotlin的应用场景
Kotlin Sript
- Gradle脚本,Gradle 3.0开始部分支持,尚在完善中
Java虚拟机应用
- Web应用,完美支持
- JavaFx,完美支持
前端开发
- 1.1开始正式支持Kotlin-JavaScript
Android应用开发
- Kotlin目前的主战场
Native程序
- 直接编译Kotlin代码为机器码,不依赖Jvm
- 支持与C代码交互
- 技术预览版功能有限,前途无量
9.2 Kotlin-Script脚本编写
- 代码示例:
- 只要以后缀名为kts,就能马上识别出这是脚本
- 还能创建安卓、前端项目,SpringBoot项目,具体省略;
9.3 创建SpringBoot项目
选择项目类型:
输入项目名称和组名:
然后一直next 成功创建后到主界面;
创建成功后如图所示:
配置noarg插件,它能在程序编译的时候,自动的为对象生成默认的无参构造方法
配置allopen插件,因为kotlin的类都是final的,我们继承它的时候需要open,能在编译的时候去掉final;
以上两个只有编译期能使用
- 操作如图所示:(添加的不止于上方所述的内容)
里面配置的如jpa的部分可以根据实际情况决定,不需要可以去掉;
9.4 Kotlin-Natie项目开发
- 可以不需要使用jvm进行编译
- 目前还未正式发布,仅用于了解
- 可直接与C语言进行交互
更多可百度学习
9.5 结束
- 互勉互励,点个关注一起加油吧!( ^∀^)
Kotlin零基础入门到精通(精选)相关推荐
- simulink仿真及代码生成技术入门到精通_Simulink仿真零基础入门到精通实用教学教程 自学全套...
Simulink仿真零基础入门到精通实用教学教程 自学全套,以教程文字为主,毕业论文和报告均可以借鉴. Simulink是电气工程必学的模型仿真专业工具软件,非常的实用.小编在全网中搜索都没有找到非常 ...
- 如何从零基础入门并精通PS?PS如何快速入门?
本文由:"学设计上兔课网"原创,图片素材来自网络,仅供学习分享 如何从零基础入门并精通PS?PS如何快速入门?ps作为时下最受欢迎的p图软件,经常有同学私信问兔课菌:零基础自学ps ...
- python零基础入门教程(非常详细),从零基础入门到精通,看完这一篇就够了
前言 本文罗列了了python零基础入门到精通的详细教程,内容均以知识目录的形式展开. 第一章:python基础之markdown Typora软件下载 Typora基本使用 Typora补充说明 编 ...
- keyshot7工业产品零基础入门到精通自学教程视频全套设计课程
keyshot7工业产品零基础入门到精通自学教程视频全套设计课程9205 课程内容-玛丽圈资源网 --/A21 keyshot7工业产品零基础入门到精通自学教程视频全套设计课程/ ├──素材 | └─ ...
- 300集ps视频从零基础入门到精通
目前来说,PS是一项很基本的工作技能了.并且Photoshop一直都被纳入大学计算机等级考试中,photoshop已经像Word,Excel,PPT那么普及了.相信同学们在写简历的时候也会写上去. 其 ...
- PowerMill模具数控编程视频教程全套产品三轴零基础入门到精通
最近有很多人在问什么是结构设计?结构要处理的东西算是比较灵活,因为一个产品有了外形设计之后,要做结构设计来处理其运动特性.这样的产品在结构上是否坚固,是否能满足产品的要求,就成了结构设计的重要内容,而 ...
- Marvelous Designer布料和角色服装造型完整教程零基础入门到精通实用教学视频教程
Marvelous Designer布料和角色服装造型完整教程零基础入门到精通实用教学视频教程 marvelous designer是目前世界上最流行的服装打板和模拟软件,能够即时的演算服装的打板,外 ...
- Python入门教程(非常详细)从零基础入门到精通,看完这一篇就够了
前言 本文罗列了了python零基础入门到精通的详细教程,内容均以知识目录的形式展开. 第一章:python基础之markdown Typora软件下载 Typora基本使用 Typora补充说明 编 ...
- python 二进制流转图片_Python零基础入门到精通-5.1节:Python程序的执行过程
教程引言: 系统地讲解计算机基础知识,Python的基础知识, 高级知识,web开发框架,爬虫开发,数据结构与算法,nginx, 系统架构.一步步地帮助你从入门到就业. 5.1.1 在命令行中执行Py ...
最新文章
- jq 读取office插件_800+页麦肯锡经典图示发送!让你不用插件,轻松搞定逻辑图...
- expdp / impdp 用法详解
- 非对称性密钥加密 java 实例_JAVA实现非对称加密
- C语言 · 黑色星期五
- mysql explain 为空_车祸现场!我的MySQL千万级数据表选错索引了!
- 带哨兵节点的链_HBA公链 | IPFS:区块链“不可能三角”的可能解
- pytorch torch.utils.data.TensorDataset
- 第八届蓝桥杯第五题取数位
- git 设置忽略文件类型 gitignore
- phonegap2.9.1 android 环境搭建,PhoneGap 开发环境搭建
- HttpUtil工具
- Guava guava-18.0.jar下载
- WS2811彩带驱动库函数
- 分支定界-附Python代码
- win10计算机远程连接命令,详细教你win10设置远程桌面连接命令
- Matlab笔记——License Manager Error -9解决办法——matlab反激活
- 转载:Android Studio 快捷键
- 用c语言计算运费.c
- java向飞秋发文件_飞秋如何发文件夹
- NBA16大亿元合同:鲨鱼飞侠双份肥约 大将军败坏亿元
热门文章
- html中hr标签有哪些属性,htmlhr标签的属性有哪些?HTMLhr标签的样式详解
- 标准体系,技术标准,政策标准,开发模板
- Partial Dependence Plots —— 部分依赖图_特征如何影响模型预测
- Linux下NFS服务器的配置
- 阿里服务器网站访问非常慢,更换本地DNS 解决网站访问速度变慢问题
- 该卸载PhotoShop了!MIT用AI实现3分钟自动抠图,精细到头发丝
- Java向pdf模板中写入数据并在模板之后添加新的表格内容
- Eigen/Matlab库矩阵运算方法
- Logger Log4j2 could not find a logging implementation.解决方法
- #umn 来美国近一个月的简单见闻