Lớp học lập trình này nằm trong khóa học về Kotlin dành cho lập trình viên. Bạn sẽ nhận được nhiều giá trị nhất từ khóa học này nếu bạn làm việc qua các lớp học lập trình theo trình tự. Tùy thuộc vào kiến thức của mình, bạn có thể đọc lướt một số phần. Khóa học này dành cho những lập trình viên biế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 đã tìm hiểu một số tính năng hữu ích trong Kotlin, trong đó có các cặp, bộ sưu tập và hàm mở rộng.
Thay vì xây dựng một ứng dụng mẫu duy nhất, các bài học trong khóa học này được thiết kế để trang bị cho bạn kiến thức nhưng hãy độc lập với nhau để có thể đọc lướt các phần mà bạn quen thuộc. Nhiều ví dụ sử dụng chủ đề thủy cung để liên kết chúng với nhau. Và nếu bạn muốn xem toàn bộ câu chuyện về thủy cung, hãy xem khóa học Kotlin Bootcamp dành cho lập trình viên Udacity.
Kiến thức bạn cần có
- Cú pháp của hàm, lớp và phương thức Kotlin
- Cách làm việc với Kotlin\39;s REPL (Read-Eval-Print Loop) trong IntelliJ IDEA
- Cách tạo lớp học mới trong IntelliJ IDEA và chạy chương trình
Kiến thức bạn sẽ học được
- Cách làm việc với các cặp và bộ ba
- Thông tin thêm về bộ sưu tập
- Xác định và sử dụng hằng số
- Viết hàm mở rộng
Bạn sẽ thực hiện
- Tìm hiểu về các cặp, bộ ba và bản đồ băm trong REPL
- Tìm hiểu những cách sắp xếp hằng số
- Viết một hàm mở rộng và một thuộc tính của phần mở rộng
Trong nhiệm vụ này, bạn tìm hiểu về các cặp và bộ ba và phá hủy chúng. Cặp và bộ ba là lớp dữ liệu được tạo sẵn cho 2 hoặc 3 mục chung. Ví dụ: điều này có thể hữu ích khi có một hàm trả về nhiều giá trị.
Giả sử bạn có List
một con cá và một hàm isFreshWater()
để kiểm tra xem cá đó là cá nước ngọt hay cá nước mặn. List.partition()
trả về hai danh sách, một danh sách có các mục có điều kiện là true
và danh sách còn lại chọn các mục có điều kiện là false
.
val twoLists = fish.partition { isFreshWater(it) }
println("freshwater: ${twoLists.first}")
println("saltwater: ${twoLists.second}")
Bước 1: Tạo một số cặp và số ba
- Mở REPL (Công cụ > Kotlin > Kotlin REPL).
- Tạo một cặp, liên kết một phần thiết bị với mục đích sử dụng, sau đó in các giá trị. Bạn có thể tạo một cặp bằng cách tạo một biểu thức kết nối hai giá trị, chẳng hạn như hai chuỗi, với từ khóa
to
, sau đó sử dụng.first
hoặc.second
để tham chiếu mỗi giá trị.
val equipment = "fish net" to "catching fish"
println("${equipment.first} used for ${equipment.second}")
⇒ fish net used for catching fish
- Tạo ba dấu chấm và in bằng
toString()
, sau đó chuyển đổi thành một danh sách bằngtoList()
. Bạn tạo một bộ ba bằng cách sử dụngTriple()
có 3 giá trị. Hãy sử dụng.first
,.second
và.third
để tham chiếu đến từng giá trị.
val numbers = Triple(6, 9, 42)
println(numbers.toString())
println(numbers.toList())
⇒ (6, 9, 42) [6, 9, 42]
Các ví dụ ở trên sử dụng cùng một loại cho tất cả các phần của cặp hoặc thứ ba, nhưng điều đó là không bắt buộc. Các phần có thể là một chuỗi, một số hoặc một danh sách, ví dụ: một cặp hoặc ba lần.
- Tạo một cặp trong đó phần đầu tiên của cặp là chính một cặp.
val equipment2 = ("fish net" to "catching fish") to "equipment"
println("${equipment2.first} is ${equipment2.second}\n")
println("${equipment2.first.second}")
⇒ (fish net, catching fish) is equipment ⇒ catching fish
Bước 2: Cấu trúc một số cặp và ba
Việc tách các cặp và số ba thành các phần được gọi là hủy cấu trúc. Chỉ định cặp hoặc ba thành số lượng biến thích hợp, và Kotlin sẽ chỉ định giá trị của mỗi phần theo thứ tự.
- Hủy cấu trúc một cặp và in các giá trị.
val equipment = "fish net" to "catching fish"
val (tool, use) = equipment
println("$tool is used for $use")
⇒ fish net is used for catching fish
- Hủy cấu trúc bộ ba và in các giá trị.
val numbers = Triple(6, 9, 42)
val (n1, n2, n3) = numbers
println("$n1 $n2 $n3")
⇒ 6 9 42
Xin lưu ý rằng việc hủy cấu trúc các cặp và bộ ba sẽ hoạt động giống như đối với lớp dữ liệu đã được đề cập trong một lớp học lập trình trước đó.
Trong nhiệm vụ này, bạn tìm hiểu thêm về bộ sưu tập, bao gồm cả danh sách và loại bộ sưu tập mới, bản đồ băm.
Bước 1: Tìm hiểu thêm về danh sách
- Danh sách và danh sách có thể thay đổi đã được giới thiệu trong bài học trước. Chúng là cấu trúc dữ liệu rất hữu ích, do đó Kotlin cung cấp một số hàm tích hợp sẵn cho danh sách. Hãy xem lại danh sách hàm một phần này đối với các danh sách. Bạn có thể xem danh sách đầy đủ trong tài liệu về Kotlin trên
List
vàMutableList
.
Chức năng | Mục đích |
| Thêm một mục vào danh sách có thể thay đổi. |
| Xóa một mục khỏi danh sách có thể thay đổi. |
| Trả về bản sao của danh sách có các phần tử theo thứ tự đảo ngược. |
| Hãy trả về |
| Trả về một phần của danh sách, từ chỉ mục đầu tiên đến nhưng không bao gồm chỉ mục thứ hai. |
- Vẫn đang làm việc trong REPL, hãy tạo một danh sách các số và gọi đến
sum()
trên đó. Thao tác này sẽ tổng hợp tất cả các phần tử.
val list = listOf(1, 5, 3, 4)
println(list.sum())
⇒ 13
- Tạo danh sách các chuỗi và tổng danh sách.
val list2 = listOf("a", "bbb", "cc")
println(list2.sum())
⇒ error: none of the following functions can be called with the arguments supplied:
- Nếu phần tử không phải là
List
biết cách tính trực tiếp, chẳng hạn như một chuỗi, thì bạn có thể chỉ định cách tính tổng bằng cách sử dụng.sumBy()
, ví dụ: để tính tổng độ dài của mỗi chuỗi. Tên mặc định cho đối số hàm lambda làit
và tại đâyit
đề cập đến từng thành phần trong danh sách khi danh sách được chuyển sang.
val list2 = listOf("a", "bbb", "cc")
println(list2.sumBy { it.length })
⇒ 6
- Bạn có thể làm nhiều việc hơn với các danh sách. Một cách để xem chức năng có sẵn là tạo một danh sách trong IntelliJ IDEA, thêm dấu chấm rồi xem danh sách tự động hoàn thành trong chú giải công cụ. Quy tắc này áp dụng cho bất kỳ đối tượng nào. Dùng thử với danh sách.
- Chọn
listIterator()
từ danh sách, sau đó xem qua danh sách bằng một câu lệnhfor
và in tất cả các thành phần được phân tách bằng dấu cách.
val list2 = listOf("a", "bbb", "cc")
for (s in list2.listIterator()) {
println("$s ")
}
⇒ a bbb cc
Bước 2: Dùng thử bản đồ băm
Trong Kotlin, bạn có thể liên kết khá nhiều nội dung với hashMapOf()
. Bản đồ hàm băm giống như danh sách các cặp, trong đó giá trị đầu tiên đóng vai trò là một khóa.
- Tạo bản đồ băm phù hợp với các triệu chứng, chìa khóa và bệnh của cá, các giá trị.
val cures = hashMapOf("white spots" to "Ich", "red sores" to "hole disease")
- Sau đó, bạn có thể truy xuất giá trị bệnh dựa trên khóa triệu chứng, sử dụng dấu ngoặc vuông
get()
hoặc thậm chí ngắn hơn[]
.
println(cures.get("white spots"))
⇒ Ich
println(cures["red sores"])
⇒ hole disease
- Hãy thử chỉ định một dấu hiệu không phải là bản đồ.
println(cures["scale loss"])
⇒ null
Nếu khóa không nằm trong bản đồ, thì việc cố gắng trả về bệnh phù hợp sẽ trả về null
. Tùy thuộc vào dữ liệu bản đồ, thông thường sẽ không có kết quả khớp cho một khóa có thể. Đối với những trường hợp như vậy, Kotlin cung cấp hàm getOrDefault()
.
- Hãy thử tìm kiếm một khóa không khớp, sử dụng
getOrDefault()
.
println(cures.getOrDefault("bloating", "sorry, I don't know"))
⇒ sorry, I don't know
Nếu bạn cần thực hiện nhiều tác vụ hơn, chỉ cần trả về một giá trị, Kotlin sẽ cung cấp hàm getOrElse()
.
- Thay đổi mã của bạn để sử dụng
getOrElse()
thay vìgetOrDefault()
.
println(cures.getOrElse("bloating") {"No cure for this"})
⇒ No cure for this
Thay vì trả về một giá trị mặc định đơn giản, bất kỳ mã nào nằm giữa dấu ngoặc nhọn {}
đều được thực thi. Trong ví dụ, else
chỉ đơn giản trả về một chuỗi, nhưng cũng có thể ưa thích như việc tìm một trang web có chữa bệnh và trả lại chuỗi đó.
Giống như mutableListOf
, bạn cũng có thể tạo mutableMapOf
. Bản đồ có thể thay đổi cho phép bạn đặt và xóa mục. Có thể thay đổi nghĩa là có thể thay đổi, không thể thay đổi nghĩa là không thay đổi được.
- Tạo bản đồ khoảng không quảng cáo có thể được sửa đổi, ánh xạ chuỗi thiết bị tới số lượng mục. Tạo khu vực đó bằng lưới cá, sau đó thêm 3 chiếc bình nuôi cá vào khoảng không quảng cáo bằng
put()
và xóa lưới cá bằngremove()
.
val inventory = mutableMapOf("fish net" to 1)
inventory.put("tank scrubber", 3)
println(inventory.toString())
inventory.remove("fish net")
println(inventory.toString())
⇒ {fish net=1, tank scrubber=3}{tank scrubber=3}
Trong nhiệm vụ này, bạn tìm hiểu về các hằng số trong Kotlin và những cách sắp xếp khác nhau.
Bước 1: Tìm hiểu về const so với val
- Trong REPL, hãy thử tạo một hằng số. Trong Kotlin, bạn có thể tạo hằng số cấp cao nhất và gán cho các giá trị này tại thời điểm biên dịch bằng cách sử dụng
const val
.
const val rocks = 3
Giá trị được gán và không thể thay đổi, điều này có vẻ giống với việc khai báo val
thông thường. Vậy điểm khác biệt giữa const val
và val
là gì? Giá trị cho const val
được xác định tại thời điểm biên dịch, trong đó giá trị cho val
được xác định trong quá trình thực thi chương trình, tức là val
có thể được chỉ định bằng một hàm tại thời gian chạy.
Điều đó có nghĩa là val
có thể được chỉ định một giá trị từ một hàm, nhưng const val
không thể được chỉ định.
val value1 = complexFunctionCall() // OK
const val CONSTANT1 = complexFunctionCall() // NOT ok
Ngoài ra, const val
chỉ hoạt động ở cấp cao nhất và trong các lớp singleton được khai báo bằng object
, chứ không phải với các lớp thông thường. Bạn có thể dùng tính năng này để tạo một tệp hoặc đối tượng singleton chỉ chứa hằng số và nhập các hằng số đó khi cần.
object Constants {
const val CONSTANT2 = "object constant"
}
val foo = Constants.CONSTANT2
Bước 2: Tạo đối tượng companion
Kotlin không có khái niệm hằng số cấp lớp.
Để xác định hằng số trong một lớp, bạn phải gói các hằng số đó vào đối tượng companion kèm theo từ khóa companion
. Về cơ bản, đối tượng companion là một đối tượng singleton trong lớp.
- Hãy tạo một lớp có đối tượng companion chứa hằng số chuỗi.
class MyClass {
companion object {
const val CONSTANT3 = "constant in companion"
}
}
Sự khác biệt cơ bản giữa đối tượng companion và đối tượng thông thường là:
- Các đối tượng companion sẽ được khởi tạo từ hàm dựng tĩnh của lớp chứa, tức là các đối tượng này được tạo khi đối tượng được tạo.
- Các đối tượng thông thường được khởi tạo lazy trên quyền truy cập đầu tiên vào đối tượng đó; nghĩa là, khi được sử dụng lần đầu tiên.
Có nhiều hơn nữa, nhưng tất cả những gì bạn cần biết ngay bây giờ là gói hằng số trong các lớp trong đối tượng companion.
Trong nhiệm vụ này, bạn sẽ tìm hiểu về cách mở rộng hành vi của lớp học. Việc viết các hàm tiện ích để mở rộng hành vi của lớp là rất phổ biến. Kotlin cung cấp cú pháp thuận tiện để khai báo các hàm tiện ích này: hàm mở rộng.
Hàm mở rộng cho phép bạn thêm các hàm vào một lớp hiện có mà không cần truy cập vào mã nguồn của lớp đó. Ví dụ: bạn có thể khai báo các tệp này trong tệp Extension.kt thuộc gói của mình. Việc này không thực sự sửa đổi lớp, nhưng nó cho phép bạn dùng chú thích dấu chấm khi gọi hàm trên các đối tượng của lớp đó.
Bước 1: Viết hàm mở rộng
- Vẫn đang làm việc trong REPL, hãy viết một hàm mở rộng đơn giản,
hasSpaces()
để kiểm tra xem một chuỗi có chứa dấu cách hay không. Tên hàm có tiền tố là lớp mà hàm hoạt động trên đó. Bên trong hàm,this
đề cập đến đối tượng mà hàm này được gọi, vàit
tham chiếu đến biến lặp trong lệnh gọifind()
.
fun String.hasSpaces(): Boolean {
val found = this.find { it == ' ' }
return found != null
}
println("Does it have spaces?".hasSpaces())
⇒ true
- Bạn có thể đơn giản hóa hàm
hasSpaces()
.this
không cần thiết; hàm có thể giảm xuống một biểu thức và trả về, vì vậy, dấu ngoặc nhọn{}
xung quanh hàm đó cũng không cần thiết.
fun String.hasSpaces() = find { it == ' ' } != null
Bước 2: Tìm hiểu các giới hạn của phần mở rộng
Các hàm mở rộng chỉ có quyền truy cập vào API công khai của lớp mà chúng đang mở rộng. Các biến là private
không thể truy cập được.
- Hãy thử thêm các hàm mở rộng vào một tài sản được đánh dấu là
private
.
class AquariumPlant(val color: String, private val size: Int)
fun AquariumPlant.isRed() = color == "red" // OK
fun AquariumPlant.isBig() = size > 50 // gives error
⇒ error: cannot access 'size': it is private in 'AquariumPlant'
- Hãy kiểm tra mã bên dưới và tìm hiểu mã sẽ in.
open class AquariumPlant(val color: String, private val size: Int)
class GreenLeafyPlant(size: Int) : AquariumPlant("green", size)
fun AquariumPlant.print() = println("AquariumPlant")
fun GreenLeafyPlant.print() = println("GreenLeafyPlant")
val plant = GreenLeafyPlant(size = 10)
plant.print()
println("\n")
val aquariumPlant: AquariumPlant = plant
aquariumPlant.print() // what will it print?
⇒ GreenLeafyPlant AquariumPlant
plant.print()
ảnh in GreenLeafyPlant
. Bạn có thể muốn aquariumPlant.print()
in được cả GreenLeafyPlant
vì giá trị đó đã được chỉ định cho plant
. Tuy nhiên, loại được phân giải lúc biên dịch nên AquariumPlant
sẽ được in.
Bước 3: Thêm một thuộc tính của phần mở rộng
Ngoài các hàm mở rộng, Kotlin cũng cho phép bạn thêm các thuộc tính của phần mở rộng. Giống như các hàm mở rộng, bạn chỉ định lớp mà bạn đang mở rộng, theo sau là dấu chấm, theo sau là tên thuộc tính.
- Vẫn đang hoạt động trong REPL, hãy thêm thuộc tính tiện ích
isGreen
vàoAquariumPlant
, làtrue
nếu màu xanh lục.
val AquariumPlant.isGreen: Boolean
get() = color == "green"
Bạn có thể truy cập vào thuộc tính isGreen
giống như thuộc tính thông thường; khi được truy cập, phương thức getter cho isGreen
sẽ được gọi để nhận giá trị.
- In thuộc tính
isGreen
cho biếnaquariumPlant
và quan sát kết quả.
aquariumPlant.isGreen
⇒ res4: kotlin.Boolean = true
Bước 4: Tìm hiểu về bộ thu rỗng
Lớp bạn mở rộng được gọi là người nhận và có thể đặt lớp đó thành có thể có giá trị null. Nếu làm như vậy, biến this
dùng trong phần nội dung có thể là null
, vì vậy, hãy nhớ thử nghiệm biến đó. Bạn sẽ muốn dùng một bộ nhận có thể có giá trị null nếu bạn muốn người gọi muốn gọi phương thức mở rộng của bạn trên các biến có thể có giá trị null, hoặc nếu bạn muốn cung cấp một hành vi mặc định khi hàm của bạn được áp dụng cho null
.
- Vẫn đang làm việc trong REPL, hãy xác định phương thức
pull()
nhận một bộ nhận có thể có giá trị null. URL này được biểu thị bằng dấu chấm hỏi?
sau loại, trước dấu chấm. Bên trong phần nội dung, bạn có thể kiểm tra xemthis
có phải lànull
hay không bằng cách sử dụng questionmark-dot-apply?.apply.
fun AquariumPlant?.pull() {
this?.apply {
println("removing $this")
}
}
val plant: AquariumPlant? = null
plant.pull()
- Trong trường hợp này, không có kết quả khi bạn chạy chương trình. Vì
plant
lànull
nênprintln()
bên trong không được gọi.
Các hàm mở rộng rất hữu hiệu và hầu hết thư viện chuẩn Kotlin đều được triển khai dưới dạng hàm mở rộng.
Trong bài học này, bạn đã tìm hiểu thêm về bộ sưu tập, tìm hiểu về hằng số và nhận biết được sức mạnh của các hàm và thuộc tính của phần mở rộng.
- Bạn có thể sử dụng các cặp và bộ ba để trả về nhiều giá trị từ một hàm. Ví dụ:
val twoLists = fish.partition { isFreshWater(it) }
- Kotlin có nhiều hàm hữu ích cho
List
, chẳng hạn nhưreversed()
,contains()
vàsubList()
. - Bạn có thể sử dụng
HashMap
để liên kết các khóa với giá trị. Ví dụ:val cures = hashMapOf("white spots" to "Ich", "red sores" to "hole disease")
- Khai báo hằng số thời gian biên dịch bằng từ khóa
const
. Bạn có thể đặt chúng ở cấp cao nhất, sắp xếp chúng vào đối tượng singleton hoặc đặt chúng vào đối tượng companion. - Đối tượng companion là đối tượng singleton trong phần xác định lớp, được xác định bằng từ khóa
companion
. - Các hàm và thuộc tính của tiện ích có thể thêm chức năng vào một lớp. Ví dụ:
fun String.hasSpaces() = find { it == ' ' } != null
- Bộ nhận có thể có giá trị null cho phép bạn tạo các tiện ích trên một lớp có thể là
null
. Bạn có thể ghép nối toán tử?.
vớiapply
để kiểm tranull
trước khi thực thi mã. Ví dụ:this?.apply { println("removing $this") }
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 khóa học này hoặc nếu bạn gặp khó khăn, https://kotlinlang.org là điểm khởi đầu tốt nhất của bạn.
Pair
Triple
List
MutableList
HashMap
- Đối tượng đồng hành
- Tiện ích
- Trình thu thập dữ liệu không hợp lệ
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 biên dịch viên dựa trên web và một bộ tài liệu tham khảo đầy đủ có các ví dụ.
Khóa học Udacity
Để xem khóa học Udacity về chủ đề này, hãy xem Kotlin Bootcamp dành cho lập trình viên.
IntelliJ IDEA
Bạn có thể tìm thấy 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à có thể được giao cho học viên đang làm việc qua lớp học lập trình này trong khóa học do người hướng dẫn tổ chức. Người hướng dẫn có thể làm những việc sau:
- Giao bài tập về nhà nếu được yêu cầu.
- Trao đổi với học viên 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 những đề xuất này ít hay nhiều tùy ý. Do đó, họ có thể thoải mái giao 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ự mình làm việc qua lớp học lập trình này, hãy thoải mái sử dụng các bài tập về nhà này để kiểm tra kiến thức của bạn.
Trả lời những câu hỏi này
Câu hỏi 1
Mục nào sau đây trả về bản sao của danh sách?
▢ add()
▢ remove()
▢ reversed()
▢ contains()
Câu hỏi 2
Tiện ích nào trong số này có chức năng trên class AquariumPlant(val color: String, val size: Int, private val cost: Double, val leafy: Boolean)
sẽ gây ra lỗi trình biên dịch?
▢ fun AquariumPlant.isRed() = color == "red"
▢ fun AquariumPlant.isBig() = size > 45
▢ fun AquariumPlant.isExpensive() = cost > 10.00
▢ fun AquariumPlant.isNotLeafy() = leafy == false
Câu hỏi 3
Điểm nào sau đây không phải là nơi mà bạn có thể xác định hằng số bằng const val
?
▢ ở cấp cao nhất của tệp
▢ trong các lớp học thông thường
▢ trong đối tượng singleton
▢ trong đối tượng companion
Chuyển đến bài học tiếp theo:
Để biết thông tin tổng quan về khóa học, bao gồm cả các đường liên kết đến các lớp học lập trình khác, hãy xem "Kotlin Bootcamp dành cho lập trình viên: Chào mừng bạn đến với khóa học này?