ZetCode

Kotlin 类

最后修改于 2024 年 1 月 29 日

本文介绍了如何在 Kotlin 语言中使用类。类是 Kotlin 中对象的蓝图。

有三种广泛使用的编程范式:过程式编程、函数式编程和面向对象编程。Kotlin 都支持它们。在面向对象编程中,我们使用对象来解决复杂的问题。

面向对象编程 (OOP) 是一种使用对象及其交互来设计应用程序和计算机程序的编程范式。

Kotlin 类

对象是从类创建的。类是对象的蓝图;它以成员和成员函数的形式共享通用属性和行为。

在 Kotlin 中,使用 class 关键字声明一个类。

类声明由类名、类头(指定其类型参数、主构造函数等)和类体组成。类体在两个大括号内指定。类头和类体都是可选的。如果类没有类体,则可以省略大括号。

Kotlin 简单类示例

在下面的示例中,我们从一个类创建一个简单的对象。

SimpleClass.kt
package com.zetcode

class Simple {

    private val name = "Simple"
    fun info() = "This is $name class"
}

fun main() {

    val s = Simple()
    println(s)
    println(s.info())
}

在示例中,我们声明一个 Simple 类,然后从中创建一个对象。

class Simple {

    private val name = "Simple"
    fun info() = "This is $name class"
}

在 Kotlin 中,使用 class 关键字创建类。在我们的类中,我们有一个属性(成员)和一个函数。

val s = Simple()

Simple 类创建一个新对象。在 Kotlin 中,我们不使用 new 关键字来创建实例。

println(s.info())

使用点运算符,我们调用对象的 info 函数。

com.zetcode.Simple@3cd1a2f1
This is Simple class

Kotlin 空类

在下面的示例中,我们创建了两个空类。

EmptyClass.kt
package com.zetcode

class Being {}
class Empty

fun main() {

    val b = Being()
    println(b)

    val e = Empty()
    println(e)
}

一个空类没有成员或成员函数。可以省略大括号。

Kotlin 主构造函数

一个 Kotlin 类可以有一个主构造函数和一个或多个次要构造函数。主构造函数是类头的一部分:它位于类名(和可选的类型参数)之后。

class User constructor(name: String, email: String) {  }

这里我们使用 constructor 关键字。

class User (name: String, email: String) {  }

如果主构造函数没有任何注解或可见性修饰符(例如 public),则可以省略 constructor 关键字。

主构造函数不能包含任何代码。初始化代码可以放在初始化块中,使用 init 关键字创建它们。

PrimaryConstructor.kt
package com.zetcode

//class User constructor (_name: String, _email: String) {
class User(name: String, email: String) {

    private val name = name
    private val email = email

    override fun toString(): String {

        return "User $name has email $email"
    }
}

fun main() {

    val u = User("Peter Novak", "pnovak47@gmail.com")
    println(u)
}

在示例中,我们有一个带主构造函数的 User 类。我们有两个成员和一个重写函数。

User Peter Novak has email pnovak47@gmail.com

Kotlin open 类

Kotlin 类默认是 final 的。其他类不能从 final 类继承。要使一个类可继承,我们用 open 关键字标记它。

OpenClass.kt
package com.zetcode

open class Being(private val alive: Boolean = true) {

    fun isAlive(): Boolean {

        return alive
    }
}

class Dog(val name: String): Being() {

    fun bark(): String {

        return "woof-woof"
    }
}

fun main() {

    val d = Dog("Rusty")

    println(d.bark())
    println(d.name)
    println(d.isAlive())
}

我们有一个 open Being 类。Dog 类继承自 Being

println(d.isAlive())

我们可以在 Dog 类上调用 isAlive 函数,因为它继承自其父类 Being 类。

woof-woof
Rusty
true

Kotlin 数据类

一些类被设计用来保存数据。使用数据类,我们可以大大减少样板代码。编译器会自动创建 equalshashCodetoStringcopy 函数。

在 Kotlin 中,使用 data 关键字创建数据类。

数据类必须遵循几个规则。主构造函数至少需要一个参数。所有主构造函数参数都必须标记为 val 或 var。数据类不能是 abstract、open、sealed 或 inner 的。

DataClass.kt
package com.zetcode

data class User(val name: String, val email: String)

fun main() {

    val u = User("Peter Novak", "pnovak47@gmail.com")
    println(u)

    println(u.name)
    println(u.email)

    val (name, email) = u;
    println("$name $email")

    val u2 = User("Peter Novak", "pnovak47@gmail.com")

    println(u == u2)
    println(u === u2)
}

在示例中,我们有一个数据类:User

data class User(val name: String, val email: String)

数据类以前缀 data 关键字开头。

val u = User("Peter Novak", "pnovak47@gmail.com")
println(u)

这里我们调用 toString 方法,它已经为我们创建好了。

val (name, email) = u;
println("$name $email")

我们可以使用解构声明从数据类中提取属性。

User(name=Peter Novak, email=pnovak47@gmail.com)
Peter Novak
pnovak47@gmail.com
Peter Novak pnovak47@gmail.com
true
false

Kotlin 嵌套类

嵌套类在另一个类中声明。

NestedClass.kt
package com.zetcode

class Outer {

