Kotlin快速入门

前言

记录自第一行代码第三版

变量

package 变量

fun main(){
    val a=10;
    println(a);
}

函数

package 函数

import kotlin.math.max
/*
不同文件中不能定义相同名称的函数,否则报错
 */
fun main(){
    val a=37
    val b=40
    val value= largerNumber(a, b)//返回的最大值传给value变量
    println(value)
}
//fun 函数.largerNumber(num1:Int,num2:Int): Int{
//    return max(num1,num2)
//
//}
//fun 函数.largerNumber(num1:Int,num2:Int): Int= max(num1,num2)//等号表达返回值
fun largerNumber(num1:Int,num2:Int)= max(num1,num2)//等号表达返回值


package 函数

import java.lang.Integer.max

// 函数.largerNumber(num1:Int, num2: Int):Int{
//    return max(num1,num2)
//}

循环语句

package 循环语句

//fun main(){
//    for (i in 0..10){//定义变量i,在0到10之间得区间
//
//
//    println(i)k
//
//    }
//}
//______________________________
//val  range = 0 until 10//[0,10)
//fun main() {
//    for (i in 0 until 10)//从0到9,每次都加1,相当于i++
//        println(i)
//}
//————————————————————————————————
//fun main() {
//    for (i in 0 until 10 step 2)//从0到9,每次都加2,相当于i=i+2
//        println(i)
//}
//__________________________________________
//创建降序的区间
fun main(){
    for (i in 10 downTo 1){//[10,1]
        println(i)
    }

}

程序的逻辑控制

if语句

package 程序的逻辑控制

import 函数.largerNumber

fun largerNumber2(num1:Int,num2:Int):Int{
    var value=0
    if (num1>num2){
        value=num1
    }else{
        value=num2
    }
    return value
}
//——————————————————————————————————————————————————————————————————
//if判断可以有返回值
fun largerNumber3(num1:Int,num2: Int):Int{
    val value= if (num1>num2){//将返回值给value
        num1//返回值
    }else{//将返回值给value
        num2//返回值
    }
    return value
}
//直接返回比较的结果
fun largerNumber4(num1:Int,num2: Int):Int{
     return if (num1>num2){
         num1
    }else{//将返回值给value
        num2//返回值
    }
}
//用语法糖进行精简
fun largerNumber5(num1:Int,num2: Int)=if (num1>num2){
    num1
}else{
    num2
}
//最精简版
fun largerNumber6(num1: Int,num2: Int)=if (num1>num2) num1 else num2

 //——————————————————————————————————————————————————————————————
//主程序,进行调用函数
fun main(){
    val a=37
    val b=40
    val value= largerNumber6(a, b)//返回的最大值传给value变量
    println(value)
}

when语句

package 程序的逻辑控制

fun getScore(name:String)=if (name=="Tom"){
    86
}else if (name=="jim"){
    77
}else if (name =="jack"){
    95
} else if (name == "Lily"){
    100
}else{
    0
}
//简化版
fun getScore1(name:String)=when(name){
    "Tom"->86
    "jim"->77
    "jack"->95
    "Lily"->100
    else ->0

}
//____________________________________________
//类型匹配
fun checkNumber(num:Number){
    when(num){
        is Int -> println("number is Int")
        is Double -> println("number is Double")
        else -> println("number not support")
    }
}
fun main(){
    val num1=10
    val num=10L
    checkNumber(num1)
    checkNumber(num)
}
//不带参数
fun getScore2(name:String)=when{
    name=="Tom"->86
    name=="jim"->77
    name== "jack"->95
    name=="Lily"->100
    else ->0
}
//名称以Tom开头的得86分
fun getScore3(name:String)=when{
name.startsWith("Tom")->86
    name=="jim"->77
    name== "jack"->95
    name=="Lily"->100
    else ->0
}

类与对象

package 类与对象

class Person {
    val p = Person()//调用类的构造函数,对类进行实例化,创建对象
    var name =""
    var age = 0

    fun eat(){
        println(name + "is eating.he is "+age +"years old")
    }
    fun main(){//在主函数中定义对象的属性
        p.name="jack"
        p.age=19
        p.eat()//调用对象的eat方法

    }
}

继承与构造函数

Student

package 继承与构造函数

import 类与对象.Person

class Student : Person1(){//Student类继承Person类
var sno=""
    var grade=0

}
open class Person1 {//加上open修饰符让类可以被继承
val p = Person()//调用类的构造函数,对类进行实例化,创建对象
    var name = ""
    var age = 0

    fun eat1() {
        println(name + "is eating.he is " + age + "years old")
    }
}
//无参student的实例化
val student = Student()
//open class Person1(var name:String,val age:Int){
//
//}
//——————————————————————————————————————————————————————————————
//不带参数的主构造函数
class Student1 (val sno:String,val grade:Int):Person1(){//主构造函数继承Person1类;Person1()代表子类构造函数必须调用父类中的无参构造函数
val student = Student1("a123",5)//将学号和年纪这两个字段都放到了主构造函数当中,这就表明在对Student类进行实例化的时候,必须传入
    //构造函数中要求的所有参数,指定Student1类的对象student的学号是a123,年级是5
    init {//在大括号中编写逻辑
        println("sno is"+sno)
        println("grade is "+grade)
    }

}
//两个参数的student类的实例化
val student1 = Student1("jack",19)

Student2

package 继承与构造函数

import 类与对象.Person

//有参数的构造函数
//子类定义name与age字段来接收父类的name与age
class Student2 (val sno:String,val grade: Int,name: String,age:Int):Person2(name,age){//主构造函数继承Person2类;Person2(name,age)代表子类构造函数必须调用父类中的有参构造函数
val student2 = Student2("a123",5,"jack",19)//将学号和年纪和姓名和年龄这两个字段都放到了主构造函数当中,这就表明在对Student类进行实例化的时候,必须传入
    //构造函数中要求的所有参数,指定Student1类的对象student的学号是a123,年级是5
    init {//在大括号中编写逻辑
        println("sno is"+sno)
        println("grade is "+grade)
    }

}
open class Person2( name: String, age: Int) {//定义name与age的有参函数,父类的有参构造函数
    //加上open修饰符让类可以被继承
    val p = Person2("xiaoming",15)//调用类的构造函数,对类进行实例化,创建对象
}

