Chương trình đào tạo về Kotlin dành cho lập trình viên 2: Kiến thức cơ bản về Kotlin

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ìm hiểu kiến thức cơ bản về ngôn ngữ lập trình Kotlin: loại dữ liệu, toán tử, biến, cấu trúc kiểm soát và biến có tính chất rỗng/không rỗng. Khoá học này hướng đến 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.

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ó

  • Cách tạo dự án trong IntelliJ IDEA
  • Cách mở và thực thi mã trong REPL (Vòng lặp Read-Eval-Print) của Kotlin trong IntelliJ IDEA (Tools > Kotlin > Kotlin REPL)

Kiến thức bạn sẽ học được

  • Cách sử dụng các kiểu dữ liệu, toán tử và biến Kotlin
  • Cách xử lý Boolean và các điều kiện
  • Sự khác biệt giữa các biến có thể có giá trị rỗng và biến không thể có giá trị rỗng
  • Cách mảng, danh sách và vòng lặp hoạt động trong Kotlin

Bạn sẽ thực hiện

  • Sử dụng Kotlin REPL để tìm hiểu những kiến thức cơ bản về Kotlin

Trong nhiệm vụ này, bạn sẽ tìm hiểu về các toán tử và kiểu trong ngôn ngữ lập trình Kotlin.

Bước 1: Khám phá các toán tử số học

  1. Mở IntelliJ IDEA (nếu chưa mở).
  2. Để mở Kotlin REPL, hãy chọn Tools (Công cụ) > Kotlin > Kotlin REPL.

Cũng giống như các ngôn ngữ khác, Kotlin sử dụng +, -, */ cho phép cộng, trừ, nhân và chia. Kotlin cũng hỗ trợ nhiều loại số, chẳng hạn như Int, Long, DoubleFloat.

  1. Nhập các biểu thức sau vào REPL. Để xem kết quả, hãy nhấn Control+Enter (Command+Enter trên máy Mac) sau mỗi kết quả.
1+1
⇒ res8: kotlin.Int = 2

53-3
⇒ res9: kotlin.Int = 50

50/10
⇒ res10: kotlin.Int = 5

1.0/2.0
⇒ res11: kotlin.Double = 0.5

2.0*3.5
⇒ res12: kotlin.Double = 7.0

Xin lưu ý rằng kết quả của phép toán sẽ giữ nguyên loại toán hạng, vì vậy, 1/2 = 0, nhưng 1,0/2,0 = 0,5.

  1. Hãy thử một số biểu thức với nhiều tổ hợp số nguyên và số thập phân.
6*50
⇒ res13: kotlin.Int = 300

6.0*50.0
⇒ res14: kotlin.Double = 300.0

6.0*50
⇒ res15: kotlin.Double = 300.0
  1. Gọi một số phương thức trên các số. Kotlin lưu giữ các số ở dạng nguyên thuỷ, nhưng cho phép bạn gọi phương thức cho các số đó như thể đó là đối tượng.
2.times(3)
⇒ res5: kotlin.Int = 6

3.5.plus(4)
⇒ res8: kotlin.Double = 7.5

2.4.div(2)
⇒ res9: kotlin.Double = 1.2

Bước 2: Thực hành sử dụng các kiểu

Kotlin không ngầm chuyển đổi giữa các loại số, vì vậy, bạn không thể chỉ định trực tiếp giá trị ngắn cho biến dài hoặc Byte cho Int. Nguyên nhân là do việc ngầm chuyển đổi số là căn nguyên chính dẫn đến lỗi trong chương trình. Bạn luôn có thể chỉ định các giá trị thuộc nhiều loại bằng cách truyền.

  1. Để xem một số kiểu truyền có thể, hãy xác định một biến thuộc loại Int trong REPL.
val i: Int = 6
  1. Tạo một biến mới, sau đó nhập tên biến như minh hoạ ở trên, theo sau là .to.
val b1 = i.to

IntelliJ IDEA sẽ hiển thị danh sách các nội dung có thể điền vào. Tính năng tự động hoàn thành này hoạt động cho các biến và đối tượng thuộc mọi loại.

  1. Chọn toByte() trong danh sách, sau đó in biến.
