Android—Kotiln基础教程(六)

前言

在上一篇中,讲解了Kotlin关于对象的基础知识,在这一篇中,将会讲解Kotlin对应的对象,接口,抽象类相关的知识点。

1. 类的单例

1.1 示例一

object ApplicationConfig {
    
    

    init {
    
    
        println("loading config ...")
    }

    fun setSomeThing() {
    
    
        println("setSomething")
    }
}

fun main{
    
    
    ApplicationConfig.setSomeThing()
    println(ApplicationConfig) //
    println(ApplicationConfig)
    ApplicationConfig.setSomeThing()
}

我们这里看到,对象ApplicationConfig含有init初始化代码块,在main函数入口这,多次调用了ApplicationConfig对象。现在来看看运行效果:

loading config ...
setSomething
ApplicationConfig@63e31ee
ApplicationConfig@63e31ee
setSomething

从这个运行效果可知,当使用object修饰对象时,当前对象只能产生一个实例的类-单例!

既然如此,那如果说使用object修饰的对象继承了某个父类会怎样呢?

1.2 示例二

// 对象表达式
/*
* */
open class Player {
    
    
    open fun load() = "loading nothing"

    open fun add(one: Int, two: Int): Int {
    
    
        return one + two
    }
}

fun main{
    
    
    //对象表达式 ,类似于 设置按钮点击监听事件,用完就丢弃
    val p = object : Player() {
    
    
        override fun load() = "anonymous class load..."
        override fun add(one: Int, two: Int): Int {
    
    
            return one-two
        }
    }
    println(p.load())
    println(p.add(10, 20))
}

这里我们看到,定义了object对象继承了Player,并且重写了对应父类的方法,将原有逻辑改成了自己的。来看看运行效果:

anonymous class load...
-10

用这种方式,它的生命周期非常短,属于随用随丢的那种。

2. 类的嵌套

// 嵌套类
class Player2() {
    
    

    class Equipment(val name: String) {
    
    

        fun show() = println("equipment: $name")
    }

    fun battle() {
    
    
        Equipment("sharp knife").show()
    }
	//伴生对象,类似于java的static,里面闭包的方法可以直接通过类名.访问
    companion object {
    
    
        fun staticBattle() {
    
    
            Equipment("staticBattle knife").show()
        }
    }
}

fun main{
    
    
    //嵌套类
    Player2.Equipment("AK47").show()
    var p = Player2();
    p.battle();
    Player2.staticBattle()
}

这个使用了嵌套类,主要介绍的事,对象里面的方法有哪些方式能够访问。

普通方法battle,需要实例化Player2对象后才能访问;通过companion object闭包里的方法,可以直接通过类名.方法名的方式直接访问(类似java的static)

来看看运行效果:

equipment: AK47
equipment: sharp knife
equipment: staticBattle knife

这些都狠简单,直接过!

3. 数据类

数据类?那它有啥作用?

  • 数据类,是专门设计用来存储数据的类
  • 数据类提供了 toString的个性化实现
  • 符号默认情况下,比较对象就是比较他们的属性值,数据类提供了equals和hashCode的个性化实现
  • data 重写了 equals 和 hashCode 的实现

说了那么多!那它有啥使用条件?

  1. 数据类必须有至少带一个参数的主构造函数
  2. 数据类主构造函数的参数必须是val或var
  3. 数据类不能使用abstract、open、sealed和inner修饰符

概念说完了,实际体验一下。

3.1 示例一

data class Coordinate(var x: Int, var y: Int) {
    
    
    //坐标值是否正值
    val isInBounds = x >= 0 && y >= 0
}

fun main{
    
    
    println(Coordinate(1, 5))
    println(Coordinate(1, 5))
    println(Coordinate(1, 5) == Coordinate(1, 5)) //比较值
    println(Coordinate(1, 5) === Coordinate(1, 5)) //比较地址
}

这里看到使用也挺简单的,在main里面定义了两个值相同的Coordinate数据类,然后将它们的值和地址分别进行对等判断,来看看运行效果:

Coordinate(x=1, y=5)
Coordinate(x=1, y=5)
true
false

从这个运行效果可以看出,每次 创建的数据类,即使值相同,赋的初始值也相同,它们的地址都不相同。也就是说,每次创建数据类,它将开辟一个新的地址供自己使用。

3.2 示例二

data class Student(var name: String, var age: Int, val sex: Int) {
    
     //主构造函数
    var score = 10
    private val hobby = "music"
    var subject: String

    init {
    
    
        println("initializing student")
        subject = "math"
    }


    constructor(_name: String, _age: Int) : this(_name, _age, 1) {
    
     //第一个次构造
        score = 40
    }

    constructor(_name: String) : this(_name, 10, 2) {
    
     //第二个次构造
        score = 20
        subject = ""
    }

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

fun main{
    
    
    val s = Student("jack")
    println(s)
    var copy = s.copy("tom")
    println(copy)
    copy = s.copy("bob",30)
    println(copy)
}

这里使用了copy函数,将对象S的数据copy了一份。

注意看,代码里面除了主构造函数外,还额外定义了两个次构造函数。

先看运行效果:

initializing student
Student(name='jack', age=10, sex=2, score=20, hobby='music', subject='')
initializing student
Student(name='tom', age=10, sex=2, score=10, hobby='music', subject='math')
initializing student
Student(name='bob', age=30, sex=2, score=10, hobby='music', subject='math')

从这个运行效果可以看出:当使用copy复制对象时,将会copy里面的值将会带入主构造函数里,并非对应的次构造函数!

3.3 示例三

data class Coordinate2(var x: Int, var y: Int) {
    
    
    val isInBounds = x >= 0 && y >= 0

