Android—Kotiln基础教程(五)

前言

在上一篇中主要讲解了Kotlin关于集合相关的知识点,在本篇中,将会对Kotlin的对象进行详解!

1. 对象

1.1 示例一(无构造函数)

class Player {
    
    
	//分析点1
    var name = "abc"
        get() = field.capitalize()
        set(value) {
    
    
            field = value.trim()
        }
    var age = 10  
        get() = field.absoluteValue
        private set(value) {
    
     
            field = value.absoluteValue
        }
    val rolledValue
    	//取随机数
        get() = (1 until 6).shuffled().first()

    var words: String? = null //分析点2

    fun saySomeThine() {
    
     
    	//分析点3
        words?.also {
    
     "Hello ${
      
      it.toUpperCase()}" }.run(::println)
    }
}
fun main{
    
    
    var player = Player()
    player.name = "tom"
    //player.age=-10 //因为set被改成私有,所以这里报错
    player.words="main hello" 
    println(player.name)
    println(player.age)
    println(player.rolledValue)
    player.saySomeThine()
}

定义了一个对象,里面有对应的成员属性。注意看这里面的分析点:

  • 分析点1:分别实现了对应的get、set方法,并且在对应的方法里实现了对应的逻辑。
  • 分析点2:该成员变量并没有任何get、set方法
  • 分析点3:在方法saySomeThine里,对成员属性使用了.also关键字,最后再打印

运行效果:

Tom
10
4
main hello

从这个运行效果可以看到:对象里的成员,就算不实现对应的get、set方法,并且在没有使用private情况下,在外部依旧可以调用对应的get、set方法

1.2 示例二(有主构造函数)

// ----------------初始化
class Player1(
    _name: String,
    _age: Int,
    _isNormal: Boolean) {
    
    
    var name = _name
        get() = field.toLowerCase()
        private set(value) {
    
    
            field = value.trim()
        }
    private var age = _age
    var isNormal = _isNormal

    fun playerInfo() {
    
    
    	//println(_name) //会报错
        println("name: $name; age:$age ;isNormal $isNormal")
    }
}

fun main() {
    
    
    var player = Player1("Tom", 20, true)
    player.playerInfo()

}

运行效果

name: tom; age:20 ;isNormal true

从这段代码看,这个Player1应该是一个拥有有参构造方法的对象。而对应的_name、_age、_isNormal,就是对应构造方法里的构造参数,有效期也仅在对应的构造方法里。

1.3 示例三(有主次构造函数)

class Player2(
    _name: String,
    var age: Int = 12, //执行顺序 1
    var isNormal: Boolean) {
    
    
    var name = _name //执行顺序2
        get() = field.capitalize()
        private set(value) {
    
    
            field = value.trim()
        }
    fun playerInfo() {
    
    
        println("name: $name; age:$age ;isNormal $isNormal")
    }
    //次构造函数【】 这里的this()相当于执行了主构造函数 相当于顺序1和2
    constructor(name: String) : this(name, age = 100, isNormal = false ) {
    
    
        this.name = name.toUpperCase()
        println("constructor----------")
        //执行顺序 4
    }
    init {
    
     // 执行顺序3
        //初始化代码块 会在构造类实例时执行
        // 则可以 在这进行 数值的检查 满足条件则抛出异常
        println("init----------")
        require(age > 10) {
    
     println("age must be positive") }
        require(name.isNotBlank()) {
    
     println("player must have a name") }
    }
}

fun main() {
    
    
    var player2=Player2("Tom", 20, true)
    player2.playerInfo()
    var player3 = Player2("bob", isNormal = false)
    player3.playerInfo()
    var player4=Player2("hqk")
    player4.playerInfo()
}

运行效果:

init----------
name: Tom; age:15 ;isNormal true
init----------
name: Bob; age:12 ;isNormal false
init----------
constructor----------
name: HQK; age:100 ;isNormal false

这次在代码里面加入了constructor以及initconstructor的意思就是创建一个次构造函数;而init相当于java里面的static代码块。里面对应的执行顺序在注释里标注清楚了。

