Biểu thức và khai báo đối tượng — Object expressions and declarations

Liem Vo
Viet Android Developers
5 min readOct 26, 2021

Đôi lúc chúng ta cần tạo một đối tượng với sự thay đổi nhỏ với lớp, và không có lớp con từ nó. Kotlin giới thiệu 2 đối tượng là biểu thức và khai báo.

https://youtu.be/y8WiwuyhQlQ

Biểu thức đối tượng — Object expressions

Biểu thức đối tượng tạo ra các đối tượng của lớp vô danh tức là lớp không có chỉ rõ với từ khoá class khi khai báo. Ví dụ lớp được dùng cho một lần dùng. Bạn có thể định nghĩa biểu thức đối tượng từ ban đầu, kế thừa từ lớp có sẵn, hoặc hiện thực các giao diện. Các thí dụ của lớp vô danh cũng được gọi là đối tượng vô danh bởi vì chúng được định nghĩa bởi biểu thức không phải có tên.

Đối tượng vô danh được tạo từ ban đầu với từ khoá object

Nếu chúng ta chỉ cần đối tượng không cần kế thừa từ một kiểu nào đó thì chỉ cần khai báo các thuộc tính, hoặc hàm trong nó

val helloObject = object {
val hello = "Hello"
val object1 = "Object"
override fun toString(): String {
return "$hello $object1"
}
}

fun main() {
println(helloObject)
}

Đối tượng vô danh được tạo từ kế thừa từ kiểu cha

Để tạo kế thừa từ kiểu cha chúng ta có từ khóa object và theo sau là tên của cha

interface MouseEvent
abstract class MouseAdapter {
abstract fun mouseClicked(e: MouseEvent)
abstract fun mouseEntered(e: MouseEvent)
}

val mouseAdapter = object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {}
override fun mouseEntered(e: MouseEvent) {}
}

Nếu kiểu cha có hàm khởi tạo thì đối tượng cần phải khởi tạo khi kế thừa và đối tượng có thể kế thừa và hiện thực interface

val mouseAdapter: MouseAdapter = object : MouseAdapter(), MouseEvent {
override fun mouseClicked(e: MouseEvent) {}
override fun mouseEntered(e: MouseEvent) {}
}

Đối tượng vô danh được dùng như kiểu trả về

Khi một đối tượng vô danh được dùng là thuộc tính hoặc hàm riêng (không phải là hàm inline) của class thì các hàm trong lớp có thể truy xuất thuộc tính hoặc hàm của đối tượng vô danh.

class Student {
private fun getObject() = object {
val x: String = "x"
}

fun printX() {
println(getObject())
}
}

Nếu đối tượng vô danh là public hoặc là private inline , thật sự nó là:

  • Any nếu đối tượng vô danh không có kế thừa từ một kiểu cha nào
  • Nếu được khai báo kế thừa từ một kiểu cha, thì nó có kiểu giống kiểu cha
  • Nó sẽ tường minh nếu có nhiều hơn kiểu cha

Trong tất cả các trường hợp này của đối tượng vô danh là không thể truy xuất được. Các thành viên được override có thể truy xuất được nếu chúng được khai bao kiểu thật sự của hàm hoặc thuộc tính.

class C {
// The return type is Any. x is not accessible
fun getObject() = object {
val x: String = "x"
}

// The return type is A; x is not accessible
fun getObjectA() = object: A {
override fun funFromA() {}
val x: String = "x"
}

// The return type is B; funFromA() and x are not accessible
fun getObjectB(): B = object: A, B { // explicit return type is required
override fun funFromA() {}
val x: String = "x"
}
}
(tham khảo từ https://kotlinlang.org/docs/object-declarations.html#using-anonymous-objects-as-return-and-value-types)

Truy xuất biến bên trong đối tượng vô danh

Đối tượng biểu thức có thể truy xuất đến code trong phạm vi cho phép

class Window {
fun addClick(adapter: MouseAdapter) {}
}

fun countClicks() {
var clickCount = 0
var enterCount = 0
val window = Window()
window.addClick(object: MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
clickCount++
}

override fun mouseEntered(e: MouseEvent) {
enterCount++
}
})
}

Khai báo đối tượng

Việc dùng mẫu Singleton rất hiệu dụng trong rất nhiều ngữ cảnh và Kotlin dùng object để khai báo là duy nhất.

object StudentManager {
private val students = mutableListOf<Student>()
val allStudents get() = students fun registerStudent(student: Student) {
students.add(student)
}
}

Nó được gọi là khai báo đối tượng và thường có từ khóa object . Giống như khai báo một biến, một đối tượng khai báo không phải là một biểu thức, và nó không không được dùng như việc gán giá trị.

Sự khởi tạo của một khai báo đối tượng là luồn an toàn và hoàn thành trong gọi lần tiên.

Để tham chiếu đến đối tượng thì gọi tên trực tiếp

val allStudents = StudentManager.allStudents

Đối tượng khai báo không thể được khai báo bên trong hàm hoặc lớp bên trong. Nó có thể được khai báo bên trong một khai báo đối tượng khác.

Đối tượng đồng hành — Companion Object

Một đối tượng được khai báo trong một lớp và được đánh dấu với từ khoá companion

class Configuration {
companion object Constants {
const val DELAY_TIME_MILLISECONDS = 1000L
}
}

Thành viên của đối tượng đồng hành có thể truy xuất theo tên lớp

val delayTime = Configuration.DELAY_TIME_MILLISECONDS

Tên của đối tượng đồng hành có thể được lượt bỏ và trong trường hợp này khi dùng chúng ta cần phải có từ khoá companion.

class Config {
companion object {
const val HTTP_OK = "OK"
}
}

val httpOk = Config.Companion.HTTP_OK

Các thành viên của lớp có thể truy xuất các thành phần private của đối tượng đồng hành.

Tên của một lớp được sử dụng bởi chính nó (không phải là định nghĩa cho một tên khác) hoạt động như một tham chiếu đến đối tượng đồng hành của lớp (cho dù có tên hay không):

class MyClass1 {
companion object Named {}
}

val x = MyClass1

class MyClass2 {
companion object {}
}

val y = MyClass2

Lưu ý rằng mặc dù các thành viên của các đối tượng đồng hành trông giống như các thành viên tĩnh trong các ngôn ngữ khác, trong thời gian chạy, chúng vẫn là các thành viên thể hiện của các đối tượng thực và có thể, chẳng hạn, có thể triển khai các giao diện:

interface Factory<T> {
fun create(): T
}

class MyClass {
companion object : Factory<MyClass> {
override fun create(): MyClass = MyClass()
}
}

val f: Factory<MyClass> = MyClass

Tuy nhiên, trên JVM, bạn có thể có các thành viên của các đối tượng đồng hành được tạo dưới dạng các trường và phương thức tĩnh thực sự nếu bạn sử dụng chú thích @JvmStatic. Xem phần khả năng tương tác Java để biết thêm chi tiết.

Sự khác biết ngữ nghĩa của biểu thức và khai báo đối tượng

  • Biểu thức đối tượng được thực thì và khởi tạo tại thời điểm được dùng
  • Khai báo đối tượng chỉ được khởi tạo cho lần đầu dùng.
  • Đối tượng đồng hành được khởi tạo khi lớp tương ứng của nó được tải.

Cảm ơn các bạn đã đọc bài. Đăng ký kênh chúng tôi để xem những bài học mới nhất.

Youtube kênh: https://bit.ly/2EFOOXs

Thảo luận bằng cách comment ở đây hoặc trong video của blog này.

--

--