    operator fun plus(other: Coordinate2) = Coordinate2(x + other.x, y + other.y)

}
fun main{
    
    
    val c1 = Coordinate2(5, 6)
    val c2 = Coordinate2(10, 20)
    println(c1 + c2)
}

运行效果:

Coordinate2(x=15, y=26)

注意看,这里重载plus对应的操作符,于是乎Coordinate2具有加法运算。当使用数字符号+的时候就会调用对应plus方法。

4. 解构声明

在开始之前,要先明白何为解构?

还记得之前讲解过,可同时定义多个变量拆分集合/数组里面的每个元素么?

fun main
   val(a,b,c)= listOf("json", "jack", "jacky")
    println(a)
    println(b)
    println(c)
}

比如这样,定义a、b、c 分别拆分赋值集合里面的元素,这就是解构。当然这个是SDK帮我们封装好了。

那如果说,我们想要弄出这个格式该怎么办呢?

class PlayerScore(val experience: Int, val level: Int) {
    
    
    operator fun component1() = experience
    operator fun component2() = level
}

fun main{
    
    
    val (x,y)=PlayerScore(10,4)
    println(x)
    println(y)
}

这里我们看出:

  • 在Class对象里通过operator fun component1()指定了第一个变量的值和类型,
  • 通过operator fun component2()指定了第二个变量的值和类型
  • 同理operator fun component数字N() 指定了第N个变量的值和类型

注意:这里一定要按照顺序依次定义!

5. 枚举

5.1 示例一

//枚举类
enum class Direction {
    
    
    EAST,
    WEST,
    SOUTH,
    NORTH
}

fun main{
    
    
    println(Direction.EAST)
}

运行效果

EAST

这简单的不能再简单了,直接过!

5.2 示例二

data class Coordinate(var x: Int, var y: Int) {
    
    
  
}
enum class Direction2(private val coordinate: Coordinate) {
    
    

    EAST(Coordinate(5, -1)),
    WEST(Coordinate(1, 0)),
    SOUTH(Coordinate(0, 1)),
    NORTH(Coordinate(-1, 0));

    fun updateCoordinate(playerCoordinate: Coordinate): Coordinate {
    
    
    //使用 (Direction2.EAST对应的x,y与Coordinate对应的x,y进行对应的逻辑处理
        return Coordinate(playerCoordinate.x + coordinate.x, playerCoordinate.y + coordinate.y)
    }
}
fun main{
    
    
    println(Direction2.EAST.updateCoordinate(Coordinate(10,20)))
}

运行效果

Coordinate(x=15, y=19)

这也不怎么复杂,就是指定EAST枚举,将对应的x,y值与Coordinate里面的x,y值进行逻辑运算,最后再打印Coordinate对象。

5.3 示例三

enum class LicenseStatus {
    
    
    UNQUALIFIED,
    LEARNING,
    QUALIFIED;
}


class Driver(var status: LicenseStatus) {
    
    
    fun checkLicense(): String {
    
    
        return when (status) {
    
    
            LicenseStatus.LEARNING -> "在学习"
            LicenseStatus.QUALIFIED -> "有资格已毕业"
            LicenseStatus.UNQUALIFIED -> "没有资格"
        }
    }
}

fun main{
    
    
    var driver = Driver(LicenseStatus.QUALIFIED)
    println(driver.checkLicense())
}

运行效果

有资格已毕业

这个很简单,只要看了之前的语法,这应该能够一眼看出其意思!

但结合例子,思考一个场景:正常人在毕业后一般都会有个毕业编号啥的。但如果单靠枚举的话,只能确定这个人毕业了,并不能在确认的同时将毕业编号显示出来!但如果想要那种效果怎么办呢?

5.4 示例四(密封类)

sealed class LicenseStatus2 {
    
    
    object UnQualified : LicenseStatus2()
    object Learning : LicenseStatus2()
    class Qualified(val licenseId: String) : LicenseStatus2()
}

class Driver2(var status: LicenseStatus2) {
    
    
    fun checkLicense(): String {
    
    
        return when (status) {
    
    
            is LicenseStatus2.UnQualified -> "没有资格"
            is LicenseStatus2.Learning -> "在学习"
            is LicenseStatus2.Qualified -> "有资格,驾驶证编号:" +
                    "${
      
      (this.status as LicenseStatus2.Qualified).licenseId}"
        }
    }
}

fun main{
    
    
    var driver=Driver2(LicenseStatus2.Qualified("ds5464564"))
    println(driver.checkLicense())
}

注意这次并不是用的enum了,而是使用的sealed class,让它变成了密封类。当对应选项不需要参数时,可以使用object定义;当对应选项需要对应的属性值时,可以用class通过主构造函数将参数传入进去。

6. 接口

interface Moveable {
    
    
    val maxSpeed: Int
    var wheels: Int
    fun move(moveable: Moveable): String
}

class Car(_name: String, override var wheels: Int = 4) : Moveable {
    
    

    override val maxSpeed: Int
        get() = TODO("Not yet implemented")

    override fun move(moveable: Moveable): String {
    
    
        TODO("Not yet implemented")
    }


}

接口也挺简单的,和java几乎一致,就不过多演示了!

结束语

好了,本篇到这里就结束了!到现在为止,类的相关知识点已经讲解完毕!从下一篇开始将会讲解Kotlin对应的扩展以及函数式编程!

Guess you like

Origin blog.csdn.net/qq_30382601/article/details/121305304