Lớp học lập trình này nằm trong khoá học Chương trình đào tạo về Kotlin dành cho lập trình viên. Bạn sẽ nhận được nhiều giá trị nhất qua khoá học này nếu thực hiện các lớp học lập trình theo trình tự. Tuỳ thuộc vào kiến thức của mình, bạn có thể lướt qua một số phần. Khoá học này hướng tới những lập trình viên biết một ngôn ngữ hướng đối tượng và muốn học Kotlin.
Giới thiệu
Trong lớp học lập trình này, bạn sẽ tạo một chương trình Kotlin và tìm hiểu về các lớp cũng như đối tượng trong Kotlin. Bạn sẽ thấy quen thuộc với phần lớn nội dung này nếu biết một ngôn ngữ hướng đối tượng khác. Tuy nhiên, Kotlin có một số điểm khác biệt quan trọng để giảm lượng mã bạn cần viết. Bạn cũng sẽ tìm hiểu về các lớp trừu tượng và việc uỷ quyền giao diện.
Thay vì tạo một ứng dụng mẫu duy nhất, các bài học trong khoá học này được thiết kế để giúp bạn nâng cao kiến thức, nhưng các bài học này có tính độc lập tương đối với nhau để bạn có thể lướt qua những phần mà bạn đã quen thuộc. Để liên kết các ví dụ này với nhau, nhiều ví dụ sử dụng chủ đề hồ cá. Nếu bạn muốn xem toàn bộ câu chuyện về bể cá, hãy tham khảo khoá học Kotlin Bootcamp dành cho lập trình viên của Udacity.
Kiến thức bạn cần có
- Kiến thức cơ bản về Kotlin, bao gồm các loại, toán tử và vòng lặp
- Cú pháp hàm của Kotlin
- Kiến thức cơ bản về lập trình hướng đối tượng
- Kiến thức cơ bản về một IDE như IntelliJ IDEA hoặc Android Studio
Kiến thức bạn sẽ học được
- Cách tạo lớp và truy cập vào các thuộc tính trong Kotlin
- Cách tạo và sử dụng hàm khởi tạo lớp trong Kotlin
- Cách tạo một lớp con và cách thức hoạt động của tính kế thừa
- Giới thiệu về các lớp, giao diện trừu tượng và việc uỷ quyền giao diện
- Cách tạo và sử dụng các lớp dữ liệu
- Cách sử dụng singleton, enum và lớp kín
Bạn sẽ thực hiện
- Tạo một lớp có các thuộc tính
- Tạo hàm khởi tạo cho một lớp
- Tạo một lớp con
- Xem xét các ví dụ về lớp và giao diện trừu tượng
- Tạo một lớp dữ liệu đơn giản
- Tìm hiểu về singleton, enum và lớp kín
Bạn cần nắm rõ các thuật ngữ lập trình sau:
- Lớp là sơ đồ thiết kế của các đối tượng. Ví dụ: lớp
Aquarium
là bản thiết kế để tạo một đối tượng hồ cá. - Đối tượng là các phiên bản của lớp; đối tượng hồ cá là một
Aquarium
thực tế. - Thuộc tính là đặc điểm của các lớp, chẳng hạn như chiều dài, chiều rộng và chiều cao của một
Aquarium
. - Phương thức, còn được gọi là hàm thành phần, là chức năng của lớp. Phương thức là những gì bạn có thể "làm" với đối tượng. Ví dụ: bạn có thể
fillWithWater()
một đối tượngAquarium
. - Giao diện là một quy cách mà một lớp có thể triển khai. Ví dụ: việc vệ sinh là hoạt động thường thấy đối với các đối tượng khác ngoài bể cá và việc vệ sinh thường diễn ra theo cách tương tự đối với các đối tượng khác nhau. Vì vậy, bạn có thể có một giao diện tên là
Clean
xác định một phương thứcclean()
. LớpAquarium
có thể triển khai giao diệnClean
để làm sạch bể cá bằng miếng bọt biển mềm. - Gói là một cách để nhóm mã có liên quan nhằm sắp xếp mã đó hoặc tạo một thư viện mã. Sau khi tạo một gói, bạn có thể nhập nội dung của gói đó vào một tệp khác và sử dụng lại mã cũng như các lớp trong gói đó.
Trong nhiệm vụ này, bạn sẽ tạo một gói mới và một lớp có một số thuộc tính và phương thức.
Bước 1: Tạo một gói
Các gói có thể giúp bạn sắp xếp mã một cách khoa học.
- Trong ngăn Project (Dự án), bên dưới dự án Hello Kotlin, hãy nhấp chuột phải vào thư mục src.
- Chọn New (Mới) > Package (Gói) rồi gọi gói đó là
example.myapp
.
Bước 2: Tạo một lớp có các thuộc tính
Các lớp được xác định bằng từ khoá class
và theo quy ước, tên lớp bắt đầu bằng một chữ cái viết hoa.
- Nhấp chuột phải vào gói example.myapp.
- Chọn New > Kotlin File / Class (Mới > Tệp/Lớp Kotlin).
- Trong phần Kind (Loại), hãy chọn Class (Lớp) rồi đặt tên cho lớp là
Aquarium
. IntelliJ IDEA sẽ thêm tên gói vào tệp và tạo một lớpAquarium
trống cho bạn. - Bên trong lớp
Aquarium
, hãy xác định và khởi chạy các thuộc tínhvar
cho chiều rộng, chiều cao và chiều dài (tính bằng cm). Khởi chạy các thuộc tính bằng các giá trị mặc định.
package example.myapp
class Aquarium {
var width: Int = 20
var height: Int = 40
var length: Int = 100
}
Trong nội bộ, Kotlin sẽ tự động tạo các phương thức getter và setter cho những thuộc tính mà bạn đã xác định trong lớp Aquarium
, nhờ đó bạn có thể truy cập trực tiếp vào các thuộc tính, chẳng hạn như myAquarium.length
.
Bước 3: Tạo một hàm main()
Tạo một tệp mới có tên là main.kt
để lưu giữ hàm main()
.
- Trong ngăn Project (Dự án) ở bên trái, hãy nhấp chuột phải vào gói example.myapp.
- Chọn New > Kotlin File / Class (Mới > Tệp/Lớp Kotlin).
- Trong trình đơn thả xuống Loại, hãy giữ lựa chọn là Tệp và đặt tên cho tệp là
main.kt
. IntelliJ IDEA có tên gói nhưng không có định nghĩa lớp cho tệp. - Xác định hàm
buildAquarium()
và tạo một thực thể củaAquarium
bên trong. Để tạo một phiên bản, hãy tham chiếu đến lớp như thể đó là một hàm,Aquarium()
. Thao tác này sẽ gọi hàm khởi tạo của lớp và tạo một thực thể của lớpAquarium
, tương tự như việc sử dụngnew
trong các ngôn ngữ khác. - Xác định hàm
main()
và gọibuildAquarium()
.
package example.myapp
fun buildAquarium() {
val myAquarium = Aquarium()
}
fun main() {
buildAquarium()
}
Bước 4: Thêm một phương thức
- Trong lớp
Aquarium
, hãy thêm một phương thức để in các thuộc tính kích thước của hồ cá.
fun printSize() {
println("Width: $width cm " +
"Length: $length cm " +
"Height: $height cm ")
}
- Trong
main.kt
, trongbuildAquarium()
, hãy gọi phương thứcprintSize()
trênmyAquarium
.
fun buildAquarium() {
val myAquarium = Aquarium()
myAquarium.printSize()
}
- Chạy chương trình bằng cách nhấp vào hình tam giác màu xanh lục bên cạnh hàm
main()
. Quan sát kết quả.
⇒ Width: 20 cm Length: 100 cm Height: 40 cm
- Trong
buildAquarium()
, hãy thêm mã để đặt chiều cao thành 60 và in các thuộc tính kích thước đã thay đổi.
fun buildAquarium() {
val myAquarium = Aquarium()
myAquarium.printSize()
myAquarium.height = 60
myAquarium.printSize()
}
- Chạy chương trình rồi quan sát kết quả.
⇒ Width: 20 cm Length: 100 cm Height: 40 cm Width: 20 cm Length: 100 cm Height: 60 cm
Trong bài này, bạn sẽ tạo một hàm khởi tạo cho lớp và tiếp tục làm việc với các thuộc tính.
Bước 1: Tạo một hàm khởi tạo
Trong bước này, bạn sẽ thêm một hàm khởi tạo vào lớp Aquarium
mà bạn đã tạo trong nhiệm vụ đầu tiên. Trong ví dụ trước đó, mọi thực thể của Aquarium
đều được tạo bằng các phương diện giống nhau. Bạn có thể thay đổi kích thước sau khi tạo bằng cách thiết lập các thuộc tính, nhưng sẽ đơn giản hơn nếu bạn tạo kích thước chính xác ngay từ đầu.
Trong một số ngôn ngữ lập trình, hàm khởi tạo được xác định bằng cách tạo một phương thức trong lớp có cùng tên với lớp. Trong Kotlin, bạn xác định hàm khởi tạo ngay trong chính phần khai báo lớp, chỉ định các tham số bên trong dấu ngoặc đơn như thể lớp đó là một phương thức. Giống như các hàm trong Kotlin, những tham số đó có thể bao gồm các giá trị mặc định.
- Trong lớp
Aquarium
mà bạn đã tạo trước đó, hãy thay đổi định nghĩa lớp để thêm 3 tham số hàm khởi tạo có giá trị mặc định cholength
,width
vàheight
, đồng thời chỉ định các tham số này cho các thuộc tính tương ứng.
class Aquarium(length: Int = 100, width: Int = 20, height: Int = 40) {
// Dimensions in cm
var length: Int = length
var width: Int = width
var height: Int = height
...
}
- Cách Kotlin nhỏ gọn hơn là xác định các thuộc tính trực tiếp bằng hàm khởi tạo, sử dụng
var
hoặcval
, đồng thời Kotlin cũng tự động tạo các phương thức getter và setter. Sau đó, bạn có thể xoá các định nghĩa thuộc tính trong phần nội dung của lớp.
class Aquarium(var length: Int = 100, var width: Int = 20, var height: Int = 40) {
...
}
- Khi tạo một đối tượng
Aquarium
bằng hàm khởi tạo đó, bạn có thể không chỉ định đối số và nhận các giá trị mặc định, hoặc chỉ định một số đối số hoặc chỉ định tất cả các đối số và tạo mộtAquarium
có kích thước hoàn toàn tuỳ chỉnh. Trong hàmbuildAquarium()
, hãy thử các cách tạo đối tượngAquarium
bằng cách sử dụng các tham số được đặt tên.
fun buildAquarium() {
val aquarium1 = Aquarium()
aquarium1.printSize()
// default height and length
val aquarium2 = Aquarium(width = 25)
aquarium2.printSize()
// default width
val aquarium3 = Aquarium(height = 35, length = 110)
aquarium3.printSize()
// everything custom
val aquarium4 = Aquarium(width = 25, height = 35, length = 110)
aquarium4.printSize()
}
- Chạy chương trình và quan sát kết quả.
⇒ Width: 20 cm Length: 100 cm Height: 40 cm Width: 25 cm Length: 100 cm Height: 40 cm Width: 20 cm Length: 110 cm Height: 35 cm Width: 25 cm Length: 110 cm Height: 35 cm
Lưu ý rằng bạn không cần phải nạp chồng hàm khởi tạo và viết một phiên bản khác cho từng trường hợp này (cộng thêm một vài trường hợp nữa cho các tổ hợp khác). Kotlin tạo những gì cần thiết từ các giá trị mặc định và tham số được đặt tên.
Bước 2: Thêm các khối khởi tạo
Các hàm khởi tạo ví dụ ở trên chỉ khai báo các thuộc tính và gán giá trị của một biểu thức cho các thuộc tính đó. Nếu hàm dựng của bạn cần thêm mã khởi tạo, thì mã đó có thể được đặt trong một hoặc nhiều khối init
. Trong bước này, bạn sẽ thêm một số khối init
vào lớp Aquarium
.
- Trong lớp
Aquarium
, hãy thêm một khốiinit
để in rằng đối tượng đang khởi tạo và một khối thứ hai để in thể tích tính bằng lít.
class Aquarium (var length: Int = 100, var width: Int = 20, var height: Int = 40) {
init {
println("aquarium initializing")
}
init {
// 1 liter = 1000 cm^3
println("Volume: ${width * length * height / 1000} l")
}
}
- Chạy chương trình và quan sát kết quả.
aquarium initializing
Volume: 80 l
Width: 20 cm Length: 100 cm Height: 40 cm
aquarium initializing
Volume: 100 l
Width: 25 cm Length: 100 cm Height: 40 cm
aquarium initializing
Volume: 77 l
Width: 20 cm Length: 110 cm Height: 35 cm
aquarium initializing
Volume: 96 l
Width: 25 cm Length: 110 cm Height: 35 cm
Xin lưu ý rằng các khối init
được thực thi theo thứ tự xuất hiện trong định nghĩa lớp và tất cả các khối này đều được thực thi khi hàm khởi tạo được gọi.
Bước 3: Tìm hiểu về hàm dựng phụ
Trong bước này, bạn sẽ tìm hiểu về hàm khởi tạo phụ và thêm một hàm khởi tạo phụ vào lớp. Ngoài hàm khởi tạo chính (có thể có một hoặc nhiều khối init
), một lớp Kotlin cũng có thể có một hoặc nhiều hàm khởi tạo phụ để cho phép nạp chồng hàm khởi tạo, tức là các hàm khởi tạo có đối số khác nhau.
- Trong lớp
Aquarium
, hãy thêm một hàm khởi tạo phụ lấy số lượng cá làm đối số, bằng cách dùng từ khoáconstructor
. Tạo một thuộc tínhval
cho bể cá để tính thể tích của bể cá theo lít dựa trên số lượng cá. Giả sử mỗi con cá cần 2 lít (2.000 cm^3) nước, cộng thêm một chút không gian để nước không bị tràn.
constructor(numberOfFish: Int) : this() {
// 2,000 cm^3 per fish + extra room so water doesn't spill
val tank = numberOfFish * 2000 * 1.1
}
- Trong hàm dựng phụ, hãy giữ nguyên chiều dài và chiều rộng (được đặt trong hàm dựng chính) và tính chiều cao cần thiết để bình chứa có thể tích đã cho.
// calculate the height needed
height = (tank / (length * width)).toInt()
- Trong hàm
buildAquarium()
, hãy thêm một lệnh gọi để tạoAquarium
bằng hàm khởi tạo phụ mới. In kích thước và thể tích.
fun buildAquarium() {
val aquarium6 = Aquarium(numberOfFish = 29)
aquarium6.printSize()
println("Volume: ${aquarium6.width * aquarium6.length * aquarium6.height / 1000} l")
}
- Chạy chương trình rồi quan sát kết quả.
⇒ aquarium initializing Volume: 80 l Width: 20 cm Length: 100 cm Height: 31 cm Volume: 62 l
Xin lưu ý rằng âm lượng được in hai lần, một lần bằng khối init
trong hàm dựng chính trước khi hàm dựng phụ được thực thi và một lần bằng mã trong buildAquarium()
.
Bạn cũng có thể thêm từ khoá constructor
vào hàm khởi tạo chính, nhưng không cần thiết trong hầu hết các trường hợp.
Bước 4: Thêm một phương thức truy xuất thuộc tính mới
Trong bước này, bạn sẽ thêm một phương thức truy xuất thuộc tính rõ ràng. Kotlin tự động xác định getter và setter khi bạn xác định các thuộc tính, nhưng đôi khi bạn cần điều chỉnh hoặc tính toán giá trị cho một thuộc tính. Ví dụ: ở trên, bạn đã in âm lượng của Aquarium
. Bạn có thể cung cấp âm lượng dưới dạng một thuộc tính bằng cách xác định một biến và một getter cho biến đó. Vì volume
cần được tính toán, nên phương thức getter cần trả về giá trị đã tính. Bạn có thể thực hiện việc này bằng một hàm một dòng.
- Trong lớp
Aquarium
, hãy xác định một thuộc tínhInt
có tên làvolume
và xác định một phương thứcget()
để tính toán âm lượng ở dòng tiếp theo.
val volume: Int
get() = width * height * length / 1000 // 1000 cm^3 = 1 l
- Xoá khối
init
in âm lượng. - Xoá mã trong
buildAquarium()
in âm lượng. - Trong phương thức
printSize()
, hãy thêm một dòng để in âm lượng.
fun printSize() {
println("Width: $width cm " +
"Length: $length cm " +
"Height: $height cm "
)
// 1 l = 1000 cm^3
println("Volume: $volume l")
}
- Chạy chương trình rồi quan sát kết quả.
⇒ aquarium initializing Width: 20 cm Length: 100 cm Height: 31 cm Volume: 62 l
Kích thước và thể tích vẫn như cũ, nhưng thể tích chỉ được in một lần sau khi đối tượng được khởi tạo đầy đủ bởi cả hàm khởi tạo chính và hàm khởi tạo phụ.
Bước 5: Thêm một phương thức thiết lập thuộc tính
Trong bước này, bạn sẽ tạo một bộ thiết lập thuộc tính mới cho âm lượng.
- Trong lớp
Aquarium
, hãy thay đổivolume
thànhvar
để có thể đặt nhiều lần. - Thêm một setter cho thuộc tính
volume
bằng cách thêm phương thứcset()
bên dưới getter. Phương thức này sẽ tính toán lại chiều cao dựa trên lượng nước được cung cấp. Theo quy ước, tên của tham số setter làvalue
, nhưng bạn có thể thay đổi tên này nếu muốn.
var volume: Int
get() = width * height * length / 1000
set(value) {
height = (value * 1000) / (width * length)
}
- Trong
buildAquarium()
, hãy thêm mã để đặt thể tích của bể cá thành 70 lít. In kích thước mới.
fun buildAquarium() {
val aquarium6 = Aquarium(numberOfFish = 29)
aquarium6.printSize()
aquarium6.volume = 70
aquarium6.printSize()
}
- Chạy lại chương trình và quan sát chiều cao cũng như thể tích đã thay đổi.
⇒ aquarium initialized
Width: 20 cm Length: 100 cm Height: 31 cm
Volume: 62 l
Width: 20 cm Length: 100 cm Height: 35 cm
Volume: 70 l
Cho đến nay, chưa có công cụ sửa đổi chế độ hiển thị nào, chẳng hạn như public
hoặc private
, trong mã. Đó là vì theo mặc định, mọi thứ trong Kotlin đều ở chế độ công khai, tức là mọi thứ đều có thể truy cập ở mọi nơi, bao gồm cả các lớp, phương thức, thuộc tính và biến thành viên.
Trong Kotlin, các lớp, đối tượng, giao diện, hàm dựng, hàm, thuộc tính và phương thức setter có thể có các từ khoá xác định mức độ hiển thị:
public
nghĩa là có thể nhìn thấy bên ngoài lớp. Mọi thứ đều ở chế độ công khai theo mặc định, bao gồm cả các biến và phương thức của lớp.internal
nghĩa là chỉ hiển thị trong mô-đun đó. Mô-đun là một tập hợp các tệp Kotlin được biên dịch cùng nhau, chẳng hạn như một thư viện hoặc ứng dụng.private
nghĩa là chỉ hiển thị trong lớp đó (hoặc tệp nguồn nếu bạn đang làm việc với các hàm).protected
giống nhưprivate
, nhưng cũng sẽ hiển thị với bất kỳ lớp con nào.
Hãy xem phần Chỉ định truy cập trong tài liệu về Kotlin để biết thêm thông tin.
Biến thành phần
Theo mặc định, các thuộc tính trong một lớp hoặc các biến thành phần là public
. Nếu bạn xác định chúng bằng var
, chúng sẽ có thể thay đổi, tức là có thể đọc và ghi. Nếu bạn xác định các thuộc tính này bằng val
, thì chúng sẽ chỉ có thể đọc sau khi khởi tạo.
Nếu muốn một thuộc tính mà mã của bạn có thể đọc hoặc ghi, nhưng mã bên ngoài chỉ có thể đọc, bạn có thể giữ thuộc tính và phương thức getter ở chế độ công khai, đồng thời khai báo phương thức setter ở chế độ riêng tư, như minh hoạ dưới đây.
var volume: Int
get() = width * height * length / 1000
private set(value) {
height = (value * 1000) / (width * length)
}
Trong nhiệm vụ này, bạn sẽ tìm hiểu cách hoạt động của lớp con và tính kế thừa trong Kotlin. Các quy tắc này tương tự như những quy tắc bạn đã thấy ở các ngôn ngữ khác, nhưng vẫn có một số điểm khác biệt.
Trong Kotlin, theo mặc định, các lớp không thể được phân lớp con. Tương tự, các lớp con không thể ghi đè các thuộc tính và biến thành viên (mặc dù có thể truy cập vào các thuộc tính và biến này).
Bạn phải đánh dấu một lớp là open
để cho phép lớp đó được phân lớp con. Tương tự, bạn phải đánh dấu các thuộc tính và biến thành viên là open
để ghi đè chúng trong lớp con. Bạn phải dùng từ khoá open
để ngăn chặn việc vô tình làm lộ chi tiết triển khai trong giao diện của lớp.
Bước 1: Mở lớp học Aquarium
Ở bước này, bạn tạo lớp Aquarium
open
để có thể ghi đè lớp này ở bước tiếp theo.
- Đánh dấu lớp
Aquarium
và tất cả các thuộc tính của lớp đó bằng từ khoáopen
.
open class Aquarium (open var length: Int = 100, open var width: Int = 20, open var height: Int = 40) {
open var volume: Int
get() = width * height * length / 1000
set(value) {
height = (value * 1000) / (width * length)
}
- Thêm một thuộc tính
shape
mở có giá trị là"rectangle"
.
open val shape = "rectangle"
- Thêm một thuộc tính
water
mở bằng phương thức getter trả về 90% âm lượng củaAquarium
.
open var water: Double = 0.0
get() = volume * 0.9
- Thêm mã vào phương thức
printSize()
để in hình dạng và lượng nước dưới dạng tỷ lệ phần trăm của thể tích.
fun printSize() {
println(shape)
println("Width: $width cm " +
"Length: $length cm " +
"Height: $height cm ")
// 1 l = 1000 cm^3
println("Volume: $volume l Water: $water l (${water/volume*100.0}% full)")
}
- Trong
buildAquarium()
, hãy thay đổi mã để tạoAquarium
bằngwidth = 25
,length = 25
vàheight = 40
.
fun buildAquarium() {
val aquarium6 = Aquarium(length = 25, width = 25, height = 40)
aquarium6.printSize()
}
- Chạy chương trình của bạn và quan sát kết quả mới.
⇒ aquarium initializing rectangle Width: 25 cm Length: 25 cm Height: 40 cm Volume: 25 l Water: 22.5 l (90.0% full)
Bước 2: Tạo một lớp con
- Tạo một lớp con của
Aquarium
có tên làTowerTank
. Lớp này triển khai một bình chứa hình trụ tròn thay vì bình chứa hình chữ nhật. Bạn có thể thêmTowerTank
bên dướiAquarium
, vì bạn có thể thêm một lớp khác vào cùng một tệp với lớpAquarium
. - Trong
TowerTank
, hãy ghi đè thuộc tínhheight
được xác định trong hàm khởi tạo. Để ghi đè một thuộc tính, hãy dùng từ khoáoverride
trong lớp con.
- Khiến hàm khởi tạo cho
TowerTank
lấydiameter
. Sử dụngdiameter
cho cảlength
vàwidth
khi gọi hàm khởi tạo trong siêu lớpAquarium
.
class TowerTank (override var height: Int, var diameter: Int): Aquarium(height = height, width = diameter, length = diameter) {
- Ghi đè thuộc tính thể tích để tính toán hình trụ. Công thức tính thể tích hình trụ là pi nhân với bình phương bán kính nhân với chiều cao. Bạn cần nhập hằng số
PI
từjava.lang.Math
.
override var volume: Int
// ellipse area = π * r1 * r2
get() = (width/2 * length/2 * height / 1000 * PI).toInt()
set(value) {
height = ((value * 1000 / PI) / (width/2 * length/2)).toInt()
}
- Trong
TowerTank
, hãy ghi đè thuộc tínhwater
thành 80% âm lượng.
override var water = volume * 0.8
- Ghi đè
shape
thành"cylinder"
.
override val shape = "cylinder"
- Lớp
TowerTank
cuối cùng của bạn sẽ có dạng như mã bên dưới.
Aquarium.kt
:
package example.myapp
import java.lang.Math.PI
... // existing Aquarium class
class TowerTank (override var height: Int, var diameter: Int): Aquarium(height = height, width = diameter, length = diameter) {
override var volume: Int
// ellipse area = π * r1 * r2
get() = (width/2 * length/2 * height / 1000 * PI).toInt()
set(value) {
height = ((value * 1000 / PI) / (width/2 * length/2)).toInt()
}
override var water = volume * 0.8
override val shape = "cylinder"
}
- Trong
buildAquarium()
, hãy tạo mộtTowerTank
có đường kính 25 cm và chiều cao 45 cm. In kích thước.
main.kt:
package example.myapp
fun buildAquarium() {
val myAquarium = Aquarium(width = 25, length = 25, height = 40)
myAquarium.printSize()
val myTower = TowerTank(diameter = 25, height = 40)
myTower.printSize()
}
- Chạy chương trình rồi quan sát kết quả.
⇒ aquarium initializing rectangle Width: 25 cm Length: 25 cm Height: 40 cm Volume: 25 l Water: 22.5 l (90.0% full) aquarium initializing cylinder Width: 25 cm Length: 25 cm Height: 40 cm Volume: 18 l Water: 14.4 l (80.0% full)
Đôi khi, bạn muốn xác định hành vi hoặc thuộc tính chung để chia sẻ giữa một số lớp có liên quan. Kotlin cung cấp 2 cách để thực hiện việc đó, đó là giao diện và lớp trừu tượng. Trong nhiệm vụ này, bạn sẽ tạo một lớp AquariumFish
trừu tượng cho các thuộc tính chung của tất cả các loài cá. Bạn tạo một giao diện có tên là FishAction
để xác định hành vi chung của tất cả các loài cá.
- Không thể tạo thực thể cho lớp trừu tượng hoặc giao diện, tức là bạn không thể tạo trực tiếp các đối tượng thuộc những loại đó.
- Các lớp trừu tượng có hàm khởi tạo.
- Giao diện không thể có bất kỳ logic hàm khởi tạo nào hoặc lưu trữ bất kỳ trạng thái nào.
Bước 1. Tạo một lớp trừu tượng
- Trong example.myapp, hãy tạo một tệp mới,
AquariumFish.kt
. - Tạo một lớp, cũng có tên là
AquariumFish
, rồi đánh dấu lớp đó bằngabstract
. - Thêm một thuộc tính
String
,color
và đánh dấu thuộc tính đó bằngabstract
.
package example.myapp
abstract class AquariumFish {
abstract val color: String
}
- Tạo 2 lớp con của
AquariumFish
,Shark
vàPlecostomus
. - Vì
color
là trừu tượng, nên các lớp con phải triển khai lớp này. ĐặtShark
thành màu xám vàPlecostomus
thành màu vàng.
class Shark: AquariumFish() {
override val color = "gray"
}
class Plecostomus: AquariumFish() {
override val color = "gold"
}
- Trong main.kt, hãy tạo một hàm
makeFish()
để kiểm thử các lớp của bạn. Khởi tạo mộtShark
và mộtPlecostomus
, sau đó in màu của từng đối tượng. - Xoá mã kiểm thử trước đó trong
main()
và thêm một lệnh gọi đếnmakeFish()
. Mã của bạn sẽ có dạng như mã dưới đây.
main.kt
:
package example.myapp
fun makeFish() {
val shark = Shark()
val pleco = Plecostomus()
println("Shark: ${shark.color}")
println("Plecostomus: ${pleco.color}")
}
fun main () {
makeFish()
}
- Chạy chương trình rồi quan sát kết quả.
⇒ Shark: gray Plecostomus: gold
Sơ đồ sau đây biểu thị lớp Shark
và lớp Plecostomus
, trong đó phân lớp lớp trừu tượng AquariumFish
.
Bước 2. Tạo một giao diện
- Trong AquariumFish.kt, hãy tạo một giao diện có tên là
FishAction
bằng phương thứceat()
.
interface FishAction {
fun eat()
}
- Thêm
FishAction
vào từng lớp con và triển khaieat()
bằng cách in những gì cá làm.
class Shark: AquariumFish(), FishAction {
override val color = "gray"
override fun eat() {
println("hunt and eat fish")
}
}
class Plecostomus: AquariumFish(), FishAction {
override val color = "gold"
override fun eat() {
println("eat algae")
}
}
- Trong hàm
makeFish()
, hãy cho mỗi con cá bạn tạo ăn một thứ gì đó bằng cách gọieat()
.
fun makeFish() {
val shark = Shark()
val pleco = Plecostomus()
println("Shark: ${shark.color}")
shark.eat()
println("Plecostomus: ${pleco.color}")
pleco.eat()
}
- Chạy chương trình rồi quan sát kết quả.
⇒ Shark: gray hunt and eat fish Plecostomus: gold eat algae
Sơ đồ sau đây biểu thị lớp Shark
và lớp Plecostomus
. Cả hai lớp này đều được tạo thành từ và triển khai giao diện FishAction
.
Trường hợp nên sử dụng các lớp trừu tượng so với giao diện
Các ví dụ trên rất đơn giản, nhưng khi bạn có nhiều lớp liên quan, các lớp và giao diện trừu tượng có thể giúp bạn giữ cho thiết kế của mình gọn gàng, ngăn nắp và dễ duy trì hơn.
Như đã lưu ý ở trên, các lớp trừu tượng có thể có hàm dựng, còn giao diện thì không, nhưng nếu không thì chúng rất giống nhau. Vậy khi nào bạn nên sử dụng từng loại?
Khi bạn sử dụng các giao diện để tạo một lớp, chức năng của lớp sẽ được mở rộng thông qua các thực thể lớp mà lớp đó chứa. Thành phần có xu hướng giúp mã dễ tái sử dụng và dễ hiểu hơn so với tính kế thừa từ một lớp trừu tượng. Ngoài ra, bạn có thể sử dụng nhiều giao diện trong một lớp, nhưng chỉ có thể tạo lớp con từ một lớp trừu tượng.
Thành phần thường dẫn đến đóng gói tốt hơn, liên kết (sự phụ thuộc lẫn nhau) thấp hơn, giao diện rõ ràng hơn và mã có thể sử dụng nhiều hơn. Vì những lý do này, việc sử dụng thành phần với các giao diện là lựa chọn thiết kế ưu tiên. Mặt khác, việc kế thừa từ một lớp trừu tượng có xu hướng phù hợp tự nhiên với một số vấn đề. Vì vậy, bạn nên ưu tiên thành phần, nhưng khi kế thừa có ý nghĩa, Kotlin cũng cho phép bạn làm điều đó!
- Sử dụng một giao diện nếu bạn có nhiều phương thức và một hoặc hai cách triển khai mặc định, chẳng hạn như trong
AquariumAction
bên dưới.
interface AquariumAction {
fun eat()
fun jump()
fun clean()
fun catchFish()
fun swim() {
println("swim")
}
}
- Sử dụng lớp trừu tượng bất cứ khi nào bạn không thể hoàn thành một lớp. Ví dụ: quay lại lớp
AquariumFish
, bạn có thể làm cho tất cảAquariumFish
triển khaiFishAction
và cung cấp một phương thức triển khai mặc định choeat
trong khi đểcolor
ở dạng trừu tượng, vì thực sự không có màu mặc định cho cá.
interface FishAction {
fun eat()
}
abstract class AquariumFish: FishAction {
abstract val color: String
override fun eat() = println("yum")
}
Nhiệm vụ trước đó đã giới thiệu các lớp trừu tượng, giao diện và ý tưởng về thành phần. Uỷ quyền giao diện là một kỹ thuật nâng cao, trong đó các phương thức của một giao diện được triển khai bằng một đối tượng trợ giúp (hoặc đối tượng uỷ quyền), sau đó được một lớp sử dụng. Kỹ thuật này có thể hữu ích khi bạn sử dụng một giao diện trong một loạt các lớp không liên quan: bạn thêm chức năng giao diện cần thiết vào một lớp trợ giúp riêng biệt và mỗi lớp sử dụng một thực thể của lớp trợ giúp để triển khai chức năng.
Trong nhiệm vụ này, bạn sẽ sử dụng tính năng uỷ quyền giao diện để thêm chức năng vào một lớp.
Bước 1: Tạo giao diện mới
- Trong AquariumFish.kt, hãy xoá lớp
AquariumFish
. Thay vì kế thừa từ lớpAquariumFish
,Plecostomus
vàShark
sẽ triển khai các giao diện cho cả hành động của cá và màu sắc của chúng. - Tạo một giao diện mới,
FishColor
, xác định màu dưới dạng một chuỗi.
interface FishColor {
val color: String
}
- Thay đổi
Plecostomus
để triển khai 2 giao diện,FishAction
vàFishColor
. Bạn cần ghi đècolor
từFishColor
vàeat()
từFishAction
.
class Plecostomus: FishAction, FishColor {
override val color = "gold"
override fun eat() {
println("eat algae")
}
}
- Thay đổi lớp
Shark
để triển khai cả hai giao diện,FishAction
vàFishColor
, thay vì kế thừa từAquariumFish
.
class Shark: FishAction, FishColor {
override val color = "gray"
override fun eat() {
println("hunt and eat fish")
}
}
- Mã hoàn tất của bạn sẽ có dạng như sau:
package example.myapp
interface FishAction {
fun eat()
}
interface FishColor {
val color: String
}
class Plecostomus: FishAction, FishColor {
override val color = "gold"
override fun eat() {
println("eat algae")
}
}
class Shark: FishAction, FishColor {
override val color = "gray"
override fun eat() {
println("hunt and eat fish")
}
}
Bước 2: Tạo một lớp singleton
Tiếp theo, bạn triển khai chế độ thiết lập cho phần uỷ quyền bằng cách tạo một lớp trợ giúp triển khai FishColor
. Bạn tạo một lớp cơ bản có tên là GoldColor
triển khai FishColor
– tất cả những gì lớp này làm là cho biết màu của lớp là màu vàng.
Không có lý do gì để tạo nhiều thực thể của GoldColor
, vì tất cả các thực thể đó đều sẽ làm chính xác cùng một việc. Vì vậy, Kotlin cho phép bạn khai báo một lớp mà bạn chỉ có thể tạo một thực thể bằng cách sử dụng từ khoá object
thay vì class
. Kotlin sẽ tạo một thực thể đó và thực thể đó được tham chiếu theo tên lớp. Sau đó, tất cả các đối tượng khác chỉ có thể sử dụng một thực thể này – không có cách nào để tạo các thực thể khác của lớp này. Nếu đã quen thuộc với mẫu singleton, thì đây là cách bạn triển khai singleton trong Kotlin.
- Trong AquariumFish.kt, hãy tạo một đối tượng cho
GoldColor
. Ghi đè màu.
object GoldColor : FishColor {
override val color = "gold"
}
Bước 3: Thêm uỷ quyền giao diện cho FishColor
Giờ đây, bạn đã sẵn sàng sử dụng tính năng uỷ quyền giao diện.
- Trong AquariumFish.kt, hãy xoá chế độ ghi đè
color
khỏiPlecostomus
. - Thay đổi lớp
Plecostomus
để lấy màu từGoldColor
. Bạn có thể làm việc này bằng cách thêmby GoldColor
vào phần khai báo lớp để tạo hoạt động uỷ quyền. Điều này có nghĩa là thay vì triển khaiFishColor
, hãy sử dụng phương thức triển khai doGoldColor
cung cấp. Vì vậy, mỗi khicolor
được truy cập, yêu cầu sẽ được uỷ quyền choGoldColor
.
class Plecostomus: FishAction, FishColor by GoldColor {
override fun eat() {
println("eat algae")
}
}
Với lớp hiện tại, tất cả cá Tỳ bà đều có màu vàng, nhưng thực tế thì loài cá này có nhiều màu. Bạn có thể giải quyết vấn đề này bằng cách thêm một tham số hàm khởi tạo cho màu với GoldColor
làm màu mặc định cho Plecostomus
.
- Thay đổi lớp
Plecostomus
để lấyfishColor
được truyền vào bằng hàm khởi tạo của lớp đó và đặt giá trị mặc định thànhGoldColor
. Thay đổi việc uỷ quyền từby GoldColor
thànhby fishColor
.
class Plecostomus(fishColor: FishColor = GoldColor): FishAction,
FishColor by fishColor {
override fun eat() {
println("eat algae")
}
}
Bước 4: Thêm uỷ quyền giao diện cho FishAction
Tương tự, bạn có thể sử dụng tính năng uỷ quyền giao diện cho FishAction
.
- Trong AquariumFish.kt, hãy tạo một lớp
PrintingFishAction
triển khaiFishAction
, lấyString
,food
, sau đó in những gì cá ăn.
class PrintingFishAction(val food: String) : FishAction {
override fun eat() {
println(food)
}
}
- Trong lớp
Plecostomus
, hãy xoá hàm ghi đèeat()
vì bạn sẽ thay thế hàm này bằng một uỷ quyền. - Trong phần khai báo
Plecostomus
, hãy uỷ quyềnFishAction
choPrintingFishAction
, truyền"eat algae"
. - Với tất cả các hoạt động uỷ quyền đó, không có mã nào trong phần nội dung của lớp
Plecostomus
, vì vậy, hãy xoá{}
vì tất cả các phương thức ghi đè đều do hoạt động uỷ quyền giao diện xử lý
class Plecostomus (fishColor: FishColor = GoldColor):
FishAction by PrintingFishAction("eat algae"),
FishColor by fishColor
Sơ đồ sau đây biểu thị các lớp Shark
và Plecostomus
, cả hai đều bao gồm các giao diện PrintingFishAction
và FishColor
, nhưng uỷ quyền việc triển khai cho các giao diện này.
Uỷ quyền giao diện là một tính năng mạnh mẽ và bạn thường nên cân nhắc cách sử dụng tính năng này bất cứ khi nào có thể sử dụng một lớp trừu tượng bằng một ngôn ngữ khác. Bạn có thể dùng thành phần để cắm các hành vi, thay vì phải có nhiều lớp con, mỗi lớp chuyên biệt theo một cách khác nhau.
Lớp dữ liệu tương tự như struct
trong một số ngôn ngữ khác – chủ yếu dùng để lưu giữ một số dữ liệu – nhưng đối tượng lớp dữ liệu vẫn là một đối tượng. Các đối tượng lớp dữ liệu trong Kotlin đem lại thêm một số lợi ích, chẳng hạn như các tiện ích để in và sao chép. Trong nhiệm vụ này, bạn sẽ tạo một lớp dữ liệu đơn giản và tìm hiểu về sự hỗ trợ mà Kotlin cung cấp cho các lớp dữ liệu.
Bước 1: Tạo một lớp dữ liệu
- Thêm một gói mới
decor
trong gói example.myapp để lưu trữ mã mới. Nhấp chuột phải vào example.myapp trong ngăn Project (Dự án) rồi chọn File > New > Package (Tệp > Mới > Gói). - Trong gói này, hãy tạo một lớp mới có tên là
Decoration
.
package example.myapp.decor
class Decoration {
}
- Để đặt
Decoration
làm lớp dữ liệu, hãy thêm tiền tố cho phần khai báo lớp bằng từ khoádata
. - Thêm một thuộc tính
String
có tên làrocks
để cung cấp một số dữ liệu cho lớp.
data class Decoration(val rocks: String) {
}
- Trong tệp, bên ngoài lớp, hãy thêm một hàm
makeDecorations()
để tạo và in một phiên bản củaDecoration
bằng"granite"
.
fun makeDecorations() {
val decoration1 = Decoration("granite")
println(decoration1)
}
- Thêm hàm
main()
để gọimakeDecorations()
rồi chạy chương trình. Lưu ý đầu ra hợp lý được tạo vì đây là một lớp dữ liệu.
⇒ Decoration(rocks=granite)
- Trong
makeDecorations()
, hãy tạo thực thể cho 2 đối tượngDecoration
khác có cùng giá trị "slate" và in các đối tượng đó.
fun makeDecorations() {
val decoration1 = Decoration("granite")
println(decoration1)
val decoration2 = Decoration("slate")
println(decoration2)
val decoration3 = Decoration("slate")
println(decoration3)
}
- Trong
makeDecorations()
, hãy thêm một câu lệnh in kết quả so sánhdecoration1
vớidecoration2
và một câu lệnh thứ hai so sánhdecoration3
vớidecoration2
. Sử dụng phương thức equals() do các lớp dữ liệu cung cấp.
println (decoration1.equals(decoration2))
println (decoration3.equals(decoration2))
- Chạy mã.
⇒ Decoration(rocks=granite) Decoration(rocks=slate) Decoration(rocks=slate) false true
Bước 2. Sử dụng tính năng phân rã
Để truy cập vào các thuộc tính của một đối tượng dữ liệu và gán các thuộc tính đó cho các biến, bạn có thể gán từng thuộc tính một, như thế này.
val rock = decoration.rock
val wood = decoration.wood
val diver = decoration.diver
Thay vào đó, bạn có thể tạo các biến (mỗi biến cho một thuộc tính) và chỉ định đối tượng dữ liệu cho nhóm biến. Kotlin đặt giá trị thuộc tính vào mỗi biến.
val (rock, wood, diver) = decoration
Đây được gọi là phân rã và là một cách viết tắt hữu ích. Số lượng biến phải khớp với số lượng thuộc tính và các biến được chỉ định theo thứ tự mà chúng được khai báo trong lớp. Sau đây là một ví dụ hoàn chỉnh mà bạn có thể thử trong Decoration.kt.
// Here is a data class with 3 properties.
data class Decoration2(val rocks: String, val wood: String, val diver: String){
}
fun makeDecorations() {
val d5 = Decoration2("crystal", "wood", "diver")
println(d5)
// Assign all properties to variables.
val (rock, wood, diver) = d5
println(rock)
println(wood)
println(diver)
}
⇒ Decoration2(rocks=crystal, wood=wood, diver=diver) crystal wood diver
Nếu không cần một hoặc nhiều thuộc tính, bạn có thể bỏ qua các thuộc tính đó bằng cách sử dụng _
thay vì tên biến, như minh hoạ trong mã bên dưới.
val (rock, _, diver) = d5
Trong nhiệm vụ này, bạn sẽ tìm hiểu về một số lớp chuyên dụng trong Kotlin, bao gồm:
- Lớp Singleton
- Enum
- Lớp kín
Bước 1: Gọi lại các lớp singleton
Hãy nhớ lại ví dụ trước với lớp GoldColor
.
object GoldColor : FishColor {
override val color = "gold"
}
Vì mọi thực thể của GoldColor
đều làm cùng một việc, nên thực thể này được khai báo dưới dạng object
thay vì class
để biến thực thể này thành một singleton. Chỉ có thể có một phiên bản của nó.
Bước 2: Tạo một enum
Kotlin cũng hỗ trợ các enum, cho phép bạn liệt kê một thứ gì đó và tham chiếu đến thứ đó theo tên, giống như trong các ngôn ngữ khác. Khai báo một enum bằng cách thêm từ khoá enum
vào tiền tố của khai báo. Khai báo enum cơ bản chỉ cần một danh sách tên, nhưng bạn cũng có thể xác định một hoặc nhiều trường được liên kết với mỗi tên.
- Trong Decoration.kt, hãy thử một ví dụ về enum.
enum class Color(val rgb: Int) {
RED(0xFF0000), GREEN(0x00FF00), BLUE(0x0000FF);
}
Enum hơi giống với singleton – chỉ có thể có một và chỉ có một trong mỗi giá trị trong quá trình liệt kê. Ví dụ: chỉ có thể có một Color.RED
, một Color.GREEN
và một Color.BLUE
. Trong ví dụ này, các giá trị RGB được chỉ định cho thuộc tính rgb
để biểu thị các thành phần màu. Bạn cũng có thể lấy giá trị thứ tự của một enum bằng thuộc tính ordinal
và tên của enum bằng thuộc tính name
.
- Hãy thử một ví dụ khác về enum.
enum class Direction(val degrees: Int) {
NORTH(0), SOUTH(180), EAST(90), WEST(270)
}
fun main() {
println(Direction.EAST.name)
println(Direction.EAST.ordinal)
println(Direction.EAST.degrees)
}
⇒ EAST 2 90
Bước 3: Tạo một lớp kín
Sealed class là một lớp có thể được tạo lớp con, nhưng chỉ ở bên trong tệp mà lớp đó được khai báo. Nếu cố gắng phân lớp lớp trong một tệp khác, bạn sẽ gặp lỗi.
Vì các lớp và lớp con nằm trong cùng một tệp, nên Kotlin sẽ biết tất cả các lớp con một cách tĩnh. Tức là tại thời gian biên dịch, trình biên dịch sẽ thấy tất cả các lớp và lớp con, đồng thời biết rằng đây là tất cả các lớp và lớp con, vì vậy trình biên dịch có thể thực hiện các bước kiểm tra bổ sung cho bạn.
- Trong AquariumFish.kt, hãy thử một ví dụ về lớp được niêm phong, tuân theo chủ đề dưới nước.
sealed class Seal
class SeaLion : Seal()
class Walrus : Seal()
fun matchSeal(seal: Seal): String {
return when(seal) {
is Walrus -> "walrus"
is SeaLion -> "sea lion"
}
}
Không thể phân lớp Seal
trong một tệp khác. Nếu muốn thêm các loại Seal
khác, bạn phải thêm chúng vào cùng một tệp. Điều này khiến các lớp được niêm phong trở thành một cách an toàn để biểu thị một số lượng kiểu cố định. Ví dụ: các lớp được niêm phong rất phù hợp để trả về trạng thái thành công hoặc lỗi từ một API mạng.
Bài học này đã đề cập đến rất nhiều nội dung. Mặc dù phần lớn kiến thức này đều quen thuộc với các ngôn ngữ lập trình hướng đối tượng khác, nhưng Kotlin có thêm một số tính năng để giữ cho mã nguồn ngắn gọn và dễ đọc.
Lớp và hàm khởi tạo
- Xác định một lớp trong Kotlin bằng cách dùng
class
. - Kotlin tự động tạo phương thức setter và getter cho các thuộc tính.
- Xác định hàm khởi tạo chính ngay trong định nghĩa lớp. Ví dụ:
class Aquarium(var length: Int = 100, var width: Int = 20, var height: Int = 40)
- Nếu hàm khởi tạo chính cần thêm mã, hãy viết mã đó trong một hoặc nhiều khối
init
. - Một lớp có thể xác định một hoặc nhiều hàm khởi tạo phụ bằng cách dùng
constructor
, nhưng theo kiểu Kotlin, bạn nên dùng một hàm ở trạng thái ban đầu.
Đối tượng sửa đổi chế độ hiển thị và lớp con
- Theo mặc định, tất cả các lớp và hàm trong Kotlin đều là
public
, nhưng bạn có thể dùng các hệ số xác định để thay đổi mức độ hiển thị thànhinternal
,private
hoặcprotected
. - Để tạo một lớp con, bạn phải đánh dấu lớp mẹ là
open
. - Để ghi đè các phương thức và thuộc tính trong một lớp con, các phương thức và thuộc tính phải được đánh dấu là
open
trong lớp mẹ. - Bạn chỉ có thể tạo lớp con cho một lớp niêm phong trong cùng một tệp nơi lớp đó được xác định. Tạo một lớp niêm phong bằng cách thêm tiền tố
sealed
vào phần khai báo.
Lớp dữ liệu, singleton và enum
- Tạo một lớp dữ liệu bằng cách thêm tiền tố
data
vào phần khai báo. - Phân rã là cách viết tắt để chỉ định các thuộc tính của một đối tượng
data
cho các biến riêng biệt. - Tạo một lớp singleton bằng cách sử dụng
object
thay vìclass
. - Xác định một enum bằng cách sử dụng
enum class
.
Lớp trừu tượng, giao diện và việc uỷ quyền
- Lớp trừu tượng và giao diện là hai cách để chia sẻ hành vi chung giữa các lớp.
- Lớp trừu tượng xác định các thuộc tính và hành vi, nhưng để lại việc triển khai cho các lớp con.
- Một giao diện xác định hành vi và có thể cung cấp các phương thức triển khai mặc định cho một số hoặc tất cả hành vi.
- Khi bạn sử dụng các giao diện để tạo một lớp, chức năng của lớp sẽ được mở rộng thông qua các thực thể lớp mà lớp đó chứa.
- Uỷ quyền giao diện sử dụng thành phần, nhưng cũng uỷ quyền việc triển khai cho các lớp giao diện.
- Thành phần là một cách hiệu quả để thêm chức năng vào một lớp bằng cách sử dụng uỷ quyền giao diện. Nhìn chung, bạn nên dùng thành phần, nhưng việc kế thừa từ một lớp trừu tượng sẽ phù hợp hơn với một số vấn đề.
Tài liệu về Kotlin
Nếu bạn muốn biết thêm thông tin về bất kỳ chủ đề nào trong khoá học này hoặc nếu bạn gặp khó khăn, thì https://kotlinlang.org là nơi tốt nhất để bạn bắt đầu.
- Lớp và tính kế thừa
- Hàm dựng
- Hàm tạo
- Thuộc tính và trường
- Chỉ định truy cập
- Lớp trừu tượng
- Giao diện
- Uỷ quyền
- Lớp dữ liệu
- Equality
- Destructuring
- Khai báo đối tượng
- Các lớp enum
- Lớp kín
- Xử lý lỗi không bắt buộc bằng cách sử dụng các lớp niêm phong của Kotlin
Hướng dẫn về Kotlin
Trang web https://try.kotlinlang.org có các hướng dẫn phong phú được gọi là Kotlin Koans, một trình thông dịch dựa trên web và một bộ tài liệu tham khảo đầy đủ kèm theo ví dụ.
Khoá học của Udacity
Để xem khoá học của Udacity về chủ đề này, hãy xem Chương trình đào tạo về Kotlin dành cho lập trình viên.
IntelliJ IDEA
Bạn có thể xem tài liệu về IntelliJ IDEA trên trang web của JetBrains.
Phần này liệt kê các bài tập về nhà cho học viên của lớp học lập trình này trong phạm vi khoá học có người hướng dẫn. Người hướng dẫn phải thực hiện các việc sau đây:
- Giao bài tập về nhà nếu cần.
- Trao đổi với học viên về cách nộp bài tập về nhà.
- Chấm điểm bài tập về nhà.
Người hướng dẫn có thể sử dụng các đề xuất này ít hoặc nhiều tuỳ ý và nên giao cho học viên bất kỳ bài tập về nhà nào khác mà họ cảm thấy phù hợp.
Nếu bạn đang tự học các lớp học lập trình, hãy sử dụng những bài tập về nhà này để kiểm tra kiến thức của mình.
Trả lời các câu hỏi sau
Câu hỏi 1
Các lớp có một phương thức đặc biệt đóng vai trò là bản thiết kế để tạo các đối tượng của lớp đó. Phương thức này có tên là gì?
▢ Một trình tạo
▢ Một trình khởi tạo
▢ Một hàm khởi tạo
▢ Bản thiết kế
Câu hỏi 2
Câu nào sau đây về giao diện và lớp trừu tượng là KHÔNG chính xác?
▢ Các lớp trừu tượng có thể có hàm khởi tạo.
▢ Giao diện không thể có hàm khởi tạo.
▢ Bạn có thể tạo trực tiếp thực thể cho giao diện và lớp trừu tượng.
▢ Các lớp con của lớp trừu tượng phải triển khai các thuộc tính trừu tượng.
Câu hỏi 3
Đối tượng sửa đổi chế độ hiển thị nào sau đây KHÔNG phải là đối tượng sửa đổi chế độ hiển thị Kotlin cho các thuộc tính, phương thức, v.v.?
▢ internal
▢ nosubclass
▢ protected
▢ private
Câu hỏi 4
Hãy xem xét lớp dữ liệu này:data class Fish(val name: String, val species:String, val colors:String)
Đoạn mã nào sau đây KHÔNG hợp lệ để tạo và phân tách một đối tượng Fish
?
▢ val (name1, species1, colors1) = Fish("Pat", "Plecostomus", "gold")
▢ val (name2, _, colors2) = Fish("Bitey", "shark", "gray")
▢ val (name3, species3, _) = Fish("Amy", "angelfish", "blue and black stripes")
▢ val (name4, species4, colors4) = Fish("Harry", "halibut")
Câu hỏi 5
Giả sử bạn sở hữu một vườn thú có nhiều động vật cần được chăm sóc. Mục nào sau đây KHÔNG thuộc quy trình triển khai hoạt động chăm sóc?
▢ Một interface
cho các loại thức ăn mà động vật ăn.
▢ Một lớp abstract Caretaker
mà bạn có thể tạo nhiều loại người chăm sóc.
▢ Một interface
để cung cấp nước sạch cho động vật.
▢ Lớp data
cho một mục trong lịch cho ăn.
Chuyển sang bài học tiếp theo:
Để biết thông tin tổng quan về khoá học này, bao gồm cả đường liên kết đến các lớp học lập trình khác, hãy xem "Chương trình đào tạo về Kotlin dành cho lập trình viên: Chào mừng bạn đến với khoá học".