Student3

package 继承与构造函数
/*
次构造函数
一个类中只有有一个主构造函数,能有多个次构造函数,次构造函数也能进行实例化类
注意变量名和方法名一定要写对,不要重复
 */
class Student3(val sno:String,val grade:Int,name:String,age:Int):Person3(name,age){//主构造函数
    constructor(name: String,age: Int):this("",0,name,age){//第一个次构造函数,接收name与age参数,通过this调用主构造函数对sno与
    //grade进行赋值成初始值

    }
    constructor():this("",0){//第二个次构造函数不接收任何参数,通过this调用第一个次构造函数,页将sno与grade进行赋值成初始值

    }
}

open class Person3( name: String, age: Int) {//定义name与age的有参函数,父类的有参构造函数
//加上open修饰符让类可以被继承
val p = Person3("xiaoming",15)//调用类的构造函数,对类进行实例化,创建对象
}
//四个参数student类的实例化
val student3 =Student3("a123",5,"jack",19)

Student

package 继承与构造函数
//只有定义次构造函数,没有定义主构造函数;没有主构造函数就不用在继承的时候家Person的参数
//Person的参数是为了与主构造函数的参数相耦合
import 类与对象.Person

class Student4:Person4{
    constructor(name:String,age:Int):super(name,age){//次构造函数,直接调用父类的构造函数,所以用super关键字

    }
}
open class Person4( name: String, age: Int) {//定义name与age的有参函数,父类的有参构造函数
//加上open修饰符让类可以被继承
val p = Person2("xiaoming",15)//调用类的构造函数,对类进行实例化,创建对象
}

接口

package 接口
/*
子类继承父类,子类的变量名要和父类的变量名一致
接口中如果有()是为了调用构造函数
 */
import 类与对象.Person
import 继承与构造函数.Person2

interface Study {
    fun readBooks()//调用readBooks方法
    fun doHomework(){//调用doHomework方法
        println("do homework default implementation.")//默认实现,如果没有重写,默认执行这个代码
    }
}
class Student(private val name5:String, age5:Int):Person5(name5,age5), Study{//创建Student类来继承Person5类同时实现Study接口
    override fun readBooks() {//重写readBooks方法
        println(name5+" is reading")//输出Student的参数name5

    }

//    override fun doHomework() {//重写doHomework方法
//        println(name5+" is doing homework")//输出Student的参数name5
//
//    }
}
open class Person5( name5: String, age5: Int) {//定义name与age的有参函数,父类的有参构造函数
//加上open修饰符让类可以被继承

}

fun main() {//主函数
    val student = Student("jack",19)//创建并实例化对象
    doStudy(student)//调用doStudy方法,将实例化的对象参数传入
}

fun doStudy(study: Study) {//创建doStudy方法,创建study对象,实现Study接口
    study.readBooks()//对象调用Study接口中的readBooks方法
    study.doHomework()//对象调用Study接口中的doHomework方法
}

数据类与单例类

:是强行指定它是什么类型的
比如val str:String //强行指定str常量是String 类型

package 数据类与单例类
//数据类
class Cellphone {

    data class Cellphone1 (val brand:String,val price:Double)//创建数据类,声明手机的品牌和价格的字段
   // class Cellphone1 (val brand:String,val price:Double)//没有data修饰是输出它的地址,因为没有重写toString方法,而且不能创建对象

}
class li

fun main() {//主函数

    val cellphone1= Cellphone.Cellphone1("Samsung", 1299.99)//两个参数的数据类要用类名来指定,创建数据类的对象
    val  cellphone2= Cellphone.Cellphone1("Samsung", 1299.99)//两个参数的数据类要用类名来指定,创建数据类的对象
    println(cellphone1)//输出数据类的对象
    println("cellphone1 equals cellphone2"+(cellphone1==cellphone2))//判断两个数据类的对象是否一致

}
package 数据类与单例类
//创建单例
object Singleton {
    fun singletonTest(){//创建单例的方法
        println("singletonTest is called.")
    }
}

fun main() {
    Singleton.singletonTest()//用静态方法的方式,用类名调用单例方法来进行调用
}

集合

list

package Lambda
//集合的创建与遍历
//定义集合和添加集合中的元素要在主函数中进行
//kotlin语法与java有相似的地方,可以对比着看
class list {


}

fun main() {
    //最原始的写法
    var list=ArrayList<String>()
    list.add("Apple")
    list.add("Banana")
    list.add("Orange")
    list.add("Pear")
    list.add("Grape")
    println(list)
    //————————————————————————
    //使用listOf()函数来出初始化集合
    val list2 = listOf("Apple","Banana","Orange","Pear","Grape")//创建list集合并添加元素,创建不可变的集合
   // list2.add("Watermelon")//不可向不可变集合添加元素
    for(fruit in list2){//使用循环来遍历list2,遍历到的元素存放在fruit中
        println(fruit)
    }
    //使用mutableListOf()函数创建可变的集合
    val  list3 = mutableListOf("Apple","Banna","Orange","Pear","Grape")
    list3.add("Watermelon")//向集合中添加元素
    for (fruit2 in list3){//使用循环来遍历list3,遍历到的元素存放在fruit2中
        println(fruit2)
    }

}

map

package Lambda

class map {

}

