Flutter 类和对象(一):类

类

在 Kotlin 中,类用关键字 class声明:

kotlin 复制代码
class Person { /*...*/ }

类的声明由:类名、类头信息(指定其类型参数、主构造函数以及其他一些内容)和由花括号包裹的类体。类头和类体都是可选的;如果没有类体,那么花括号可以省略:

kotlin 复制代码
class Empty

构造函数

在 Kotlin 中,一个类会有一个主构造函数和一个或者多个次要构造函数。主构造函数需要写在类头上,并且主构造函数需要写在类名后面,主构造函数还可以携带参数:

javascript 复制代码
class Person constructor(firstName: String) { /*...*/ }

如果主构造函数没有注解或者可见性标识符,那么constructor可以省略:

kotlin 复制代码
class Person(firstName: String) { /*...*/ }

主构造函数初始化类实例和类属性。类头不能包含可执行的代码。开发者可以使用初始化代码块,来在对象创建期间,执行一些代码。初始化代码块使用 init 声明,初始化代码块需要包裹在花括号之中。

During the initialization of an instance, the initializer blocks are executed in the same order as they appear in the class body, interleaved with the property initializers: 初始化代码块会按着自上而下的顺序执行,所以可能属性的赋值也会在代码块改变。

kotlin 复制代码
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}")
    }
}
// 先给 firstProperty 赋值,
// 执行第一个init
// 再给secondProperty 赋值
// 执行第二个init

主构造函数的参数可以用在初始化代码块中。它们也可以用在代码体中的属性初始化中:

kotlin 复制代码
class Customer(name: String) {
    val customerKey = name.uppercase()
}

Kotlin 中有语法糖来实现声明和初始化属性:

kotlin 复制代码
class Person(val firstName: String, val lastName: String, var age: Int)

声明也可以包含默认的值:

kotlin 复制代码
class Person(val firstName: String, val lastName: String, var isEmployed: Boolean = true)

在声明类的属性时,可以使用末尾逗号:

kotlin 复制代码
class Person(
    val firstName: String,
    val lastName: String,
    var age: Int, // trailing comma
) { /*...*/ }

和常规属性类似,主函数中的属性可以是 var 或者 val

Plain constructor parameters (that are not properties) are accessible in: 未用var 或者 val修饰的构造参数在以下场景是可用的:

  • 类头
  • 类体中的已初始化属性
  • 初始化代码块

它们仅在初始化阶段有效,不成为类的成员,因此无法在类的方法中直接访问。

kotlin 复制代码
class RectangleWithParameters(width: Int, height: Int) {
    val perimeter = 2 * width + 2 * height

    init {
        println("Rectangle created with width = $width and height = $height")
    }
}

如果构造函数有注解或者可见性修饰符,那么 constructor 关键字是必须的,并且修饰符在 constructor 之前:

kotlin 复制代码
class Customer public @Inject constructor(name: String) { /*...*/ }

次构造函数

类也可以声明次要构造函数,次要构造函数使用 constructor 声明:

kotlin 复制代码
class Person(val pets: MutableList<Pet> = mutableListOf())

class Pet {
    constructor(owner: Person) {
        owner.pets.add(this) // adds this pet to the list of its owner's pets
    }
}

如果类本身有一个主构造函数,那么每个次要函数需要代理到主构造函数,要么直接调用,要么通过其他的次要函数调用。如果代理到一个类的其他构造函数,就可以使用 this 关键字:

kotlin 复制代码
class Person(val name: String) {
    val children: MutableList<Person> = mutableListOf()
    constructor(name: String, parent: Person) : this(name) {
        parent.children.add(this)
    }
}

初始化代码块实际上是主构造方法的一部分。代理到主构造函数的时机是访问次要构造函数第一句的时候,因此,所有的初始化代码块和初始化属性会在次要构造函数体之前调用。

即使类没有主构造函数,代理也会隐式发生,并且所有的初始化代码块依然会执行:

kotlin 复制代码
class Constructors {
    init {
        println("Init block")
    }

    constructor(i: Int) {
        println("Constructor $i")
    }
}
// first
// first block
// second
// second block
// Constructor 1

即使一个非抽象类没有声明构造函数(主要和次要),这个类也会生成一个无参的主要构造,并且这个构造是 public 的。

如果开发者不想要有一个 public 的构造,那么可以声明一个空的非默认可见性的主要构造:

kotlin 复制代码
class DontCreateMe private constructor() { /*...*/ }

创建类实例

开发者可以调用构造函数来实例化一个类,将实例化的对象来给变量赋值:

kotlin 复制代码
val invoice = Invoice()

val customer = Customer("Joe Smith")

类成员

类可以包含:

  • 构造方法和初始化代码块
  • 方法
  • 属性
  • 嵌套类和内部类
  • 对象声明

继承

类之间可以相互派生,形成继承层次结构。

抽象类

一个类可以被声明成抽象类,抽象类的部分或者全部成员可以是抽象的。一个抽象成员在类中是没有具体实现的。开发者不需要使用 open 来注解一个抽象类或者抽象方法。

kotlin 复制代码
abstract class Polygon {
    abstract fun draw()
}

class Rectangle : Polygon() {
    override fun draw() {
        // draw the rectangle
    }
}

开发者可以使用抽象来重写一个非抽象的open成员。

kotlin 复制代码
open class Polygon {
    open fun draw() {
        // some default polygon drawing method
    }
}

abstract class WildShape : Polygon() {
    // Classes that inherit WildShape need to provide their own
    // draw method instead of using the default on Polygon
    abstract override fun draw()
}

伴生对象

开发者可以在类内容声明一个伴生对象,这样就可以在不创建对象的情况下,使用伴生对象访问类的成员。

原文链接

相关推荐
千里马学框架4 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台4 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone4 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc4 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo4 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077004 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
ai2work4 天前
ch23 综合复刻:从零做一个最小可用版本(capstone)
kotlin
其实防守也摸鱼4 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
ai2work4 天前
ch21 签名、校验与发版
kotlin
AFinalStone4 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui