Android开发——kotlin语法基础2

目录

标准库函数

also

takeif

takeUnless

集合

List

List集合的遍历

解构语法过滤元素

Set

Map

读取Map的值

遍历Map

可变Map

Field

初始化顺序

验证

延迟初始化

惰性初始化

类型检测和转换

const关键字

const和val区别

Object关键字

对象表达式

伴生对象

嵌套类

数据类

Copy函数

解构声明

运算符重载

枚举类

代数数据类型(ADT)

密封类

接口

抽象类

泛型

定义泛型类

泛型函数

多泛型参数

泛型类型约束

关键字补充(vararg,infix,inline)

Vararg

Infix

inline


标准库函数

also

        also函数和let函数功能相似,和let一样,also也是把接收者作为值参传给lambda,但是有一点不同,also返回接收者对象,而let返回lambda结果,所以also更适合针对同一原始对象,利用副作用做事,可以基于原始接收者对象执行额外的链式调用

fun main() {
    var fileContest:List<String>
    File("D:/workspace/studyproject/untitled/src/main/resources/test.txt")
        .also {
            println(it.name)
        }.also {
            fileContest = it.readLines()//一行一行读取,返回list集合
        }
    println(fileContest)
}

takeif

        和其他标准函数有一点不一样,takeif函数需要判断lambda中提供的条件表达式,给出true或false结果,如果为true,则返回接收者对象,反之返回null,如果需要判断某个条件是否满足,再决定是否可以赋值变量或执行某项任务,takeif就非常有用,它类似于if语句,优势是可以直接在对象实例上调用,避免了临时变量赋值的麻烦

