Kotlin-面向对象之构造函数、实例化和初始化

构造函数

主构造函数:

主构造函数直接在class头部用小括号定义

Kotlin 复制代码
class Player(_name:String, _healthPoints:Int, _isBlessed:Boolean, _isImmortal:Boolean=false) 
{}

变量下划线前缀用来表示临时变量

主构造函数支持直接定义属性

Kotlin 复制代码
class Player(name:String, var healthPoints:Int, val isBlessed:Boolean, private val isImmortal:Boolean)

构造函数内定义的属性,只能使用默认getter和setter,无法自定义

次构造函数:

次构造函数对应主构造函数

词构造函数要么直接调用主构造函数(传入它需要的值参),要么通过调用另一个次构造函数简介调用主构造函数

Kotlin 复制代码
class Player(_name:String, _healthPoints:Int, _isBlessed:Boolean, _isImmortal:Boolean=false) 
{

    constructor(name:String) : this(
        name,
        _healthPoints = 100,
        _isBlessed = true,
        // 如果主构造函数定义的属性有默认值,那么次构造函数可以不用给它传值
        //_isImmortal=false // 这里与默认值相同,所以可以省略
    ) // 定义次构造函数

}

一个class允许多个次构造函数

次构造函数需要满足主构造函数的条件

词构造函数支持定义初始化代码逻辑

比如山本作为boss,血条应该很满

Kotlin 复制代码
class Player(_name:String, _healthPoints:Int, _isBlessed:Boolean, _isImmortal:Boolean=false) 
{

    constructor(name:String) : this(
        name,
        _healthPoints = 100,
        _isBlessed = true,
        // 如果主构造函数定义的属性有默认值,那么次构造函数可以不用给它传值
        //_isImmortal=false // 这里与默认值相同,所以可以省略
    ){
        if(name.uppercase()=="SHANBEN") healthPoints=300
    }


}

次构造函数不能像主构造函数那样定义雷属性,

实例化:

​​​​​​调用一个类的构造函数,就创建了一个该类的实例,这个过程叫做实例化

kotlin实例化一个class kotlin实例化类不需要new关键字

Kotlin 复制代码
val player = Player("YunLong Li", 88, true, false)

因为在class中定义了次构造函数,并且在次构造函数中传入了几个参数

所以这里实例化class时只需要传入name就可以了

Kotlin 复制代码
val player = Player("ShanBen")

初始化:

初始化变量、属性或类实例,就是给它赋初始值。

初始化块:

初始化块用于设置变量或值,以及执行有效性检查

比如检查传给构造函数的值是否有效等

初始化代码块会在构造类实例时执行

定义初始化块使用init{}

Kotlin 复制代码
    init{
        require(healthPoints > 0, { "healthPoints must be greater than zero." }) // 初始化块检查属性时,必须在被检查属性定义后
        require(name.isNotBlank(), { "Player must have a name." })

    }

属性初始化:

属性必须在构造类实例时完成初始化

属性初始化值可以是包括函数返回在内的任意类型匹配值

Kotlin 复制代码
// val hometown: String = selectHometown().shuffled().first()
*如果某个属性需要复杂的初始化逻辑,应该将其考虑放到函数或者初始化块中处理
val hometown:String by lazy { selectHometown()}
private fun selectHometown():String = listOf("BeiJing","ShanXi").shuffled().first()

属性必须初始化这一要求并不适用于函数这种作用域较小的变量

比如下面这个

Kotlin 复制代码
private fun isHero(){
        val hero:Boolean
        hero=true
        
    }

hero变量在被引用前会完全赋值,所以代码能正常运行

但是kotlin对属性有严格初始化要求,因为如果属性所在的类是public,其他类都有可能使用它们

而函数的作用于仅局限于函数内部,外部代码并不会接触到它们

所以这个hero无效的

复制代码
属性初始化方式有很多:
1、在主构造函数中初始化
2、在声明时初始化
3、在次构造函数中初始化
4、在初始化块中初始化
既然函数有这么多初始化方式,同一属性就有可能会在很多个地方被调用,
所以初始化顺序就非常重要
属性初始化顺序图:
1、主构造函数中声明的属性
2、类级别的属性赋值
3、init初始化块中的属性赋值和函数调用
4、次构造函数中的属性赋值和函数调用
不过,初始化块init{}和类级别的属性赋值顺序取决于定义的先后。
因此如果要在初始化块中初始化属性值。需要先定义被初始化的属性
因为java基本类型int的默认值是0,所以如果属性int类型值为0,那么编译器为了优化初始化代码,就会忽略该属性

