前言
在上一篇中主要讲解了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
以及init
,constructor
的意思就是创建一个次构造函数;而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
结束语
好了,本篇到这里就结束了,本想着在这篇吧对象讲完的,奈何发现内容太丰富了。还有很多很多,加上时间不允许了,还要写那个代码撸猫,所以暂时放到下一篇了。