Kotlin 类与构造函数:深入理解与实践
在本教程中,我们将讨论Kotlin的面向对象编程概念。我们将详细介绍Kotlin类。我们还将研究Kotlin的构造函数、访问修饰符和抽象类。
Kotlin类

class FirstClass {
}
以下是如何实例化该类的示例:
val firstClass = FirstClass()
var new = FirstClass() //这里的 new 是变量名。
与Java不同,Kotlin中的new
不是一个关键字。在Kotlin中,默认情况下,类是final
的。因此,在Java中,上述定义的等效方式可能类似于以下代码:
public final class FirstClass {
}
因此,默认情况下,Kotlin中的类是不可继承的。为了使一个类非final
,我们需要在其前面加上关键字open
。
open class Me{
}
open
注解允许其他类继承。
Kotlin 类示例
让我们创建一个带有几个函数和属性的类。我们将看到如何访问该类的函数和属性。此外,我们还将看到如何设置成员属性。
class User {
var loggedIn: Boolean = false
val cantChangeValue = "Hi"
fun logOn() {
loggedIn = true
}
fun logOff() {
loggedIn = false
}
}
fun main(args: Array<String>) {
val user = User()
println(user.loggedIn) //false
user.logOn()
println(user.loggedIn) //true
user.logOff()
println(user.loggedIn) //false
user.cantChangeValue = "Hey" //无法编译。不能修改 final 变量。
}
main
函数属于Test.kt
文件。要访问成员和函数,我们需要使用点运算符。不能再使用点运算符来设置val
属性。
Kotlin的初始化
Kotlin的init
块定义如下所示。
class User {
init{
print("Class instance is initialised.")
}
var loggedIn: Boolean = false
val cantChangeValue = "Hi"
fun logOn() {
loggedIn = true
}
fun logOff() {
loggedIn = false
}
}
当类实例化时,init
块中的代码将首先执行。每次实例化类时,init
块都会运行,无论使用什么样的构造函数,下面我们将看到。一个类中可以编写多个初始化块。它们将按照下面的顺序依次执行。
class MultiInit(name: String) {
init {
println("First initializer block that prints ${name}")
}
init {
println("Second initializer block that prints ${name.length}")
}
}
fun main(args: Array<String>) {
var multiInit = MultiInit("Kotlin")
}
//以下内容打印在日志控制台中。
//First initializer block that prints Kotlin
//Second initializer block that prints 6
使用下面所示的also
函数,Kotlin 类可以在声明本身中打印属性。
class MultiInit(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(args: Array<String>) {
var multiInit = MultiInit("Kotlin")
}
//以下内容被打印。
//First property: Kotlin
//First initializer block that prints Kotlin
//Second property: 6
//Second initializer block that prints 6
Kotlin 构造函数
Kotlin的构造函数是用于初始化属性的特殊成员函数。与Java相比,Kotlin的构造函数的编写和结构有所不同。默认情况下,一个类具有以下空构造函数:
class Student {
var name: String
val age : Int
init {
name = "Anupam"
age = 24
}
init {
name = "Anupam Chugh"
//age = 26
}
}
fun main(args: Array<String>) {
val student = Student()
println("${student.name} age is ${student.age}")
student.name = "Your"
//student.age = 26 //无法编译。age 是 val
println("${student.name} age is ${student.age}")
}
//以下内容打印在控制台上:
//Anupam Chugh age is 24
//Your age is 24
主构造函数
Kotlin中的主构造函数是在类头中定义的,如下所示。
class User(var name: String, var isAdmin: Boolean) {
init {
name = name + " @ JournalDev.com"
println("Author Name is $name. Is Admin? $isAdmin")
}
}
主构造函数定义在类头部。我们已经在构造函数本身中定义了属性类型(val
/var
)。注意:除非声明为var
,否则默认情况下构造函数参数是val
。
class User(name: String, isAdmin: Boolean)
在上面的代码中,无法重新分配name
和isAdmin
。或者,我们也可以将构造函数的参数分配给类中的成员属性,如下所示。
class User(name: String, val isAdmin: Boolean) {
var username = name
val _isAdmin = isAdmin
init {
username= username + " @ JournalDev.com"
println("Author Name is $name. Is Admin? $_isAdmin")
}
}
fun main(args: Array<String>) {
var user = User("Anupam",false)
user.isAdmin = true //无法编译,因为 isAdmin 是 val
user._isAdmin = true //无法编译。原因相同。
user = User("Pankaj",true)
}
//以下内容打印在日志控制台中。
//Author Name is Anupam. Is Admin? false
//Author Name is Pankaj. Is Admin? true
Kotlin 构造函数的默认值
在Kotlin中,我们可以在构造函数本身中指定默认值,如下所示。
class User(name: String, var website: String = "JournalDev") {
init {
println("Author $name writes at $website")
}
init {
website = website + ".com"
println("Author $name writes at $website")
}
}
fun main(args: Array<String>) {
var user = User("Anupam","JournalDev")
user = User("Pankaj","JournalDev")
}
// 以下内容打印在控制台:
// Author Anupam writes at JournalDev
// Author Anupam writes at JournalDev.com
// Author Pankaj writes at JournalDev
// Author Pankaj writes at JournalDev.com
次构造函数
在类的主体中,通过在构造函数前添加constructor
关键字来编写次构造函数。以下示例演示了这一点。
class Student {
var name: String
val age : Int
constructor(name: String, age: Int)
{
this.name = name
this.age = age
}
fun printDetails()
{
println("Name is $name and Age is $age")
}
}
fun main(args: Array<String>) {
var student = Student("Anupam", 24)
student.printDetails()
}
// 以下内容打印在控制台:
// Name is Anupam and Age is 24
在子类中,次构造函数最常见的用途是在需要以不同方式初始化类时使用。如果该类包含一个主构造函数,次构造函数必须在其声明中引用主构造函数。使用关键字“this
”来进行引用。
class Student(var name: String, val age: Int) {
var skill: String
init {
skill = "NA"
}
constructor(name: String, age: Int, skill: String) : this(name, age) {
this.skill = skill
}
fun printDetails() {
if (skill.equals("NA"))
println("Name is $name and Age is $age")
else
println("Name is $name and Age is $age Skill is $skill")
}
}
// 以下内容打印在日志控制台:
// Name is Anupam and Age is 24
// Name is Anupam and Age is 24 Skill is Kotlin
初始化块用于初始化成员属性skill
。次构造函数使用“this
”关键字委托给主构造函数。
自定义的获取器和设置器
到目前为止,我们通过对类的实例使用点运算符来访问和修改属性。让我们使用set
和get
语法来看看如何自定义访问。
class Name{
var post: String = "default"
set(value) {if(!post.isNotEmpty()) {
throw IllegalArgumentException(" Enter a valid name")
}
field = value
print(value)
}
}
fun main(args: Array<String>) {
var name = Name()
name.post = "Kotlin Classes"
name.post = ""
name.post = "Kotlin Data Classes Our Next Tutorial"
}
以下内容打印在日志控制台:
Kotlin Classes
Exception in thread "main" java.lang.IllegalArgumentException: Enter a valid name
at Name.setPost(Test.kt:16)
at TestKt.main(Test.kt:78)
在设置器(setter)中,field
变量保存了旧值。让我们添加一个获取器(getter)。
class Name{
var post: String = "default"
set(value) {if(!post.isNotEmpty()) {
throw IllegalArgumentException(" Enter a valid name")
}
field = value
}
get() {
return field.capitalize()
}
}
fun main(args: Array<String>) {
var name = Name()
name.post = "kotlin classes"
println(name.post)
name.post = "kotlin data Classes our next Tutorial"
println(name.post)
}
// 以下内容打印:
// Kotlin classes
// Kotlin data Classes our next Tutorial
capitalize()
方法将字符串的第一个字母大写。注意:如果属性是val
,则设置方法将无法编译通过。
Kotlin 可见性修饰符
- Public(公共):任何带有此修饰符的类、函数、属性、接口或对象都是可见的,并且可以从任何地方访问。
- Private(私有):使用此修饰符定义的类/函数只能在同一文件内访问。类/函数中带有此修饰符的成员/属性只能在该块内访问。
- Protected(受保护):此修饰符与
private
相同,但它允许在子类中可见和访问。 - Internal(内部):带有此修饰符的类/接口/函数只能在同一模块内访问。
可见性修饰符同样适用于构造函数。将修饰符分配给主构造函数需要在类头中的constructor
关键字旁指定修饰符。
class Student private constructor (var name: String, val age: Int) {
var skill: String
init {
skill = "NA"
}
constructor(name: String, age: Int, skill: String) : this(name, age) {
this.skill = skill
}
fun printDetails() {
if (skill.equals("NA"))
println("Name is $name and Age is $age")
else
println("Name is $name and Age is $age Skill is $skill")
}
}
fun main(args: Array<String>) {
var student = Student("Anupam",24,"Kotlin")
student.printDetails()
}
// 打印:
// Name is Anupam and Age is 24 Skill is Kotlin
私有构造函数不能在类外部调用。在上述代码中,我们只能使用辅助构造函数在不同的函数中实例化该类。
Kotlin的抽象类
与Java类似,Kotlin中使用abstract
关键字来声明抽象类。抽象类不能被实例化,但可以被子类继承。默认情况下,抽象类的成员都是非抽象的,除非另有声明。
abstract class Person(name: String) {
init {
println("抽象类. 初始化块. 人员姓名是 $name")
}
abstract fun displayAge()
}
class Teacher(name: String): Person(name) {
var age : Int
init {
age = 24
}
override fun displayAge() {
println("非抽象类重写了 displayAge 函数. 年龄是 $age")
}
}
fun main(args: Array<String>) {
val person = Teacher("Anupam")
person.displayAge()
}
// 以下内容将打印在控制台:
// 抽象类. 初始化块. 人员姓名是 Anupam
// 非抽象类. 年龄是 24
请注意:抽象类在 Kotlin 中默认是开放的(open),因此无需额外添加 open
修饰符即可允许其他类继承。override
关键字用于在子类中重写父类的方法。在本教程中,我们已经涵盖了 Kotlin 类的基础知识。Kotlin 还包含许多其他高级特性,例如数据类(data class)、密封类(sealed class)、继承(inheritance)等。我们将在后续教程中详细介绍这些内容。参考资料:Kotlin 官方文档。