延迟初始化:

复制代码
无论何时,构建类实例时,类属性都必须完成初始化
这是kotlin非空安全系统的重要组成部分
这意味着,当类的构造函数被调用时,class中所有非空属性都能以非空值完成初始化
也就是说
当某个类一旦完成实例化,就可以直接调用实例对象中的任何属性
延迟初始化就是为了规避这条规则而产生的
因为如何调用构造函数、或者什么时候调用,有时很难掌控
比如在class中定义了一个window界面属性
而界面内容需要其它属性数据支撑,这种情况就需要延时初始化
延时加载关键字 lateinit
Kotlin 复制代码
lateinit var alignment:String // 定义一个延时初始化属性
复制代码
对于任何var属性声明,都可以加上lateinit关键字
这样编译器就会允许延后初始化属性
需要注意的是,必须在属性引用前初始化变量,否则就会抛出初始化异常
延迟初始化变量一旦完成初始化后,就跟其它变量咪什么区别了
Kotlin 复制代码
    fun determineFate(){
        alignment = "Good"
    }

    fun proclaimFate(){
        // kotlin提供了一个isInitialized检查延迟初始化变量是否完成初始化
        if(::alignment.isInitialized) println(alignment)
        
    }

不过建议少用isInitialized检查,

因为如果对于每个延迟初始化变量都检查一遍,那么这个强大的功能就显得跟可空性变量没什么区别了

惰性初始化:

复制代码
惰性初始化可以看做是延迟初始化的一个补充
惰性初始化可以暂时不初始化某个变量
直到首次使用它
惰性初始化能让代码执行更有效率
例如,在某个复杂的class中,往往需要实例化多个对象,以及读取文件这样的计算密集型任务
当某个属性需要触发大量这类任务,但该属性又暂时没有class需要用到它时,惰性加载就非常有用了
惰性初始化在kotlin中式使用代理机制来实现的
代理负责约定属性该如何初始化
使用代理需要用到by关键字
kotlin中有一些预先配置好的代理可用
比如lazy
Kotlin 复制代码
    val hometown:String by lazy {
     selectHometown() // 当hometown首次被引用时,by lazy才会被初始化, lambda中的所有代码都会执行一次且仅执行一次,下次引用hometown时,直接使用缓存结果
    }
复制代码
惰性初始化同样需要在属性引用前初始化变量,否则就会抛出初始化异常
Kotlin 复制代码
 Exception in thread "main" java.lang.NullPointerException: Cannot invoke "kotlin.Lazy.getValue()" because "<local1>" is null

惰性初始化虽然有用,但代码实现稍微繁琐,建议在处理计算密集型任务时使用

无论如何,在定义一个初始化块或者使用属性之前,一定要确保块中的所有属性已经完成初始化赋值

否则会抛出空指针异常

理论上讲,给对象分配内存就是实例化对象,给对象赋值就是初始化对象

但通常,实例化倾向于仅仅创建一个类的实例,而初始化则是指为变量、属性或类实例变得可用的工作

相关推荐
用余生去守护9 分钟前
python报错系列(16)--pyinstaller ????????
开发语言·python
数据小爬虫@13 分钟前
利用Python爬虫快速获取商品历史价格信息
开发语言·爬虫·python
向宇it15 分钟前
【从零开始入门unity游戏开发之——C#篇25】C#面向对象动态多态——virtual、override 和 base 关键字、抽象类和抽象方法
java·开发语言·unity·c#·游戏引擎
带电的小王25 分钟前
WhisperKit: Android 端测试 Whisper -- Android手机(Qualcomm GPU)部署音频大模型
android·智能手机·whisper·qualcomm
莫名其妙小饼干32 分钟前
网上球鞋竞拍系统|Java|SSM|VUE| 前后端分离
java·开发语言·maven·mssql
梦想平凡38 分钟前
PHP 微信棋牌开发全解析:高级教程
android·数据库·oracle
十年一梦实验室41 分钟前
【C++】sophus : sim_details.hpp 实现了矩阵函数 W、其导数,以及其逆 (十七)
开发语言·c++·线性代数·矩阵
最爱番茄味1 小时前
Python实例之函数基础打卡篇
开发语言·python
元争栈道1 小时前
webview和H5来实现的android短视频(短剧)音视频播放依赖控件
android·音视频
Oneforlove_twoforjob1 小时前
【Java基础面试题033】Java泛型的作用是什么?
java·开发语言