fun main() {
    //用类似java的方法通过put与get方法来存取数组元素
    val map = HashMap<String,Int>()
    map.put("Apple",1)
    map.put("Banana",2)
    map.put("Orange",3)
    map.put("Pear",4)
    map.put("Grape",5)
    //简化版
    val map2 = HashMap<String,Int>()
    map2["Apple"]=1//存数组元素
    map2["Banana"]=2
    map2["Orange"]=3
    map2["Pear"]=4
    map2["Grape"]=5
    val number=map2["Apple"]//通过键来取值
    println(number)
    //___________________________________________
    println("________________________________________________")
    //通过mapOf()与mutableMapof()来创建map集合,注意要留有空格,别挤在一起,否则无法识别
    val map3= mapOf("Apple" to 1 ,"Banana" to 2,"Orange" to 3,"Pear" to 4,"Grape" to 5)//传入初始的键值对组合来创建map集合
    for ((fruit,number)in map3){//用for in来进行遍历,键遍历到存放到fruit中,值遍历到存放到number中
        println("fruit is"+fruit+",number is "+number)
    }


}

set

package Lambda
//主函数必须单独定义,不能在类里面,当内部类
class set {

}
//set集合
fun main() {
    val set = setOf("Apple","Banna","Orange","Pear","Grape")//创建不可变集合
    for (fruit in set){
        println(fruit)
    }
    val set2 = mutableSetOf("Apple","Banna","Orange","Pear","Grape")//创建可变集合
    set2.add("西瓜")//为集合添加元素
    for (fruit2 in set2){
        println(fruit2)
    }
}

集合的函数式API(lambda表达式)

package 集合的函数式API
//需求:遍历所有水果集合,并取出单词最长的水果

class MaxLength {

}

fun main() {
    //最简单的写法
    val list= listOf("Apple","Banna","Orange","Pear","Grape","Watermelon")//定义不可变的集合
    var maxLengthFruit=""//创建存放最长长度的水果名称变量
    for (fruit in list){//用for in 循环遍历list ,结果存放在fruit
        if (fruit.length>maxLengthFruit.length){//如果遍历到的结果大于存放最长长度的水果名称变量
            maxLengthFruit=fruit//将遍历到的结果赋值给最长长度的水果名称变量
        }
    }
    println("1 max length fruit is "+maxLengthFruit)
println("______________________________________________________")
    //用集合的函数式API
    val list2= listOf("Apple","Banna","Orange","Pear","Grape","Watermelon")//定义不可变的集合
    val maxLengthFruit2=list2.maxBy { it.length }//maxBy根据我们传入的条件来遍历集合,从而找到该条件下的最大值
    println("2 max length fruit is "+maxLengthFruit2)
 println("_______________________________________________________")
    val list3= listOf("Apple","Banna","Orange","Pear","Grape","Watermelon")//定义不可变的集合
    val lambda={fruit2:String->fruit2.length}//定义lambda表达式,定义String 类型的frulit2变量,取其长度
    val maxLengthFruit3=list3.maxBy(lambda)//取list3里面lambda表达式中字符的最大值
    println("3 max length fruit is "+maxLengthFruit3)
    println("_______________________________________________________")
    //省去lambda变量,之间将lambda表达式传入
    val list4= listOf("Apple","Banna","Orange","Pear","Grape","Watermelon")//定义不可变的集合
    val maxLengthFruit4=list4.maxBy({fruit4:String->fruit4.length})//注意,里面还要有大括号,大括号的内容就是lambda表达式,小括号的内容是maxby的参数
    println("4 max length fruit is "+maxLengthFruit4)
    println("_______________________________________________________")
    //当Lambda参数式函数的最后一个参数时,可以将Lambda表达式移到函数括号外面
    val list5= listOf("Apple","Banna","Orange","Pear","Grape","Watermelon")//定义不可变的集合
    val maxLengthFruit5=list5.maxBy(){fruit5:String->fruit5.length}
    println("5 max length fruit is "+maxLengthFruit5)
    println("_______________________________________________________")
    //lambda参数是函数的唯一参数,还可以将函数括号省略
    val list6= listOf("Apple","Banna","Orange","Pear","Grape","Watermelon")//定义不可变的集合
    val maxLengthFruit6=list6.maxBy{fruit6:String->fruit6.length}
    println("6 max length fruit is "+maxLengthFruit6)
    println("_______________________________________________________")
    //lambda表达式参数列表可以不用声明参数类型
    val list7= listOf("Apple","Banna","Orange","Pear","Grape","Watermelon")//定义不可变的集合
    val maxLengthFruit7=list7.maxBy{fruit7->fruit7.length}
    println("7 max length fruit is "+maxLengthFruit7)
    println("_______________________________________________________")
    //当Lambda表达式的参数列表只有一个参数时,不必声明参数名,可以使用it关键字来替代
    val list8= listOf("Apple","Banna","Orange","Pear","Grape","Watermelon")//定义不可变的集合
    val maxLengthFruit8=list8.maxBy{it.length}
    println("8 max length fruit is "+maxLengthFruit8)
    println("_______________________________________________________")


}

判空辅助工具

fun doStudy(study:Study?){//Study类型的study变量可为空
study?.readBooks()//当study对象为空的时候什么都不做,当study对象不为空时调用readBooks()方法
}

val c = a ?: b //如果左边表达式的结果不为空就返回左边表达式的结果,否则就返回右边表达式的结果

!!为非空断言工具,写法时写在对象的后面加上!!,意在告诉kotlin,我非常确信这里的对象不会为空,所有不用你来帮我做空指针检查

let函数的使用

   let函数的介绍
/*
  ① fun dostudy(study:Study?){
   study?.readBook()
   }



   fun doStudy(study:Study?){
   if(study!=null){
   study.readBook()
   }
   if(study!=null){
   study.doHomework()
   }
   }
   ①等于②,一次if就能调用study对象的任何方法,但?的限制,每次调用study对象方法都要进行一次if判断,冗余



   let函数能够判断一次非空就调用非空对象的所有方法
   fun doStudy(study:Study?){
   study?.let{ stu->              //stu代表study的对象,名字可以随意写,调用所需,如果对象为空时候什么都不做,对象不为空时调用let函数
   stu.readBooks()                //let函数将study对象作为参数传到lambda表达式中
   stu.doHomework()
   }
   }



   fun doStudy(study:Study?){
   study?let{                   //如果不为空,就执行let函数
   it.readBooks()               //当lambda表达式的参数列表只有一个参数时,可以不用声明参数名,直接用it关键字来代替
   it.doHomework()

   }
   }



   let函数可以处理全局变量的判定
   var study:Study?=null
   fun  doStudy(){
   (study!=null){        //doStudy()函数中的参数变成一个全局变量
   study.readBooks()
   study.doHomework()
   }
   }
 */