fun main() {
    val result = File("D:/workspace/studyproject/
                                   untitled/src/main/resources/test.txt")
        .takeIf {
            it.exists()&&it.canRead()
        }?.readText()//读整个文件,返回字符串
    println(result)
}

takeUnless

        takeUnless与takeif完全相反,只有给定条件结果为false时才会返回原始接收对象

集合

        和变量类型的var和val一样,kotlin中集合List,Set和Map的变量也分为两类,只读和可变

List

        List的创建是通过listof()创建只读集合,mutableList()创建可变集合,可以增,删,改只读与可变能够相互转化,通过.toList().toMutableList()

        get()根据索引取值

        getOrElse是一个安全索引取值函数,它需要两个参数,一个是索引值,第二个是能提供默认值的Lambda表达式,如果索引值不存在的话,返回Lambda表达式结果,可用来代替异常

        getOrNull是kotlin提供的另一个安全索引函数,它返回null结果,而不是抛出异常

//list取值方式
fun main() {
    var listOf = listOf("Jason", "Jack", "Rose")//不可变集合
    var mutableListOf = mutableListOf("a", "b", "c")//可变集合
    //增删改
    mutableListOf.set(0,"c")
    mutableListOf.add("d")
    mutableListOf.remove("b")
    //取值方式
    println(mutableListOf[0])
    println(listOf[1])
    println(listOf.get(1))
    println(listOf.getOrElse(3,{"抛出异常,索引不存在"}))
    println(listOf.getOrNull(3))
    //可变与不可变的相互转化
    listOf.toMutableList()
    mutableListOf.toList()
}

添加元素运算符,删除元素运算符(类似C++中的运算符重载)

mutableListOf += "王五"
println(mutableListOf)
mutableListOf -= "c" //只删去一个c
println(mutableListOf)
//基于lambda表达式指定的条件删除元素
mutableListOf.removeIf { it.contains("c") }


List集合的遍历

//集合的遍历
    var nameList = mutableListOf("孙伟", "张帅", "李磊",
                                         "赵瑜", "刘静", "夏天", "元宝")
    //for-in
    for (name in nameList) {
        println(name)
    }
    //forEach
    nameList.forEach{
        println(it)
    }
    //forEachIndexed 遍历时要获取索引
    nameList.forEachIndexed { index, name ->
        println("$index : $name")
    }

解构语法过滤元素

        list集合可以一次性为多个元素赋值,如果想只为特定位置的元素赋值的话,需要把不需要赋值的元素用下划线 '_' 替换,例

// _ 解构语法过滤元素
    var(a,b,_,d,e)=nameList
    println("$a $b $d $e")

java源码对照:可以看到将2号下标跳过赋值

Set

        与Java一样不允许有重复元素,并且无序,通过setof创建不可变,mutableSetOf创建可变,elementAt获取,安全获取与List相似。

var set = setOf("a", "a", "C")
println(set.elementAt(1))//
因为不可以重复,1号位是C

List也可以通过toSet()转换成Set,做去重操作。kotlin中还提供distinct()函数做去重

println(listOf("java","java","python").distinct())

Map

        Map集合创建时会用到一个 to,像个关键字,事实上它是个省略了点号和参数的特殊函数,to函数将他左边和右边的值转化成一对 Pair,也可使用Pair(Key , Value)的方式创建

//kotlin中Map集合的创建
var map1 = mapOf(1 to "李磊", 2 to "赵刚", 3 to "彭于晏")
var map2 = mapOf(Pair(1, "Java"), Pair(1, "VUE"),Pair(2,"Python"),Pair(3,"C++"))

读取Map的值

println(map1[2])//[]取值运算符,读取对应键值对的值,如果键不存在就返回null
println(map1.get(5))//读取对应键值对的值,如果键不存在就返回null
println(map1.getValue(1))//读取对应键值对的值,如果键不存在就抛出异常
println(map1.getOrElse(5,{ 1>=6 }))//不存在就返回匿名函数结果
println(map1.getOrDefault(5,"默认值"))//没有就返回默认值

遍历Map

for (i in map2){
    println(i)
}
map2.forEach {
    println("${it.key}=${it.value}")
}
map2.forEach { (t, u) ->
    println("$t=$u")
}

可变Map

fun main() {
    //mutableMapOf 创建可变Map集合
    var mutableMapOf = mutableMapOf(1 to "李磊", 2 to "赵刚", 3 to "彭于晏")
    //添加
    mutableMapOf += 4 to "潘安"
    mutableMapOf[5] = "武松"
    mutableMapOf.put(6,"西门")
    println(mutableMapOf)
}

Field

        针对类中定义的每一个属性,Kotlin都会产生一个field,一个getter和一个setter,field用来储存属性数据,你不能直接定义field,kotlin会封装field,保护它里面的数据,只暴露给gettersetter使用

        尽管kotlin会自动提供默认的getter和setter方法,但在需要控制如何读写属性数据时,还是应手动自定义他们

var aaa = "aaa"
    get() = field.uppercase()//将属性值转大写返回
    set(value) {
        field =value.trim()//将参数值格式化后赋给属性
    }

注意:kotlin中临时变量名用下划线开头 如:

class Player( _name String ){  var name = _name }

初始化顺序

  1. 主构造函数里声明的属性
  1. 类级别的属性赋值和init初始化块里的属性赋值/函数调用是同级别的,按从上之下执行
  1. 次构造函数里的属性赋值和函数调用

验证

class InitOrder(_name:String,val age:Int) {
    //类级属性,age属于主构造函数里声明的属性
    var name = _name
    var score:Int = 10
    private val hobby = "music"
    val subject : String
   
    init {
        println("==========初始化块执行了========")
        subject = "math"
    }
    constructor(name:String):this(name,10){
        score = 20
    }

    override fun toString(): String {
        return "InitOrder(age=$age, name='$name', score=$score, hobby='$hobby', subject='$subject')"
    }

}

Java源码对照:可以看到init块是构造对象时运行,并且在次级构造之前,主构造和类级属性赋值之后执行

延迟初始化

        指属性在用到时才被初始化化,kotlin中使用lateinit关键字相当于做了一个约定;在用它之前负责初始化,如果lateinit修饰的变量在未被初始化前使用,会抛出异常

class LateInitTest {
    lateinit var equipment:String
    fun ready(){
        equipment = "AK-47"
    }
    fun battle(){
        println(equipment)
    }
}
fun main(){
    var lateProperty = LateInitTest()
    //下面代码打开就不会抛异常,而会输出AK-47
    //lateProperty.ready()  
    lateProperty.battle()
}

只要无法确认lateinit变量是否完成初始化,可以执行isInitialized检查,如下buttle()中代码替换

fun battle(){
    if (::equipment.isInitialized){
        println(equipment)
    }
}

惰性初始化

        使用lateinit定义的属性在调用之前还是需要手动初始化(如:调用ready()),而惰性初始化是在声明变量时通过 by lazy 定义好初始化函数,只要用到这个属性就调用这个函数,不需要手动调用函数进行初始化,但是此属性变量只能设置val

class ByLazyTest(_name:String) {
    var name = _name
    val config by lazy {
        println("loading...........")
    }
}

fun main() {
    var p = ByLazyTest("王五")
    p.config
}

类型检测和转换

        kotlin中is运算符可以用来检测某个对象的类型,通过as关键字进行类型转换

var 元宝 :Animal = Dog("旺旺","狗","元宝","公")
 if (元宝 is Dog){//判断元宝是不是Dog类型
    元宝 as Dog  //如果是就进行转换
}

const关键字

        在kotlin语法中,修饰符var用来修饰可变变量,val修饰只读变量

        但是Kotlin同时又提供了一个const修饰符。kotlinconst只能用在顶级属性,以及object对象的属性中(伴随对象也是obejct)。它只能修饰val,在开发过程中,如果我们在伴生对象中定义了一个val类型的变量,那么Android Studio会智能的提示开发者需要使用const来修饰该变量。

constval区别

  • const val 可见性为public final static,可以直接访问。val 可见性为private final static,并且val 会生成方法getNormalObject(),通过方法调用访问。
  • 当定义常量时,出于效率考虑,我们应该使用const val方式,避免频繁函数调用。

Object关键字

        使用Object关键字,你可以定义一个只能产生一个实例的类——单例,类名又是对象名,可以直接调用方法

object SingletonTest {
    init {
        println("ApplicationConfig Loading......")
    }
    fun doSomeThing(){
        println("Object Test")
    }
}

fun main() {
    SingletonTest.doSomeThing()//SingletonTest即是类名又是对象名,可以直接调用方法
}

对象表达式

        有时候你不一定非要定义一个新的命名类不可,也许只需要某个现有类的一种变体实例,但只需要用一次就行了,事实上,对于这种用完就丢的类实例,命名也可以省略,这个对象表达式是XX的子类,这个匿名类依然遵循object关键字的一个规则,即一旦实例化,该匿名类只能有唯一一个实例存在

open class father{
    open fun load() = "loading someThing"
}

fun main() {
    var p = object : father(){
        override fun load(): String {
            return "子类重写load()"
        }
    }
    println(p.load())
}

伴生对象

                如果想将某个对象的初始化和一个类实例捆绑在一起,可以考虑使用伴生对象,使用companion修饰符,你可以在一个类定义里声明一个伴生对象,一个类里只能有一个伴生对象,类似静态static

class SingletonTest03 {
    companion object{
        private const val PATH = "xxxxxx"
        fun load() = File(PATH).readBytes()
    }
}
fun main(){
    //伴生对象函数可以通过类名直接调用
    SingletonTest03.load()
}

嵌套类

        如果一个类只对另一个类有用,那么将其嵌入到该类中并使这两个类保持在一起

class InnerClassTest {
    class Equipment(var name:String){
        fun show(){
            println(name)
        }
    }
}

fun main() {
    //kotlin中可以直接通过类名.内部类名调用,JAVA中实现这种操作的前提是内部类为静态
    InnerClassTest.Equipment("knife").show()
}

数据类

        数据类提供了个性化的toString()并且重写了Hashcode()和equals()方法

kotlin == 比较内容默认通过equals()比较,===比较引用,但是Any超累的equals()默认实现是用===比较,所以一个类不重写equals时使用==默认还是===比较

Copy函数

格式:对象名.copy(构造参数)

如果有属性在次构造函数中赋值,那么copy后的对象这个属性就不会赋值,因为copy时不执行次构造函数,即使用次构造函数copy

解构声明

        解构声明的后台实现就是声明component1,component2等若干个组件函数,让每个函数负责管理你想返回的一个属性数据,如果你定义一个数据类,它会自动为所有定义在主构造函数的属性添加对应的组件函数

class Student(val name:String,val sex:String,val age:Int) {
    //从1开始计数
    operator fun component1()= name
    operator fun component2()= sex
    operator fun component3()= age
}
fun main() {
    val (x,y,_) = Student("张三","男",1)
}

运算符重载

        如果要将内置运算符应用在自定义类身上,必须重写运算符函数,该函数使用 operator 修饰符,告诉编译器该如何操作自定义类

class OperatorTest (var num : Int){
    //重写plus加法运算符
    operator fun  plus(other: OperatorTest): Int {
        return this.num + other.num
    }
}

fun main() {
    var one1 = OperatorTest(1)
    var one2 = OperatorTest(1)
    println(one1+one2)//调用重写后的plus()
}

函数汇总:

枚举类

        在kotlin中定义枚举需要在class前加enum关键字修饰,kotlin中枚举类可以加构造函数和普通函数

enum class EnumTest (private val coordinate:Coordinate){
    //每一个方向都是实例对象
    EAST(Coordinate(2,0)),
    WEST(Coordinate(4,0)),
    NORTH(Coordinate(0,8)),
    SOUTH(Coordinate(0,10));
    //枚举类定义函数 传参坐标与实例坐标相加
    fun updataCoordinate(playerCorrdinate:Coordinate)=
        Coordinate((playerCorrdinate.x+coordinate.x),(playerCorrdinate.y+coordinate.y))
}

fun main() {
    println(EnumTest.EAST is EnumTest) //判断是不是实例
    println(EnumTest.NORTH.updataCoordinate(Coordinate(1,1)))
}

代数数据类型(ADT

        指可以用来表示一组子类型的闭集,枚举类就是一种简单的ADT

enum class EnumTest02 {
    UNQUALIFIED,
    LEARNING,
    QUALIFIED
}
class Driver(var status:EnumTest02){
    fun checkLicense():String{
        return when(status){
            EnumTest02.UNQUALIFIED -> "没资格"
            EnumTest02.LEARNING -> "在学"
            EnumTest02.QUALIFIED -> "有资格"
        }
    }
}
fun main() {
    println(Driver(EnumTest02.QUALIFIED).checkLicense())
}

密封类

        对于更复杂的ADT,可以使用Kotlin的密封类(sealed class),来实现更复杂的定义,密封类可以用来定义一个类似于枚举类的ADT,但你可以更灵活的控制某个子类型。密封类可以有若干个子类,要继承密封类,这些子类必须和他定义在同一个文件里

sealed class SealClass {
    //下面两个状态类无属性,设置为单例
    object UnQualified : SealClass()
    object Learning : SealClass()
    class Qualified(val id : String) : SealClass()
}
class Driver2(var status:SealClass){
    fun checkLicense():String{
        return when(status){
           is SealClass.UnQualified -> "没资格"
           is SealClass.Learning -> "在学"
           is SealClass.Qualified-> "有资格,id编号为:
                                   ${(this.status as SealClass.Qualified).id}"
        }
    }
}

fun main() {
    var driver2 = Driver2(SealClass.Qualified("123456"))
    println(driver2.checkLicense())
}

接口

        kotlin中规定所有重写的接口属性和函数实现都要使用override关键字,接口中定义的函数并不需要open关键字修饰,他们默认就是open

interface InterfaceTest {
    var maxSpeed:Int
    var brand :String
    fun move():String
}
class Car(override var maxSpeed: Int, override var brand: String):InterfaceTest {
    override fun move(): String {
        return "这台${brand}的速度是:${maxSpeed}公里/h"
    }
}
fun main() {
    println(Car(20,"奔驰").move())
}

kotlin中的接口属性或者方法可以有默认实现

interface InterfaceTest {
    //kotlin可以在接口里提供默认属性的getter方法和函数实现
    val maxSpeed
        get() = (0..500).shuffled().last()
    var brand :String
    fun move():String{
        return "接口默认实现"
    }
}
class Car():InterfaceTest {
    override var brand: String
        get() = TODO("Not yet implemented")
        set(value) {}
}
fun main() {
    var car :InterfaceTest = Car()
    println("${car.maxSpeed}>>>>${car.move()}")
}

抽象类

kotlin中抽象类概念和Java中没有区别,只是定义时在class前加abstract修饰,重写抽象函数要在前加override

abstract class AbstractTest(var range:Int) {
    abstract fun trigger():String
}
class AK47(_price:Int):AbstractTest(100){
    var price= _price
    //重写抽象类方法
    override fun trigger(): String {
        return "${price}"
    }
}
  • 在Kotlin中的抽象类在顶层定义的时候只能使用public可见性修饰符修饰抽象类不能直接被实例化
  • 抽象类中可以定义内部抽象类
  • 抽象类可以继承自一个继承类,即抽象类可以作为子类。不过,抽象类建议不用open修饰符修饰,因为可以覆写抽象类的父类的函数
  • 若要实现抽象类的实例化,需要依靠子类采用向上转型的方式处理

泛型

定义泛型类

        泛型类的构造函数可以接受任何类型,泛型参数常用字母T表示(代表英文type)

class Animal<T>(item:T) {
    var subject:T =item
    override fun toString(): String {
        return "${subject.toString()}"
    }

}
class Dog(var name:String,var age:Int){
    override fun toString(): String {
        return "Dog(name='$name', age=$age)"
    }
}
class Cat(var weight:Int){
    override fun toString(): String {
        return "Cat(weight=$weight)"
    }
}
fun main() {
    println(Animal<Dog>(Dog("旺财",12)))
    println(Animal<Cat>(Cat(12)))
}

泛型函数

class MagicBox<T>(item:T) {
    var available = false
    var subject = item
    fun fatch():T?{//返回可空的T类型
        return subject.takeIf { available } //takeif标准函数true返回接收对象
    }
}
class Boy(var name:String,var age:Int)
fun main{
    var magicBox = MagicBox<Boy>(Boy("小米", 15))
    magicBox.available = true
    println(magicBox.fatch() ?.run {
        "you find ${name}"
    })
}

多泛型参数

class MagicBox<T>(item:T) {
    var subject = item
    fun <R> fatch(subjectFun:(T)-> R):R?{
        return subjectFun(subject) //调用匿名函数并将subject传入
    }
}
class Boy(var name:String,var age:Int)
class Man(var name: String,var age: Int){
    override fun toString(): String {
        return "Man(name='$name', age=$age)"
    }
}
fun main() {
    var man =MagicBox<Boy>(Boy("李磊",15)).fatch {
        Man(it.name,it.age+15)
    }
    println(man)
}

泛型类型约束

T:父类 来约束,表示泛型T只能传入指定父类的子类

//约束泛型类 MagicBox
class MagicBox<T:Human>(item:T) {
}
//指定父类 Human
open class Human(val age: Int)
//可传入的子类 Boy Man
class Boy(var name:String,age:Int):Human(age)
class Man(var name: String,age:Int):Human(age){
    override fun toString(): String {
        return "Man(name='$name', age=$age)"
    }
}

关键字补充(vararg,infix,inline

Vararg

修饰形参,表明该参数是个数可变的形参。类似Java中 ...

//可变长参数函数
//函数的变长参数可以用 vararg 关键字进行标识:
fun vars(vararg v:Int){
    var arr = v //v为Array数组类型
    for(vt in v){
        print(vt)
    }
}

Infix

        标有infix关键字的函数可以使用中缀表示法(忽略该调用的点与圆括号)调用,中缀函数必须满足以下要求:

  1. 它们必须是成员函数和扩展函数
  1. 它们必须只有一个参数
  1. 其参数不得接受可变数量的参数,而且不能有默认值
infix fun Int.plus(x: Int): Int =
        this.plus(x)

// 可以中缀表示法调用函数
1 plus 2    //until 和 to就是中缀表示法的一种

// 等同于这样
1.plus(2)

inline

当调用一个inline function的时候,编译器不会产生一次函数调用,而是会在每次调用时,将inline function中的代码直接嵌入到调用处。(正常的是利用引用去寻找我们要调用的函数)

       inline 在一般的方法是标注,是不会起到很大作用的,inline 能带来的性能提升,往往是在参数是 lambda 的函数上

fun meth(){
     println("aaaa")
     meth1()
 }
 
 inline fun meth1(){
     println("bbbb")
 }
 
 //编译之后会变成
 fun meth(){
     println("aaaa")
     println("bbbb")
 }

猜你喜欢

转载自blog.csdn.net/yb941693493/article/details/127833259