当前位置: 首页 > article >正文

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

构造函数

主构造函数:

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

class Player(_name:String, _healthPoints:Int, _isBlessed:Boolean, _isImmortal:Boolean=false) 
{}

变量下划线前缀用来表示临时变量
主构造函数支持直接定义属性

class Player(name:String, var healthPoints:Int, val isBlessed:Boolean, private val isImmortal:Boolean)

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

次构造函数:

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

class Player(_name:String, _healthPoints:Int, _isBlessed:Boolean, _isImmortal:Boolean=false) 
{

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

}

一个class允许多个次构造函数
次构造函数需要满足主构造函数的条件
词构造函数支持定义初始化代码逻辑
比如山本作为boss,血条应该很满

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关键字

val player = Player("YunLong Li", 88, true, false)

因为在class中定义了次构造函数,并且在次构造函数中传入了几个参数
所以这里实例化class时只需要传入name就可以了

val player = Player("ShanBen")

初始化:

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

初始化块:

初始化块用于设置变量或值,以及执行有效性检查
比如检查传给构造函数的值是否有效等
初始化代码块会在构造类实例时执行
定义初始化块使用init{}

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

    }

属性初始化:

属性必须在构造类实例时完成初始化
属性初始化值可以是包括函数返回在内的任意类型匹配值

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

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

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
lateinit var alignment:String // 定义一个延时初始化属性
对于任何var属性声明,都可以加上lateinit关键字
这样编译器就会允许延后初始化属性
需要注意的是,必须在属性引用前初始化变量,否则就会抛出初始化异常
延迟初始化变量一旦完成初始化后,就跟其它变量咪什么区别了
    fun determineFate(){
        alignment = "Good"
    }

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

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

惰性初始化:

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

 Exception in thread "main" java.lang.NullPointerException: Cannot invoke "kotlin.Lazy.getValue()" because "<local1>" is null

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

无论如何,在定义一个初始化块或者使用属性之前,一定要确保块中的所有属性已经完成初始化赋值
否则会抛出空指针异常

理论上讲,给对象分配内存就是实例化对象,给对象赋值就是初始化对象
但通常,实例化倾向于仅仅创建一个类的实例,而初始化则是指为变量、属性或类实例变得可用的工作  


http://www.kler.cn/a/386039.html

相关文章:

  • HTML5实现俄罗斯方块小游戏
  • 软件设计师 - 第1章 计算机网络概论
  • Qt文件目录操作
  • 图像处理之cornerdetection(角点检测)综述
  • uniapp ios app以framwork形式接入sentry
  • javaWeb小白项目--学生宿舍管理系统
  • Vue3+axios+Vite配置Proxy代理解决跨域
  • 【spark面试题】RDD容错机制
  • 基于Jeecgboot3.6.3vue3的flowable流程增加online表单的审批支持(三)后端接口
  • 高效集成:聚水潭采购数据同步到MySQL
  • 【ARM Linux 系统稳定性分析入门及渐进 1.1 -- Crash 工具功能概述】
  • 浅谈智能家居在智慧养老实训室中的作用
  • 飞书 富文本(Markdown)
  • 【网络安全 | Java】AES加密算法
  • docker运行code-servre并配置https通信
  • 图神经网络(GNN)入门笔记(1)——图信号处理与图傅里叶变换
  • 语音识别ic赋能烤箱,离线对话操控,引领智能厨房新体验
  • 电脑中丢失 vcruntime140.dll 的五种解决方法
  • 【c语言】memcpy函数的使用和模拟实现
  • Linux-2
  • 2024年软件设计师中级(软考中级)详细笔记【12】软件系统分析与设计
  • 枚举及优化(二)
  • 「Mac玩转仓颉内测版2」入门篇2 - 编写第一个Cangjie程序
  • python实战(八)——情感识别(多分类)
  • Tomcat(3) Tomcat是哪种类型的服务器?
  • c语言学习8位运算