字符串内嵌表达式

/*
 格式
 ${}
 "hello,${obj.name}.nice to meet you"
 当表达式仅有一个变量,可以将两边的大括号省略
 $
 "hello,$obj.name.nice to meet you"




 */

函数的参数默认值

/*
目标:给函数设定参数默认值
作用:在定义函数的时候给任意参数设定一个默认值,这样当调用此函数时就不会强制要求调用方为此参数传值,没有传值的情况下会自动设置参数的默认值


①主构造器和次构造器
class student(val sno:String,val grade:Int,name:String,age:Int):Person(name,age){//student类继承Person类,主构造函数
constructor(name:String,age:Int):this("",0,name,age)//对Person类进行实例化,调用4个参数的主构造函数,将缺失的两个参数赋值成初始值
constructor();this("",0){//对Person类进行实例化,调用两个参数的次构造函数,将两个参数赋值成初始值
}
}


用给函数设定参数默认值来实现①
class student(val sno:String="",val grade:Int=0,name:String="",age:Int=0):Person(name,age){//student类继承Person类,主构造函数



 */
fun printParams(num:Int,str:String="hello"){//给str的参数默认传值为hello
    println("num is $num , str is $str")//$为字符串内嵌表达式
}
fun main() {
printParams(123)//给num传值123,第二个不传值,为默认值;按照参数的定义顺序来传参
printParams2(str="world")//按照键值对的方式来传参,而num用默认值
}


fun printParams2(num:Int=100,str:String){//给num的参数默认传值为100
    println("num is $num , str is $str")//$为字符串内嵌表达式
}

延迟初始化和密封类

对变量延迟初始化

lateinit关键字:修饰后,可以晚些对这个变量进行初始化,不用一开始就将它赋值为null

MainActivity.kt

package com.example.a20200612study2

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.msg_left_item.view.*
import kotlinx.android.synthetic.main.msg_right_item.view.*
import java.text.FieldPosition

class MainActivity : AppCompatActivity() ,View.OnClickListener{
    private val msgList=ArrayList<Msg>()
    private lateinit var adapter:MsgAdapter//对变量进行延迟初始化

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initMsg()
        val  layoutManager= LinearLayoutManager(this)
        recyclerView.layoutManager=layoutManager//给RecyclerView指定LayoutManager
        adapter=MsgAdapter(msgList)//给RecyclerView指定适配器
        recyclerView.adapter=adapter
        send.setOnClickListener(this)
    }
    class Msg(val content:String,val type:Int){//content为消息内容,Type表示消息的类型
        companion object{//定义常量的关键字是const,在单例类、companion object或顶层方法才能实用const关键字
            const val TYPE_RECEIVED=0//TYPE_RECEIVED表示收到消息
            const val TYPE_SENT=1;//TYPE_SENT表示发送消息
        }
    }
    class MsgAdapter(val msgList: List<Msg>):RecyclerView.Adapter<RecyclerView.ViewHolder>(){
        inner class LeftViewHolder(view: View):RecyclerView.ViewHolder(view){//创建viewHolder用于缓存布局的控件
            val leftMsg:TextView=view.findViewById(R.id.leftMsg)
        }
        inner class RightViewHolder(view:View):RecyclerView.ViewHolder(view){//创建viewHolder用于缓存布局的控件
            val rightMsg:TextView=view.findViewById(R.id.rightMsg)
        }
        override  fun  getItemViewType(position:Int):Int{
            val msg=msgList[position]//根据postion来获取消息
            return msg.type//返回消息的类型
        }
        override fun onCreateViewHolder(parent:ViewGroup,viewType:Int)=if (viewType==Msg.TYPE_RECEIVED){//根据不同的viewType创建不同的界面
            val view= LayoutInflater.from(parent.context).inflate(R.layout.msg_left_item,parent,false)
            LeftViewHolder(view)//根据不同的viewType来加载不同的ViewHolder
        }else{
            val view=LayoutInflater.from(parent.context).inflate(R.layout.msg_right_item,parent,false)
            RightViewHolder(view)//根据不同的viewType来加载不同的ViewHolder
        }
        override  fun onBindViewHolder(holder:RecyclerView.ViewHolder,position:Int){
            val msg=msgList[position]
            when(holder){//判断ViewHolder类型
                is LeftViewHolder->holder.leftMsg.text=msg.content//is相当于if,如果是leftViewHolder就将内容显示到左边的消息布局
                is RightViewHolder->holder.rightMsg.text=msg.content//is相当于if,如果是RightViewHolder就将内容显示到右边的消息布局
                else -> throw  IllegalArgumentException()
            }
        }
override  fun getItemCount()=msgList.size
    }

    override fun onClick(v: View?) {
     when(v){
         send->{
             val content=inputText.text.toString()//获取EditText中的内容
             if (content.isNotEmpty()){//如果内容不为空字符串
                 val msg = Msg(content,Msg.TYPE_SENT)//创建一个新的Msg对象
                 msgList.add(msg)//Msg对象添加到msgList列表中去
                 adapter.notifyItemInserted(msgList.size-1)//调用适配器的方法,当有新消息时,刷新RecyclerView中的显示,对变量进行延迟初始化后,就不用进行判空处理
                 //notifyDataSetChanged()方法,将RecyclerView 中所有可谏的元素全部刷新
                 recyclerView.scrollToPosition(msgList.size-1)//将RecyclerView定位到最后一行
                 inputText.setText("")//清空输入框中的内容
             }
         }
     }
    }
    private  fun initMsg(){//初始化几条数据在RecyclerView zhong xianshi
        val  msg1=Msg("你好",Msg.TYPE_RECEIVED)
        msgList.add(msg1)
        val msg2=Msg(" 你好,你是谁?",Msg.TYPE_SENT)
        msgList.add(msg2)
        val  msg3=Msg("我是tom,很高兴和你谈话",Msg.TYPE_RECEIVED)
        msgList.add(msg3)
    }
}