val b1 = i.toByte()
println(b1)
⇒ 6
  1. Chỉ định giá trị Byte cho các biến thuộc nhiều loại.
val b2: Byte = 1 // OK, literals are checked statically
println(b2)
⇒ 1

val i1: Int = b2
⇒ error: type mismatch: inferred type is Byte but Int was expected

val i2: String = b2
⇒ error: type mismatch: inferred type is Byte but String was expected

val i3: Double = b2
⇒ error: type mismatch: inferred type is Byte but Double was expected
  1. Đối với những câu lệnh gán trả về lỗi, hãy thử truyền chúng.
val i4: Int = b2.toInt() // OK!
println(i4)
⇒ 1

val i5: String = b2.toString()
println(i5)
⇒ 1

val i6: Double = b2.toDouble()
println(i6)
⇒ 1.0
  1. Để giúp các hằng số dài dễ đọc hơn, Kotlin cho phép bạn đặt dấu gạch dưới vào các số, ở những vị trí mà bạn thấy phù hợp. Hãy thử nhập các hằng số số học khác.
val oneMillion = 1_000_000
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010

Bước 3: Tìm hiểu giá trị của các loại biến

Kotlin hỗ trợ 2 loại biến: dễ thay đổi và không thay đổi. Với val, bạn có thể chỉ định một giá trị một lần. Nếu cố chỉ định lại giá trị nào đó, bạn sẽ gặp lỗi. Với var, bạn có thể chỉ định một giá trị, rồi thay đổi giá trị đó trong chương trình vào lúc khác.

  1. Xác định các biến bằng valvar, sau đó chỉ định các giá trị mới cho các biến đó.
var fish = 1
fish = 2
val aquarium = 1
aquarium = 2
⇒ error: val cannot be reassigned

Bạn có thể chỉ định một giá trị cho fish, sau đó chỉ định một giá trị mới cho biến này vì đây là biến được xác định bằng var. Việc cố gắng chỉ định một giá trị mới cho aquarium sẽ gây ra lỗi vì biến này được xác định bằng val.

Loại mà bạn lưu trữ ở dạng biến sẽ được dự đoán khi trình biên dịch có thể tìm ra loại dựa trên ngữ cảnh. Nếu muốn, bạn luôn có thể chỉ định rõ loại của một biến bằng cách dùng ký hiệu dấu hai chấm.

  1. Xác định một số biến và chỉ định rõ loại.
var fish: Int = 12
var lakes: Double = 2.5

Sau khi bạn hoặc trình biên dịch chỉ định một loại, bạn không thể thay đổi loại đó, nếu không sẽ gặp lỗi.

Bước 4: Tìm hiểu về chuỗi

Các chuỗi trong Kotlin hoạt động giống hệt chuỗi trong bất kỳ ngôn ngữ lập trình nào khác sử dụng " cho chuỗi và ' cho ký tự đơn lẻ. Bạn có thể ghép các chuỗi bằng toán tử +. Bạn có thể tạo mẫu chuỗi bằng cách kết hợp chuỗi với các giá trị; tên $variable sẽ được thay thế bằng văn bản đại diện cho giá trị. Đây được gọi là phép nội suy biến.

  1. Tạo mẫu chuỗi.
val numberOfFish = 5
val numberOfPlants = 12
"I have $numberOfFish fish" + " and $numberOfPlants plants"
⇒ res20: kotlin.String = I have 5 fish and 12 plants
  1. Tạo một mẫu chuỗi có biểu thức trong đó. Giống như trong các ngôn ngữ khác, giá trị có thể là kết quả của một biểu thức. Hãy dùng dấu ngoặc nhọn {} để xác định biểu thức.
"I have ${numberOfFish + numberOfPlants} fish and plants"
⇒ res21: kotlin.String = I have 17 fish and plants

Trong nhiệm vụ này, bạn sẽ tìm hiểu về kiểu boolean và cách kiểm tra điều kiện trong ngôn ngữ lập trình Kotlin. Giống như các ngôn ngữ khác, Kotlin có các giá trị boolean và toán tử boolean như nhỏ hơn, bằng, lớn hơn, v.v. (<, ==, >, !=, <=, >=).

  1. Viết một câu lệnh if/else.