    val name = "Outer"
    fun show() = "the name: $name"

    class Nested {
        val name = "Nested"
        fun show() = "the name: $name"
    }
}

fun main() {

    println(Outer().show())
    println(Outer.Nested().show())
}

为了访问嵌套类,我们指定其外部类的名称。因此,嵌套类的 show 函数被这样调用:Outer.Nested().show。嵌套类不能访问外部类的成员。

Kotlin 内部类

内部类是使用 inner 关键字创建的。与嵌套类不同,它们可以访问其外部类的成员。

InnerClass.kt
package com.zetcode

class Outer {

    val name1 = "Outer"
    fun show() = "the name: $name1"

    inner class Inner {

        val name2 = "Inner"
        fun show() = "data: $name2 and $name1"
    }
}

fun main() {

    println(Outer().show())
    println(Outer().Inner().show())
}

在示例中,我们有一个内部类。它的 show 函数输出 Outer 类的 name1 成员。

Kotlin 抽象类

抽象类、成员或成员函数使用 abstract 关键字创建。如果一个类继承自一个抽象类,它必须实现其所有抽象成员和成员函数。实现的成员和成员函数必须以 override 关键字为前缀。我们不能从抽象类创建实例。抽象类是隐式 open 的,因为如果它们没有任何具体的子类,它们就毫无用处。

抽象类在概念上类似于接口。与接口不同,抽象类可以有状态。虽然一个类可以实现多个接口,但它只能继承一个抽象类。

抽象类用于构建类的层次结构;它们用作相关类的一些共享功能的组合。

AbstractClass.kt
package com.zetcode

abstract class Shape() {

    abstract var w: Int
    abstract var h: Int

    abstract fun area(): Int

    fun info(): String {
        return "width: $w; height: $h"
    }
}

class Rectangle(override var w: Int, override var h: Int): Shape() {

    override fun area(): Int {
        return w * h;
    }
}

fun main() {

    val r = Rectangle(5, 6)
    println(r.area())
    println(r.info());
}

在示例中,我们有一个抽象的 Shape 类。不可能创建一个形状,因为它太通用了。我们只能创建一个形状的后代;例如,一个矩形。

abstract var w: Int
abstract var h: Int

abstract fun area(): Int

这里我们有两个抽象变量和一个抽象函数。我们必须在子类中为它们提供实现。

Kotlin 密封类

密封类用于表示受限的类层次结构。一个值可以具有来自有限集合的类型之一,但不能具有任何其他类型。密封类比枚举类更强大。

注意: 在某些语言中,例如 C# 或 Scala,密封类是一个禁止继承的类。

密封类是抽象的,可以有抽象成员;这意味着它们不能直接实例化。密封类不能有公共构造函数(构造函数默认为私有)。密封类可以有子类,但它们必须在同一文件中或嵌套在密封类声明中。

SealedClass.kt
package com.zetcode

sealed class Shape
class Circle(var radius: Float) : Shape()
class Square(var width: Int) : Shape()
class Rectangle(var width: Int, var height: Int) : Shape()

fun getArea(e: Shape) =
    when (e) {
        is Circle -> println("Circle area is ${Math.PI * e.radius * e.radius}")
        is Square -> println("Square area is ${e.width * e.width}")
        is Rectangle -> println("Rectangle area is ${e.width * e.height}")
    }

fun main() {

    val circle = Circle(7f)
    val square = Square(5)
    val rectangle = Rectangle(8, 6)

    getArea(circle)
    getArea(square)
    getArea(rectangle)
}

在示例中,我们有一个密封的 Shape 类。它有三个子类:CircleSquareRectangle

fun getArea(e: Shape) =
    when (e) {
        is Circle -> println("Circle area is ${Math.PI * e.radius * e.radius}")
        is Square -> println("Square area is ${e.width * e.width}")
        is Rectangle -> println("Rectangle area is ${e.width * e.height}")
    }

getArea 函数计算形状的面积。请注意,不需要 else 语句,因为编译器知道选项列表是详尽的。

Circle area is 153.93804002589985
Square area is 25
Rectangle area is 48
SealedClass2.kt
package com.zetcode

sealed class Shape {
    class Circle(var radius: Float) : Shape()
    class Square(var width: Int) : Shape()
    class Rectangle(var width: Int, var height: Int) : Shape()
}

fun getArea(e: Shape) =
    when (e) {
        is Shape.Circle -> println("Circle area is ${Math.PI * e.radius * e.radius}")
        is Shape.Square -> println("Square area is ${e.width * e.width}")
        is Shape.Rectangle -> println("Rectangle area is ${e.width * e.height}")
    }

fun main() {

    val circle = Shape.Circle(7f)
    val square = Shape.Square(5)
    val rectangle = Shape.Rectangle(8, 6)

    getArea(circle)
    getArea(square)
    getArea(rectangle)
}

在此示例中,子类嵌套在 Shape 类中。

来源

Kotlin 类 - 语言参考

在本文中,我们介绍了 Kotlin 中的类。

作者

我的名字是 Jan Bodnar,我是一个充满激情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直在撰写编程文章。到目前为止,我撰写了 1,400 多篇文章和 8 本电子书。我拥有超过十年的编程教学经验。

列出 所有 Kotlin 教程