用代码来判断全局变量是否完成初始化

package com.example.a20200612study2

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.msg_left_item.view.*
import kotlinx.android.synthetic.main.msg_right_item.view.*
import java.text.FieldPosition

class MainActivity : AppCompatActivity() ,View.OnClickListener{
    private val msgList=ArrayList<Msg>()
    private lateinit var adapter:MsgAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initMsg()
        val  layoutManager= LinearLayoutManager(this)
        recyclerView.layoutManager=layoutManager//给RecyclerView指定LayoutManager
        /**************************判断全局变量是否被初始化****************/
      if (!::adapter.isInitialized) {//::adapter.isInitialized判断adapter变量是否已经初始化,!来进行取反,如果没有初始化
            adapter = MsgAdapter(msgList)//给RecyclerView指定适配器,对adapter变量进行初始化
        }
        /**************************判断全局变量是否被初始化****************/
        recyclerView.adapter=adapter
        send.setOnClickListener(this)
    }
    class Msg(val content:String,val type:Int){//content为消息内容,Type表示消息的类型
        companion object{//定义常量的关键字是const,在单例类、companion object或顶层方法才能实用const关键字
            const val TYPE_RECEIVED=0//TYPE_RECEIVED表示收到消息
            const val TYPE_SENT=1;//TYPE_SENT表示发送消息
        }
    }
    class MsgAdapter(val msgList: List<Msg>):RecyclerView.Adapter<RecyclerView.ViewHolder>(){
        inner class LeftViewHolder(view: View):RecyclerView.ViewHolder(view){//创建viewHolder用于缓存布局的控件
            val leftMsg:TextView=view.findViewById(R.id.leftMsg)
        }
        inner class RightViewHolder(view:View):RecyclerView.ViewHolder(view){//创建viewHolder用于缓存布局的控件
            val rightMsg:TextView=view.findViewById(R.id.rightMsg)
        }
        override  fun  getItemViewType(position:Int):Int{
            val msg=msgList[position]//根据postion来获取消息
            return msg.type//返回消息的类型
        }
        override fun onCreateViewHolder(parent:ViewGroup,viewType:Int)=if (viewType==Msg.TYPE_RECEIVED){//根据不同的viewType创建不同的界面
            val view= LayoutInflater.from(parent.context).inflate(R.layout.msg_left_item,parent,false)
            LeftViewHolder(view)//根据不同的viewType来加载不同的ViewHolder
        }else{
            val view=LayoutInflater.from(parent.context).inflate(R.layout.msg_right_item,parent,false)
            RightViewHolder(view)//根据不同的viewType来加载不同的ViewHolder
        }
        override  fun onBindViewHolder(holder:RecyclerView.ViewHolder,position:Int){
            val msg=msgList[position]
            when(holder){//判断ViewHolder类型
                is LeftViewHolder->holder.leftMsg.text=msg.content//is相当于if,如果是leftViewHolder就将内容显示到左边的消息布局
                is RightViewHolder->holder.rightMsg.text=msg.content//is相当于if,如果是RightViewHolder就将内容显示到右边的消息布局
                else -> throw  IllegalArgumentException()
            }
        }
override  fun getItemCount()=msgList.size
    }

    override fun onClick(v: View?) {
     when(v){
         send->{
             val content=inputText.text.toString()//获取EditText中的内容
             if (content.isNotEmpty()){//如果内容不为空字符串
                 val msg = Msg(content,Msg.TYPE_SENT)//创建一个新的Msg对象
                 msgList.add(msg)//Msg对象添加到msgList列表中去
                 adapter.notifyItemInserted(msgList.size-1)//调用适配器的方法,当有新消息时,刷新RecyclerView中的显示
                 //notifyDataSetChanged()方法,将RecyclerView 中所有可谏的元素全部刷新
                 recyclerView.scrollToPosition(msgList.size-1)//将RecyclerView定位到最后一行
                 inputText.setText("")//清空输入框中的内容
             }
         }
     }
    }
    private  fun initMsg(){//初始化几条数据在RecyclerView zhong xianshi
        val  msg1=Msg("你好",Msg.TYPE_RECEIVED)
        msgList.add(msg1)
        val msg2=Msg(" 你好,你是谁?",Msg.TYPE_SENT)
        msgList.add(msg2)
        val  msg3=Msg("我是tom,很高兴和你谈话",Msg.TYPE_RECEIVED)
        msgList.add(msg3)
    }
}

使用密封类优化代码

密封类的关键字是 sealed class,使用密封类可以使判断语句更严谨,不必写else 语句,但必须处理子类里面的所有条件

密封类及其所有子类只能定义在同一文件的顶层位置,不能嵌套在其他类中

package com.example.a20200613study

import java.lang.Exception
import kotlin.IllegalArgumentException

sealed class Result
class Success(val msg:String):Result()//密封类可继承
class Failure(val error:Exception):Result()
fun getResultMsg(result: Result)=when(result){//接收一个result参数,用when判断result参数的值
    is Success->result.msg//如果result为Success  返回result的消息
    is Failure -> "Error is ${result.error.message}"
}
//class UnKnown :Result()//因为判断里没有unknown分支,所以方法必然报错

扩展函数和运算符重载

扩展函数

功能

即使在不修改某个类的源码的情况下,仍然可以打开这个类,向该类添加新的函数

语法结构

fun className.methodName(param1:Int,param2:Int):Int{
return 0
}

定义扩展函数只需要在函数名前面加上className.的语法结构,就表示将该函数添加到指定类当中了

例子

one.kt

/*
用于统计字符串中字母的数量
 */
var count=0
var count2=0
class one {

    object StringUtil{//用object关键字来创建单例类
        fun lettersCount(str:String):Int{//传入要统计的str字符串的变量
            for(char in str){//for in 类似java的foreach,创建循环后的变量char 循环的变量str
                if (char.isLetter()){//如果该字符是一个字符的话
                    count++//加一
                }
            }
            return count//返回以便调用
        }
    }


}

fun main() {
    one.StringUtil.lettersCount("ertert654651ertert651321")//调用函数,传入要遍历的字符串
    println(count)
    //————————————————————————————————————————————————————————
    //另法
    val str2="salkjflkasjfoiejqorf1564656849asd"
    val count2= one.StringUtil.lettersCount(str2)
println(count2)
}

String.kt

//创建顶层方法,让扩展函数拥有全局的访问域
//表示向String类中添加一个扩展函数,将lettersCount()方法定义成String类的扩展函数,该函数自动拥有了String实例的上下文
//因此lettersCount()函数就不再需要接收一个字符串参数,而是遍历this,因为现在的this就代表字符串本身
fun String.lettersCount():Int{//将lettersCount()方法定义成String类的扩展函数
    var count3=0//创建变量用于接收字符串的数量
    for (char in this){//用for in 来进行遍历
        if (char.isLetter()){
            count3++
        }
    }
    return count3
}
fun main() {
    val count3="salkjgoiij1654641sadaojhsf13464".lettersCount()//调用String类型(字符串)的lettersCount()方法
println(count3)
}

有趣的运算符重载

系统提供的类无法修改,但是能扩展功能,借用扩展函数功能向String类中添加新函数

运算符重载使用的是oprator关键字,在不同函数的前面加上关键字就能实现不同的运算符重载的功能

class obj{
    operator fun plus(obj: obj):obj{
        //处理相加的逻辑
        val obj1= obj()
        val obj2=obj()
        val obj3=obj1+obj2//相当于obj1.plus(obj2)
return obj3
    }
}

Money.kt

class Money (val value:Int) {//Money的主构造函数接收一个value参数,用于表示钱的金额
operator  fun plus(money:Money):Money{//operator关键字修饰plus使其加法运算符重载
    val sum=value+money.value//将当前Money对象的value和参数传入的Money对象的value相加
    return  Money(sum)//将得到的和传给一个新的Money对象并将该对象返回
}
}

fun main() {
    val money1=Money(5)
    val money2=Money(10)
    val money3=money1+money2
    println(money3.value)
}

同一个运算符进行多重重载

Money对象直接和数字相加

    operator  fun plus(newValue:Int):Money{//又重载一个plus()函数,这次接收的参数是一个整型数字
        val sum=value+newValue
        return Money(sum)

    }
}

fun main() {
    val money1=Money(5)
    val money2=Money(10)
    val money3=money1+money2
    val money4=money3+20
    println(money4.value)
}
//Money对象直接和数字相加
//kotlin允许我们对同一个运算符进行多重重载

three.kt

class three {


}
//随机生成字符串长度的函数
fun getRandomLengthString(str:String):String{
    val n=(1..20).random()//1到20之间随机
    val builder=StringBuilder()//创建个字符串构造器对象
    repeat(n){//循环n次
        builder.append(str)//build当作传输工具,将传入的str进行存储
    }
   return  builder.toString()//build当作传输工具,将存储的str进行输出,返回以供调用
}


fun main() {
    //判断"hello"字符串是否包含he字串
    if("hello".contains("he")){
println("包含")
    }
    if ("he" in "hello"){
        println("是的")
    }
    getRandomLengthString("15643216543126354654")

}
//创建顶层方法,让扩展函数拥有全局的访问域
//表示向String类中添加一个扩展函数,将lettersCount()方法定义成String类的扩展函数,该函数自动拥有了String实例的上下文
//因此lettersCount()函数就不再需要接收一个字符串参数,而是遍历this,因为现在的this就代表字符串本身
fun String.lettersCount():Int{//将lettersCount()方法定义成String类的扩展函数
    var count3=0//创建变量用于接收字符串的数量
    for (char in this){//用for in 来进行遍历
        if (char.isLetter()){
            count3++
        }
    }
    return count3
}
fun main() {
    val count3="salkjgoiij1654641sadaojhsf13464".lettersCount()//调用String类型(字符串)的lettersCount()方法
println(count3)
}

让字符串可以乘以一个数字(重复指定的遍数遍)

fun main() {
    val str= "abc" * 3
    fun getRandomLengthString(str:String) {
        operator fun String.times(n: Int): String {//重载乘法运算符味times,方法名前面加上String的语法结构
            val builder = StringBuilder()
            repeat(n) {
                builder.append(this)
            }
            return builder.toString()
        }
    }
   println(str)
}

字符串重复n遍的精简写法

/* 字符串重复n遍的精简写法 */
//operator  fun String.times(n:Int)=repeat(n)
fun main() {

    fun getRandomLengthString(str:String)=str * (1..20).random()//定义一个String类型的字符串str,让其乘以一个1到20的随机数
    val str= "abc" * 2//abc重复两边
    println(str)
    val str2=getRandomLengthString(str)//abc作为参数传入getRandomLengthString,让其乘以一个1到20的随机数
    println(str2)
}

private operator fun String.times(n: Int): String {//字符串类型的变量的扩展函数
val builder =StringBuilder()//定义字符串构造器的实例
    repeat(n){//重复n此
        builder.append(this)//将内容添加进行
    }
    return builder.toString()//返回以供调用
}

高阶函数详解

定义高阶函数

概念

一个函数接收另一个函数作为参数,或者返回值的类型是另一个函数;将函数类型添加到一个函数的参数声明或者返回值声明当中

语法规则

(String,Int)-> Unit ->左边的部分就是用来声明该函数接收什么参数的,多个参数之间使用逗号隔开,如果不接收任何参数,写一对空括号就行;->右边的部分用于声明该函数的返回值是什么类型,如果没有返回值就使用U你太,它大致相当于java 中的void

将函数类型添加到某个函数的参数声明或者返回值声明上,则为高阶函数;example接收一个函数类型的参数

fun example(func:(String,Int)-> Unit){
    func("hello",123)
}

调用方法

类似于普通函数,只需要在参数名的后面加上一对括号,并在括号中传入必要的参数即可