val numberOfFish = 50
val numberOfPlants = 23
if (numberOfFish > numberOfPlants) {
    println("Good ratio!") 
} else {
    println("Unhealthy ratio")
}
⇒ Good ratio!
  1. Hãy thử một dải ô trong câu lệnh if. Trong Kotlin, điều kiện mà bạn kiểm tra cũng có thể sử dụng phạm vi.
val fish = 50
if (fish in 1..100) {
    println(fish)
}
⇒ 50
  1. Viết một if có nhiều trường hợp. Đối với các điều kiện phức tạp hơn, hãy dùng &&||. Giống như trong các ngôn ngữ khác, bạn có thể gặp nhiều trường hợp khi sử dụng else if.
if (numberOfFish == 0) {
    println("Empty tank")
} else if (numberOfFish < 40) {
    println("Got fish!")
} else {
    println("That's a lot of fish!")
}
⇒ That's a lot of fish!
  1. Hãy thử câu lệnh when. Có một cách hữu hiệu hơn để viết loạt câu lệnh if/else if/else trong Kotlin thông qua câu lệnh when, tương tự như câu lệnh switch trong các ngôn ngữ khác. Các điều kiện trong câu lệnh when cũng có thể sử dụng phạm vi.
when (numberOfFish) {
    0  -> println("Empty tank")
    in 1..39 -> println("Got fish!")
    else -> println("That's a lot of fish!")
}
⇒ That's a lot of fish!

Trong nhiệm vụ này, bạn sẽ tìm hiểu về các biến có thể có giá trị rỗng và biến không thể có giá trị rỗng. Các lỗi lập trình liên quan đến biến null chính là nguồn gốc của vô số lỗi. Kotlin tìm cách giảm số lỗi bằng việc đưa các biến không thể có giá trị null vào.

Bước 1: Tìm hiểu về khả năng có giá trị rỗng

Theo mặc định, các biến không thể là null.

  1. Khai báo một Int và chỉ định null cho Int đó.
var rocks: Int = null
⇒ error: null can not be a value of a non-null type Int
  1. Sử dụng toán tử dấu chấm hỏi, ?, sau loại để cho biết rằng một biến có thể có giá trị rỗng. Khai báo một Int? và chỉ định null cho Int? đó.
var marbles: Int? = null

Khi bạn có các loại dữ liệu phức tạp, chẳng hạn như một danh sách:

  • Bạn có thể cho phép các thành phần trong danh sách có giá trị null.
  • Bạn có thể cho phép danh sách có giá trị null, nhưng nếu danh sách không có giá trị null thì các thành phần sẽ không thể có giá trị null.
  • Bạn có thể cho phép cả danh sách và thành phần có giá trị null.

Danh sách và một số kiểu dữ liệu phức tạp khác sẽ được đề cập trong một nhiệm vụ sau.

Bước 2: Tìm hiểu về ? và toán tử ?:

Bạn có thể kiểm tra null bằng toán tử ?, giúp bạn không phải viết nhiều câu lệnh if/else.

  1. Viết một đoạn mã dài hơn để kiểm tra xem biến fishFoodTreats có phải không phải là null hay không. Sau đó, giảm dần biến đó.
var fishFoodTreats = 6
if (fishFoodTreats != null) {
    fishFoodTreats = fishFoodTreats.dec()
}
  1. Bây giờ, hãy xem cách viết của Kotlin, sử dụng toán tử ?.
var fishFoodTreats = 6
fishFoodTreats = fishFoodTreats?.dec()
  1. Bạn cũng có thể kiểm tra biến null toàn chuỗi bằng toán tử ?:. Hãy xem ví dụ sau:
fishFoodTreats = fishFoodTreats?.dec() ?: 0

Đây là cách viết tắt của "nếu fishFoodTreats không phải là null, hãy giảm dần và sử dụng biến đó; nếu không, hãy dùng giá trị sau ?:, tức là 0". Nếu fishFoodTreatsnull, quá trình đánh giá sẽ dừng lại và phương thức dec() sẽ không được gọi.

Một điểm về con trỏ rỗng

Nếu bạn thực sự thích NullPointerExceptions, Kotlin sẽ cho phép bạn lưu giữ giá trị đó. Toán tử xác nhận không có giá trị rỗng, !! (double-bang), sẽ chuyển đổi mọi giá trị thành loại không có giá trị rỗng và gửi một ngoại lệ nếu giá trị đó là null.

