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

Kotlin 2.1.0 入门教程(十四)类、构造函数、对象、抽象类

Kotlin 中,类使用 class 关键字进行声明:

class Person { /*...*/ }

类声明由类名、类头(指定其类型参数、主构造函数以及其他一些内容)和用花括号括起来的类体组成。类头和类体都是可选的;如果类没有类体,则可以省略花括号。

class Empty

构造函数

Kotlin 中,一个类有一个主构造函数,可能还有一个或多个次构造函数。

主构造函数在类头中声明,它跟在类名和可选的类型参数之后。

class Person constructor(firstName: String) { /*...*/ }

如果主构造函数没有任何注解或可见性修饰符,那么 constructor 关键字可以省略:

class Person(firstName: String) { /*...*/ }

主构造函数用于在类头中初始化类的实例及其属性。类头中不能包含任何可执行代码。如果你想在对象创建期间运行一些代码,可以在类体中使用初始化块。初始化块使用 init 关键字声明,后面跟着花括号。你可以将任何想要运行的代码写在花括号内。

在实例初始化期间,初始化块会按照它们在类体中出现的顺序执行,并且会与属性初始化器穿插执行。

class InitOrderDemo(name: String) {
    val firstProperty = "First property: $name".also(::println)
    
    init {
        println("First initializer block that prints $name")
    }
    
    val secondProperty = "Second property: ${name.length}".also(::println)
    
    init {
        println("Second initializer block that prints ${name.length}")
    }
}

fun main() {
    // First property: demo
    // First initializer block that prints demo
    // Second property: 4
    // Second initializer block that prints 4
    InitOrderDemo("demo")
}

主构造函数的参数可以在初始化块中使用。它们也可以在类体中声明的属性初始化器中使用:

class Customer(name: String) {
    val customerKey = name.uppercase()
}

Kotlin 拥有一种简洁的语法,可用于声明属性并通过主构造函数对其进行初始化:

class Person(val firstName: String, val lastName: String, var age: Int)

此类声明还可以包含类属性的默认值:

class Person(val firstName: String, val lastName: String, var isEmployed: Boolean = true)

和普通属性非常相似,在主构造函数中声明的属性可以是可变的(使用 var),也可以是只读的(使用 val)。

如果构造函数带有注解或可见性修饰符,那么就需要使用 constructor 关键字,并且这些修饰符要放在该关键字之前:

class Customer public @Inject constructor(name: String) { /*...*/ }

次构造函数

一个类还可以声明次构造函数,次构造函数要以 constructor 作为前缀:

class Person(val pets: MutableList<Pet> = mutableListOf())

class Pet {
    constructor(owner: Person) {
        owner.pets.add(this)
    }
}

fun main() {
    val person = Person()
    val pet1 = Pet(person)
    val pet2 = Pet(person)
    println(person.pets.size) // 2
}

如果类有主构造函数,每个次构造函数都需要直接或间接地通过其他次构造函数委托给主构造函数。委托给同一个类的其他构造函数使用 this 关键字来完成:

class Person(val name: String) {
    val children: MutableList<Person> = mutableListOf()

    constructor(name: String, parent: Person) : this(name) {
        parent.children.add(this)
    }
}

初始化块中的代码实际上会成为主构造函数的一部分。对主构造函数的委托会在访问次构造函数的第一条语句时发生,因此所有初始化块和属性初始化器中的代码会在次构造函数体执行之前执行。

class InitOrderDemo(name: String) {
    val firstProperty = "111: $name".also(::println)
    
    init {
        println("222: $name")
    }
    
    val secondProperty = "333: ${name.length}".also(::println)
    
    init {
        println("444: ${name.length}")
    }
    
    constructor(i: Int, name: String) : this(name) {
        println("555: $i, $name")
    }
}

fun main() {
    // 111: demo
    // 222: demo
    // 333: 4
    // 444: 4
    // 555: 1, demo
    InitOrderDemo(1, "demo")
    
    // 111: demo
    // 222: demo
    // 333: 4
    // 444: 4
    InitOrderDemo("demo")
}

即使类没有主构造函数,委托仍然会隐式发生,并且初始化块仍然会执行:

class Demo {
    init {
        println("111")
    }
    
    constructor(i: Int) {
        println("222")
    }
}

fun main() {
    // 111
    // 222
    Demo(1)
}