one.kt

fun Num1AndNum2(num1:Int,num2:Int,Operation:(Int,Int)->Int):Int{//第三个参数是一个接收两个整型参数并且返回值也是整型的函数类型参数
    val resuit=Operation(num1,num2)//将num1和num2参数传给第三个函数类型参数,并获取它的返回值
    return resuit//将得到的返回值返回
}
//定义与其函数类型相匹配的函数,下面两个函数声明和返回值声明都和Num1AndNum2()函数中的函数类型参数是完全匹配的
fun plus(num1: Int,num2: Int):Int{
    return num1+num2//两个数相加并返回
}
fun minus(num1: Int,num2: Int):Int{
    return num1-num2//两个数相减并返回
}
fun main() {
    val num1=100
    val num2=80
    val result1=Num1AndNum2(num1,num2,::plus)//::plus为函数引用方式的写法,表示将plus()函数作为参数传递给num1AndNum2()函数
    val result2=Num1AndNum2(num1,num2,::minus)//::minus为函数引用方式的写法,表示将minus()函数作为参数传递给num1AndNum2()函数
    //Num1AndNum2()函数中使用传入的函数类型参数来决定具体的运算逻辑
    println("result1 is $result1")
    println("result2 is $result2")
    /***********************使用Lambda表达式来********************************/
    val num3=100
    val num4=80
    val result3=Num1AndNum2(num3,num4){n3,n4->
        n3+n4//Lam表达式中的最后一行代码会自动作为返回值
    }
    val result4=Num1AndNum2(num3,num4){n3,n4->
        n3-n4//Lam表达式中的最后一行代码会自动作为返回值
    }
    println("result3 is $result3")
    println("result4 is $result4")
    /***********************使用Lambda表达式来********************************/
}

two.kt

fun StringBuilder.build(block:StringBuilder.()->Unit):StringBuilder{
    /*
        给StringBuilder类定义一个build扩展函数,这个扩展
    函数接收一个函数类,并且返回值类型也是StringBuilder;
    StringBuilder. 为在函数类型的前面加上Classname,就表示这个函数类型是定义在哪个类当中
    将函数类型定义到StringBuilder类可以当我们调用buid函数时传入的Lambda表达式将会自动拥有StringBuilder的上下文
     */
    block()
    return this
}
fun main() {
    val list = listOf("苹果", "香蕉", "橘子", "桃", "葡萄")
    val result = StringBuilder().build {//使用扩展函数
        append("开始吃水果.\n")
        for (fruit in list){
            append(fruit).append("\n")//list集合的数组添加到fruit变量
        }
        append("吃了所有水果")
    }
    println(result.toString())
}

内联函数

作用:将内联函数中的代码在编译的时候自动替换到调用它的地方,这样也就不存在在运行时的开销了

inline fun num1AndNum2(num1:Int,num2:Int,operation:(Int,Int)->Int):Int{
    val result = operation(num1,num2)
    return result
}

noinline与crossinline

noinline

作用

一个高阶函数中如果接收了两个或者更多函数类型的参数,只内联其中一个Lambda表达式,使用noinline关键字修饰

inline fun inlineTest(block1:() -> Unit,noinline block2:()->Unit)
原因

内联的函数类型参数在编译的时候会被进行代码替换,因此它没有真正的参数属性,非内联的函数类型参数可以自由地传递给其他任何函数,因为它就是一个真实的参数,而内联的函数类型参数只允许传递给另一个内联函数,着也是它最大的局限性

内联函数和非内联函数的区别

内联函数所引用的Lambda表达式中式可以使用return关键字来进行函数返回的,而非内联函数只能进行局部返回

fun printString(str:String,block:(String)->Unit){//定义printString()的高阶函数,用于lambda表达式中打印传入的字符串参数
    println("printString begin")
    block(str)
    println("printString end")
}
fun main() {
    println("main start")
    val str=""
    printString(str){ s->
        println("lambda start")
        if (s.isEmpty()) return@printString//如果字符串参数为空,则不进行打印,Lambda表达式中式不允许直接使用return关键字
        //return@printString 进行局部返回,而且不再执行Lambda表达式的剩余部分代码
        println(s)
        println("lambda end")
    }
    println("main end")
    println("_________________________________________________________")
}

将printString声明成一个内联函数

inline fun printString(str:String,block:(String)->Unit){//定义printString()的高阶函数,用于lambda表达式中打印传入的字符串参数
    println("printString begin")
    block(str)
    println("printString end")
}
fun main() {
    println("main start")
    val str=""
    printString(str){ s->
        println("lambda start")
        if (s.isEmpty()) return//return代表返回外层的调用函数,也是main函数
        println(s)
        println("lambda end")
    }
    println("main end")
    println("_________________________________________________________")
}

注意

我们在高阶函数中创建了另外的Lambda或者匿名类的实现,并且在这些实现中调用函数类型参数,此时再将高阶函数声明成内联函数,就一定会提示错误

Kotlin快速入门_第1张图片

原因:内联函数的Lambda表达式中允许使用return关键字,和高阶函数的匿名类实现中不允许使用return关键字之间造成冲突

解决方法:加上crossinline关键字就能解决

inline fun runRunnable(crossinline block:()->Unit){
    val runnable =Runnable{
        block()
    }
    runnable.run()
}

crossinline:用于保证在内联函数的Lambda表达式中一定不会使用return关键字,解决原因之间的冲突,声明crossinline就无法在调用runRunnable 函数时的Lambda表达式中使用return关键字进行函数返回了,但是仍然可以使用return@runRunnable的写法进行局部返回

Git时间:初始版本控制工具

安装Git

安装地址

建立代码仓库

cd 项目的路径

在这里插入图片描述

如果不知道当前的git Bash 路径 可用pwd 命令就能看到

在这里插入图片描述

在项目目录下 输入 git init 就能完成创建代码仓库的操作

在这里插入图片描述

仓库创建完成后,会在项目的根目录下生成一个隐藏的.git目录,这个目录就是用来记录本地所有的Git操作的,可以通过ls -al查看一下