init代码块里含有require方法,这里面的意思就是:当里面的表达式不成立/false时,将直接报错,自定义错误信息就在后面闭包里实现。

1.4 示例四(延迟初始化)

class Player3 {
    
    
    lateinit var equipment: String
    fun ready() {
    
    
        equipment = "sharp knife"
    }

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

fun main{
    
    
    var palyer3 = Player3()
    palyer3.ready()   //必须 调用该方法 才能使用对应的属性变量
    palyer3.battle()
}

运行效果:

sharp knife

在之前的学习中,我们认知到,当我们定义变量的时候,要么一开始赋初始值,要么通过.?表明这个变量为可空。但在对象的使用里,我既不想一开始赋初始值,也不想让这个变量为可空的,那就只有通过lateinit表明这个属性变量需要在后面初始化(使用它之前)!

那万一在使用它之前未初始化该怎么办呢?岂不是就直接报空指针异常了?Kotlin也提供了对应的解决方案:只要无法确认 lateinit 变量是否完成初始化,可以执行 isInitialized 检查

1.5 示例五(惰性初始化)


class Player4(_name: String) {
    
    
    var name = _name
    val config by lazy {
    
     loadConfig() }
    private fun loadConfig(): Boolean {
    
    
        println("loading...")
        return false
    }
}

fun main{
    
    
    var play4 = Player4("Tom");
    println(play4.config)
}

运行效果

loading...
false

这里我们看到,当定义成员属性时,使用了by lazy {}的方式,让config实现了惰性初始化。当使用play4.config成员属性时,它才会初始化相应的操作。

1.6 示例六(初始化陷阱)

class Player5() {
    
    
    init {
    
    
    	//这样会直接报错
        blood  = this.blood.times(4)
    }
    var blood = 100
}

当我们将变量放在init闭包后时,将会发现代码居然报了语法错误。解决方案也很简单,只需要将init闭包放在最后即可。

class Player6() {
    
    
    var blood = 100
    init {
    
    
    	//这样就不会报错
        blood  = this.blood.times(4)
    }
}

fun main{
    
    
   var play6=Player6()
   println(play6.blood )
}

运行效果:

400

1.7 示例七(对象的继承)

class Player7() {
    
    
    val name: String

    private fun firstLetter() = name[0]

    init {
    
    
        name = "Jack" //分析点1
        firstLetter()
    }
    //--------------继承----------------
    open class Product(val name: String) {
    
    	//分析点2
       open fun description() = "Product: $name"
        open fun load() = "Nothing..."
         fun add(num1: Int, num2: Int) = num1 + num2 //该方法不可被子类重写
    }
    class LuxuryProduct : Product("Java") {
    
    
        //---重载
        override fun load() = "Java " + description()
    }
}
/**
 * 类型转换
 */
fun sale(p: Player7.Product) {
    
    
    println(p.load())
}

fun main() {
    
    
    val play7 = Player7()
    println(play7.name)

	val product = LuxuryProduct()
    product.description().run(::println)
    product.load().run(::println)
    //类型检测
    println(product is LuxuryProduct)
    println(product is Player7.Product)

    sale(product)
    sale(product as Player7.Product) //分析点3
}

我们先看这几点分析点:

  • 分析点1:和上面示例一样,当init代码块在对应的属性定义下方时,可在这闭包里初始化对应变量,即使变量是用val修饰的,依然能够初始化。
  • 分析点2:当我们想是用继承时,可以通过open class XXX定义被继承的对象,里面的方法如果想要被子对象重写的话可使用open修饰对应的方法。子类继承父类语法为:class 子类 : 父类
  • 分析点3:当我们想进行对象的转换时,可使用as关键字强转对应的对象。

运行效果

Jack
Product: Java
Java Product: Java
true
true
Java Product: Java
Java Product: Java

结束语

好了,本篇到这里就结束了,本想着在这篇吧对象讲完的,奈何发现内容太丰富了。还有很多很多,加上时间不允许了,还要写那个代码撸猫,所以暂时放到下一篇了。

おすすめ

転載: blog.csdn.net/qq_30382601/article/details/121284314