如果一个非抽象类没有声明任何构造函数(主构造函数或次构造函数),它将生成一个无参数的主构造函数。该构造函数的可见性将是公共的(public)。

如果你不希望你的类拥有 public 构造函数,可以声明一个具有非默认可见性的空主构造函数,示例如下:

class DontCreateMe private constructor() { /*...*/ }

在上述示例中,DontCreateMe 类的构造函数被声明为私有(private),这意味着该类的构造函数只能在类的内部被调用,外部代码无法直接创建 DontCreateMe 类的实例。

JVM 平台上,如果主构造函数的所有参数都有默认值,编译器会生成一个额外的无参构造函数,该构造函数会使用这些默认值。这使得 Kotlin 在与像 JacksonJPA 这样通过无参构造函数创建类实例的库一起使用时更加方便。

class Example(val param1: Int = 0, val param2: String = "")

编译器会额外生成一个无参构造函数,使用 param1 的默认值 0param2 的默认值 "" 来创建 Example 类的实例,这样 JacksonJPA 这类库就能顺利地创建 Example 类的实例了。

创建类的实例

要创建一个类的实例,就像调用普通函数一样调用构造函数。你可以将创建的实例赋值给一个变量:

val invoice = Invoice()

val customer = Customer("Joe Smith")

Kotlin 没有 new 关键字。

抽象类

可以将类以及它的部分或全部成员声明为抽象的。抽象成员在其所在类中没有具体的实现。而且,不需要使用 open 关键字来标注抽象类或抽象函数。

abstract class Polygon {
    abstract fun draw()
}

class Rectangle : Polygon() {
    override fun draw() {
        // 绘制矩形的具体实现。
    }
}

在上述代码中,Polygon 是一个抽象类,其中的 draw 方法是抽象方法,没有具体的实现代码。Rectangle 类继承自 Polygon 类,并且必须重写 draw 方法,提供绘制矩形的具体逻辑。

还可以使用抽象成员来重写非抽象的开放成员。

open class Polygon {
    open fun draw() {
        // 一些默认的多边形绘制方法。
    }
}

abstract class WildShape : Polygon() {
    // 继承自 WildShape 的类需要提供自己的 draw 方法,
    // 而不是使用 Polygon 中的默认方法。
    abstract override fun draw()
}

在这个例子中,Polygon 类有一个开放的 draw 方法,有默认的实现。WildShape 类继承自 Polygon 类,使用 abstract overridedraw 方法进行重写,将其变成抽象方法。这意味着任何继承 WildShape 类的子类都必须提供 draw 方法的具体实现,而不能再使用 Polygon 类里的默认实现了。


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

相关文章:

  • 2024BaseCTF_week4_web上
  • 开源身份和访问管理方案之keycloak(一)快速入门
  • Vue3(2)
  • mysql读写分离与proxysql的结合
  • 【Python】集合
  • STM32 HAL库 CANbus通讯(C语言)
  • mysql BUG 导致 show processlist 有大量的show slave stauts 处于init状态
  • Java调用C++动态库、入参为对象
  • websocketpp库使用:快速搭建一个websocket服务端
  • 【学习】如何高效通过CCRC信息安全服务资质认证
  • 介绍下SpringBoot在分布式架构中,如何实现读写分离
  • 晶闸管主要参数分析与损耗计算
  • 【Web安全测试】Burp中NEW_xp_CAPTCHA插件(含4.1和4.2)的下载安装和导入
  • 网络安全设备异构要求 网络安全设备硬件
  • 伺服报警的含义
  • PostgreSQL插件-pg_stat_statements-安装和使用
  • Unity-Mirror网络框架-从入门到精通之Multiple Additive Scenes示例
  • .NET Core中使用HttpClient模拟form-data格式数据提交
  • Spring Cloud 04 - 负载均衡和外部服务访问
  • 《LSTM与HMM:序列建模领域的双雄对决》
  • 什么手机卡最便宜 怎么办手机卡最便宜
  • AI前端开发对团队协作能力的影响:机遇与挑战并存
  • 从零开始认识大语言模型(LLM)
  • 读 DeepSeek-R1 论文笔记
  • 使用 Go 语言调用 SiliconFlow 语音生成 API 的脚本,用于将文本转换为 MP3 格式的语音文件。
  • NLP_[2]-认识文本预处理