val len = s!!.length   // throws NullPointerException if s is null

Trong nhiệm vụ này, bạn sẽ tìm hiểu về mảng và danh sách, đồng thời tìm hiểu nhiều cách để tạo vòng lặp trong ngôn ngữ lập trình Kotlin.

Bước 1: Lập danh sách

Danh sách là một kiểu cơ bản trong Kotlin và tương tự như danh sách trong các ngôn ngữ khác.

  1. Khai báo một danh sách bằng listOf và in ra. Bạn không thể thay đổi danh sách này.
val school = listOf("mackerel", "trout", "halibut")
println(school)
⇒ [mackerel, trout, halibut]
  1. Khai báo một danh sách có thể thay đổi bằng cách sử dụng mutableListOf. Xoá một mục.
val myList = mutableListOf("tuna", "salmon", "shark")
myList.remove("shark")
⇒ res36: kotlin.Boolean = true

Phương thức remove() trả về true khi xoá thành công mục đã chuyển.

Bước 2: Tạo mảng

Giống như các ngôn ngữ khác, Kotlin có mảng. Khác với các danh sách trong Kotlin, có phiên bản có thể thay đổi và không thể thay đổi, Array không có phiên bản có thể thay đổi. Sau khi bạn tạo một mảng, kích thước của mảng sẽ là cố định. Bạn không thể thêm hoặc xóa các thành phần, trừ khi dùng cách sao chép sang một mảng mới.

Quy tắc về việc sử dụng valvar là như nhau đối với mảng và danh sách.

  1. Khai báo một mảng chuỗi bằng cách dùng arrayOf. Sau đó, dùng tiện ích mảng java.util.Arrays.toString() để in ra.
val school = arrayOf("shark", "salmon", "minnow")
println(java.util.Arrays.toString(school))
⇒ [shark, salmon, minnow]
  1. Mảng được khai báo bằng arrayOf không có loại nào liên kết với các phần tử, vì vậy bạn có thể kết hợp các loại, điều này rất hữu ích. Khai báo một mảng có nhiều loại.
val mix = arrayOf("fish", 2)
  1. Bạn cũng có thể khai báo các mảng chỉ có một loại cho tất cả các phần tử. Khai báo một mảng số nguyên bằng cách dùng intArrayOf(). nhưng cũng có các trình tạo hoặc hàm tạo thực thể tương ứng cho các mảng thuộc loại khác.
val numbers = intArrayOf(1,2,3)
  1. Kết hợp hai mảng bằng toán tử +.
val numbers = intArrayOf(1,2,3)
val numbers3 = intArrayOf(4,5,6)
val foo2 = numbers3 + numbers
println(foo2[5])
=> 3
  1. Hãy thử nhiều cách kết hợp mảng và danh sách lồng nhau. Giống như trong các ngôn ngữ khác, bạn có thể lồng các mảng và danh sách. Tức là khi đặt một mảng trong một mảng, bạn sẽ có một mảng các mảng chứ không phải một mảng được làm phẳng của nội dung của hai mảng. Các phần tử của một mảng cũng có thể là danh sách và các phần tử của danh sách có thể là mảng.
