Kotlin零基础入门到精通(精选)

399 阅读25分钟

Kotlin零基础入门到精通(精选)

一. Kotlin课程概述

1.1 课程安排:

  1. 课程介绍、Kotlin介绍、开发环境搭建
  2. 基本语法:基本类型、空安全类型、智能转换、类与对象同步、数组与区间
  3. 程序结构:常量与变量、函数、Lambda、类成员、条件表达式、循环语句、运算符、异常捕获
  4. 面向对象:抽象类和接口、Object、伴生对象、扩展方法、属性代理、数据类、内部类、枚举与密封类
  5. 高阶函数:基本概念、常见高阶函数、尾递归优化、闭包、函数复合、科里化、偏函数
  6. DSL:基本概念、案例开发、Gradle脚本
  7. 协程:基本概念、协程的使用、封装协程库、协程原理分析;
  8. 与Java混编:基本互操作、正则表达式、集合框架、IO操作、装箱与拆箱、NoArg插件、AllOpen插件、注解处理器
  9. 应用与展望:前景与展望、编写脚本、服务端、前端、Android、Native

1.2 什么是Kotlin?

  • Kotlin就是一门可以运行在Java虚拟机、Android、浏览器上的静态语言,它与Java 100%兼容,如果你对Java非常熟悉,那么你就会发现Kotlin除了自己的标准库之外,大多仍然使用经典的Java集合框架;
  • 总结来说:
    1. Android官方开发语言
    2. 100%兼容Java
    3. Kotlin-Js 前端开发
    4. Kotlin-Jvm 服务端开发
    5. 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 参考资料

1.7 Hello,world

  1. 安装Kotlin插件,如图所示: 在这里插入图片描述

     安装后要重启一次才能生效;

  2. 创建一个Kotlin工程,如图所示: 在这里插入图片描述 3. 根据实际,填写项目的groupId,ArtifactId,以及Version,如图所示: 在这里插入图片描述

  3. 一路默认下去即可,然后成功创建项目,然后在此项目中创建Kotlin包,如图所示: 在这里插入图片描述

  4. 紧接着创建包net.println.kotlin(根据实际来,此处可不一致),然后创建HelloWorld.kt,如图所示: 在这里插入图片描述

  5. 编写Hello,world代码如下:

    fun main(args:Array<String>){
    	println("Hello World")
    }	
    
  6. 执行,如图所示: 在这里插入图片描述

    执行成功后,控制台会打印Hello,world字样,说明运行成功,如图所示: 在这里插入图片描述

  7. 点击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 Hello
    
    fun 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..-1
      
      fun main(args:Array<String>){
      	// true   是为空,因为此范围中没有值
      	println(emptyRange.isEmpty())
      	// true 此范围中包含了50
      	println(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
    • 跟数一点关系没有
    • 就是一系列对象,这个对象可以是各类型的数字,字符,字符串,或者自定义对象等;
  • 基本写法

    • 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 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=5
      	when(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=5
      	while(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{
    		@JvmStatic
    		fun ofDouble(double:Double):Latitude{
    			return Latitude(double)
    		}
    		
    		fun ofLatitude(latitude: Latitude): Latitude{
    			return Latitude(latitude.value)
    		}
    	
    		@JvmField
    		val 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插件解决:

    1. 引入依赖:
      ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200611225801792.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3MTI4MDQ5,size_16,color_FFFFFF,t_70)
      
    2. 定义并应用插件: 在这里插入图片描述
    3. 对指定的数据类使用@PoKo注解,如图所示: 在这里插入图片描述

4.9 内部类与匿名内部类

  • 特点:

    • 定义在类内部的类
    • 与类成员有相似的访问机制
    • 默认是静态内部类,非静态用inner关键字
    • this@Outter,this@Inner的用法
  • 匿名内部类:

    • 没有定义名字的内部类
    • 类名编译时生成,类似Outter$1.class
    • 可继承父类、实现多个接口,与Java注意区别
  • 定义一个内部类(非静态): 在这里插入图片描述

    非静态内部类,需要在 类的class 前面加inner关键字

  • 定义一个内部类(静态): 在这里插入图片描述

    静态内部类。默认内部类为静态内部类;调用时,外部类.内部类()即可;静态内部类无法获取非静态的外部类的属性和方法,因为它是先被编译加载的;而非静态内部类可以持有外部类的非静态属性和方法;

  • 当调用外部类的属性时,我们可以直接调用,也可以用this.@外部类名.属性或方法来执行,如图所示: 在这里插入图片描述

  • 匿名内部类实现方法示例: 在这里插入图片描述

4.10 枚举

  • 特点:

    • 实例可数的类,注意枚举也是类
    • 可以修改构造,添加成员
    • 可以提升代码的表现力,也有一定的性能开销
    • 枚举的属性和方法之间必须要用 ; 号隔开,这里可能是Kotlin中唯一强制要求使用 ; 号的地方
  • 定义一个枚举类,如图所示: 在这里插入图片描述

    name是它的名称name 是它的名称 ordinal 是它括号里的值

  • 调用示例:

    // 打印指定的枚举类  1,DEBUG
    println(LogLevel.DEBUG.getTag())
    // 打印指定枚举类的序号 1
    println(LogLevel.DEBUG.ordinal)
    // 打印指定枚举类的实例  ERROR,4
    println(ERROR,4)
    

4.11 密封类

  • 特点:

    • 子类可数:
      1. Kotlin版本小于1.1时,子类必须定义为密封类的内部类
      2. 在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=0
      	return 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的实现方法
    • 有科理化就有反科化,就是将一元函数调用链变成多元函数
  • 代码图片示例: 在这里插入图片描述

    这里是打印参数的例子;

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接口
      • 协程控制拦截器
      • 可用来处理协程调度
  • 执行流程:
    1. 携程被编译成状态机
    2. suspend函数即状态转移,如图所示: 在这里插入图片描述
    3. 详细来说,就是正常的结果通过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
  • 泛型
    • 通配符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 结束

  • 互勉互励,点个关注一起加油吧!( ^∀^)