Kotlin快速入门_第2张图片

如果要删除本地仓库,只需要删除这个目录就行了

提交本地代码

add命令用于把想要提交的代码添加进来

commit则是真正执行提交操作

添加build.gradle文件

git add build.gradle

添加整个目录,只要在add后面加上目录名,比如要添加app目录下所有文件

git add app

添加所有文件,只需要在add后面加上一个点

git add .

添加文件完,进行提交,-m参数为提交的描述信息,没有描述信息的提交被认为是不合法的

git commit -m "First commit"

高阶函数的应用

简化SharedPreferences的用法

SharePreferences存储数据可分为3步:

1.调用SharePreferences的edit()方法获取SharedPreferences.Editor对象

2.向SharedPreferences.Editor对象中添加数据

3.调用apply()方法将添加的数据提交,完成数据存储操作

val editor=getSharedPreferences("data",Context.MODE_PRIVATE).edit()
editor.putString("name","Tom")
editor.putInt("age",28)
editor.putBoolean("married",false)
editor.apply()

简化后

SharedPreferences.kt

package com.example.a20200711gaojiehanshu

import android.content.Context
import android.content.SharedPreferences



    fun SharedPreferences.open(block: SharedPreferences.Editor.() -> Unit) {//通过扩展函数的方式向SharedPreferences类中添加一个open函数
        //而且它还接收一个函数类型的参数,open函数就是一个高阶函数
        val editor = edit()//open函数内拥有SharedPreferences的上下文,
        // 因此可以直接调用edit()方法来获取SharedPreferences.Editor对象
        editor.block()//对函数类型参数进行调用
        editor.apply()//提交数据
    }

    getSharedPreferences("data", Context.MODE_PRIVATE).edit {//直接在SharedPreferences对象上调用open函数
        putString("name", "Tom")//拥有SharedPreferences.Editor的上下文环境,可以直接调用相应的put方法来添加数据
        putInt("age", 28)
        putBoolean("married", false)
        //不需要调用apply()方法来提交数据,因为open函数会自动完成提交操作
    }

简化ContentValues的用法

ContentValues作用:结合SQLiteDatabase的API存储和修改数据库中的数据

val values=ContentValues()
values.put("name","Game of Thrones")
values.put("author","George Martin")
values.put("pages",720)
values.put("price",20.85)
db.insett("Book",null,values)

mapOf()函数允许我们使用"Apple to 1"这样的语法结构快速创建一个键值对,在kotlin中使用A to B 这样的语法结构会创建一个Pair对象

ContentValues.kt

package com.example.a20200711gaojiehanshu

import android.content.ContentValues
import androidx.core.content.contentValuesOf

//使用类似mapOf()函数的语法结构来创建ContentValues对象
/*
构建一个ContentValues对象
cvOf()方法接收一个Pair参数,使用A to B语法结构创建出来的参数类型
vararg对应java中的可变参数列表。我们允许向这个方法传入0个,1个,2个甚至多个pair类型的参数
这些参数都会被赋值到使用vararg声明的这一个变量上面,然后使用for-in循环可以将传入的所有参数遍历出来

 pair为键值对的数据类型,需要用泛型来指定键与值分别对应什么类型得数据,ContentValues的所有键都时字符串类型
 ContentValues的值的数据类型不固定,泛型用Any?来指定,Any是kotlin中所有类的共同基类,相当于java中的Object
 而Any?则表示允许传入空值
 */
fun cvOf(vararg pairs: Pair<String,Any?>): ContentValues {
    val cv=ContentValues()//创建一个ContentValues对象
    for (pair in pairs){//遍历Pairs参数列表
        val key =pair.first
        val value = pair.second
        when(value){//根据值的数据类型进行判断
            //取出其中的值,并填入ContentValues中,参数中传入的键值对逐个添加到ContentValues中
            //覆盖ContentValues所支持的所有数据类型
            //如果wehn 语句进入Int条件分支后,这个条件下面的value会被自动转为Int类型,而不再是Any?类型,在if语句同样适用
            is Int->cv.put(key, value)
            is Long->cv.put(key, value)
            is Short->cv.put(key, value)
            is Float->cv.put(key, value)
            is Double->cv.put(key, value)
            is Boolean->cv.put(key, value)
            is String->cv.put(key, value)
            is Byte->cv.put(key, value)
            is ByteArray->cv.put(key, value)
            null->cv.putNull(key)
        }
    }
    return cv//最终将ContentValues对象返回即可

}
val values= cvOf("name" to "Game of Thrones","author" to "George Martin","pages" to 720,"price" to 20.85)
db.insert("Book",null,values)

        /***********************使用高阶函数来进行优化**********************************/
        fun cvOf2(vararg pairs: Pair<String,Any?>)=ContentValues().apply {//apply函数的返回值就是它调用对象本身,可以使用单行代码函数的语法糖
            //用等号代替返回值的声明
            //apply函数的Lambda表达式中会自动拥有ContentValues的上下文,可以直接调用ContentValues的各种put方法

            for (pair in pairs){//遍历Pairs参数列表
                val key =pair.first
                val value = pair.second
                when(value){//根据值的数据类型进行判断
                    //取出其中的值,并填入ContentValues中,参数中传入的键值对逐个添加到ContentValues中
                    //覆盖ContentValues所支持的所有数据类型
                    //如果wehn 语句进入Int条件分支后,这个条件下面的value会被自动转为Int类型,而不再是Any?类型,在if语句同样适用
                    is Int->put(key, value)
                    is Long->put(key, value)
                    is Short->put(key, value)
                    is Float->put(key, value)
                    is Double->put(key, value)
                    is Boolean->put(key, value)
                    is String->put(key, value)
                    is Byte->put(key, value)
                    is ByteArray->put(key, value)
                    null->putNull(key)
                }
            }


        }
val values2= contentValuesOf("name" to "Game of Thrones","author" to "George Martin","pages" to 720,"price" to 20.85)
db.insert("Book",null,values2)

你可能感兴趣的:(Kotlin快速入门)