val numbers = intArrayOf(1, 2, 3)
val oceans = listOf("Atlantic", "Pacific")
val oddList = listOf(numbers, oceans, "salmon")
println(oddList)
⇒ [[I@89178b4, [Atlantic, Pacific], salmon]

Phần tử đầu tiên, numbers, là một Array. Khi bạn không dùng tiện ích mảng để in, Kotlin sẽ in địa chỉ thay vì nội dung của mảng.

  1. Một tính năng hay của Kotlin là bạn có thể khởi tạo mảng bằng mã thay vì khởi tạo chúng thành 0. Hãy thử ví dụ sau:
val array = Array (5) { it * 2 }
println(java.util.Arrays.toString(array))
⇒ [0, 2, 4, 6, 8]

Mã khởi chạy nằm giữa dấu ngoặc nhọn, {}. Trong mã, it đề cập đến chỉ mục mảng, bắt đầu từ 0.

Bước 3: Tạo vòng lặp

Giờ đây, khi đã có danh sách và mảng, việc lặp lại các phần tử sẽ diễn ra như bạn mong đợi.

  1. Tạo một mảng. Sử dụng vòng lặp for để lặp lại câu lệnh suốt toàn bộ mảng và in các phần tử.
val school = arrayOf("shark", "salmon", "minnow")
for (element in school) {
    print(element + " ")
}
⇒ shark salmon minnow
  1. Trong Kotlin, bạn có thể lặp theo các thành phần và chỉ mục cùng một lúc. Hãy thử ví dụ sau:
for ((index, element) in school.withIndex()) {
    println("Item at $index is $element\n")
}
⇒ Item at 0 is shark
Item at 1 is salmon
Item at 2 is minnow
  1. Hãy thử các kích thước và phạm vi bước khác nhau. Bạn có thể chỉ định phạm vi số hoặc phạm vi ký tự, theo thứ tự bảng chữ cái. Giống như trong các ngôn ngữ khác, bạn không phải tiến lên 1 bước. Bạn có thể lùi lại bằng cách sử dụng downTo.
for (i in 1..5) print(i)
⇒ 12345

for (i in 5 downTo 1) print(i)
⇒ 54321

for (i in 3..6 step 2) print(i)
⇒ 35

for (i in 'd'..'g') print (i)
⇒ defg
  1. Hãy thử một số đoạn nhạc lặp lại. Giống như các ngôn ngữ khác, Kotlin có vòng lặp while, vòng lặp do...while cũng như toán tử ++--. Kotlin cũng có vòng lặp repeat.
var bubbles = 0
while (bubbles < 50) {
    bubbles++
}
println("$bubbles bubbles in the water\n")

do {
    bubbles--
} while (bubbles > 50)
println("$bubbles bubbles in the water\n")

repeat(2) {
     println("A fish is swimming")
}
⇒ 50 bubbles in the water
49 bubbles in the water
A fish is swimmingA fish is swimming

Kotlin rất giống với các ngôn ngữ khác về những điểm cơ bản như toán tử, danh sách và vòng lặp, nhưng có một số điểm khác biệt quan trọng.

Các tính năng sau đây có thể khác trong Kotlin so với những gì bạn thường dùng trong các ngôn ngữ khác:

  • Không thể chuyển đổi ngầm các kiểu Kotlin – hãy sử dụng tính năng truyền.
  • Bạn chỉ có thể chỉ định các biến được khai báo bằng val một lần.
  • Theo mặc định, các biến Kotlin không thể có giá trị rỗng. Dùng ? để tạo các biến có tính chất rỗng.
  • Với Kotlin, bạn có thể lặp qua chỉ mục và các phần tử của một mảng cùng một lúc trong vòng lặp for.

Các cấu trúc lập trình Kotlin sau đây tương tự như các cấu trúc trong những ngôn ngữ khác:

  • Mảng và danh sách có thể có một loại hoặc nhiều loại.
  • Bạn có thể lồng các mảng và danh sách.
  • Bạn có thể tạo vòng lặp bằng for, while, do/whilerepeat.
  • Câu lệnh when là phiên bản của câu lệnh switch trong Kotlin, nhưng when linh hoạt hơ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.

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âu lệnh nào sau đây khai báo một danh sách chuỗi không thay đổi?

val school = arrayOf("shark", "salmon", "minnow")

var school = arrayOf("shark", "salmon", "minnow")

val school = listOf("shark", "salmon", "minnow")

val school = mutableListOf("shark", "salmon", "minnow")

Câu hỏi 2

Kết quả của đoạn mã sau sẽ là gì?
for (i in 3..8 step 2) print(i)

▢ 345678

▢ 468

▢ 38

▢ 357

Câu hỏi 3

Mục đích của dấu chấm hỏi trong mã này là gì?
var rocks: Int? = 3

▢ Loại biến rocks không cố định.

▢ Bạn có thể đặt biến rocks thành giá trị rỗng.

▢ Bạn không thể đặt biến rocks thành giá trị rỗng.

▢ Bạn không nên khởi tạo biến rocks ngay lập tức.

Chuyển sang bài học tiếp theo: 3. Hàm

Để 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".