Chương trình rèn luyện về Kotlin dành cho lập trình viên 5.2: Khái niệm chung

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 về các lớp, hàm và phương thức chung, cũng như cách chúng hoạt động trong Kotlin.

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 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 lớp, phương thức và hàm chung

Bạn sẽ thực hiện

  • Tạo một lớp chung và thêm các hạn chế
  • Tạo loại inout
  • Tạo các hàm, phương thức và hàm mở rộng chung

Giới thiệu chung

Kotlin, giống như nhiều ngôn ngữ lập trình, có các loại chung. Kiểu chung cho phép bạn đặt một lớp chung, do đó làm cho một lớp linh hoạt hơn nhiều.

Giả sử bạn đang triển khai một lớp MyList lưu giữ danh sách các mục. Nếu không có chung chung, bạn sẽ cần triển khai phiên bản MyList mới cho từng loại: một cho Double, một cho String, một cho Fish. Với mục chung, bạn có thể tạo danh sách chung chung để có thể lưu giữ mọi loại đối tượng. Điều này giống như việc tạo loại ký tự đại diện phù hợp với nhiều loại.

Để xác định một loại chung chung, hãy đặt T trong dấu ngoặc nhọn <T> sau tên lớp. (Bạn có thể sử dụng một chữ cái khác hoặc tên dài hơn, nhưng quy ước về loại chung là T.)

class MyList<T> {
    fun get(pos: Int): T {
        TODO("implement")
    }
    fun addItem(item: T) {}
}

Bạn có thể tham chiếu T như thể đó là một loại bình thường. Loại dữ liệu trả về cho get()T và tham số tới addItem() thuộc loại T. Tất nhiên, danh sách chung rất hữu ích, vì vậy lớp List được tích hợp vào Kotlin.

Bước 1: Tạo phân cấp loại

Ở bước này, bạn tạo một số lớp để sử dụng trong bước tiếp theo. Lớp học phụ đã được đề cập trong một lớp học lập trình trước đó, nhưng đây là một bài đánh giá ngắn.

  1. Để giữ cho ví dụ không bị lộn xộn, hãy tạo một gói mới trong src và gọi gói đó là generics.
  2. Trong gói generic, hãy tạo một tệp Aquarium.kt mới. Điều này cho phép bạn xác định lại những thứ sử dụng cùng một tên mà không xung đột, vì vậy phần còn lại của mã cho phòng thí nghiệm mã này sẽ được đưa vào tệp này.
  3. Tạo một hệ phân cấp loại nguồn cấp nước. Bắt đầu bằng cách đặt WaterSupply làm lớp open để có thể phân lớp con.
  4. Thêm một tham số var boolean, needsProcessing. Thao tác này sẽ tự động tạo một thuộc tính có thể thay đổi, cùng với một phương thức getter và setter.
  5. Tạo một lớp con TapWater mở rộng WaterSupply và chuyển true cho needsProcessing, vì nước nhấn có chứa các chất phụ gia không tốt cho cá.
  6. Trong TapWater, hãy xác định một hàm có tên là addChemicalCleaners() để đặt needsProcessing thành false sau khi làm sạch nước. Bạn có thể đặt thuộc tính needsProcessing từ TapWater vì nó là public theo mặc định và các lớp con có thể truy cập được. Sau đây là mã hoàn chỉnh.
package generics

open class WaterSupply(var needsProcessing: Boolean)

class TapWater : WaterSupply(true) {
   fun addChemicalCleaners() {
       needsProcessing = false
   }
}
  1. Tạo thêm hai lớp con của WaterSupply, được gọi là FishStoreWaterLakeWater. FishStoreWater không cần xử lý, nhưng LakeWater phải được lọc bằng phương thức filter(). Sau khi lọc, bạn không cần phải xử lý lại giá trị này, vì vậy, trong filter(), hãy đặt needsProcessing = false.
class FishStoreWater : WaterSupply(false)

class LakeWater : WaterSupply(true) {
   fun filter() {
       needsProcessing = false
   }
}

Nếu bạn cần thêm thông tin, hãy xem lại bài học trước về tính kế thừa trong Kotlin.

Bước 2: Tạo lớp học chung

Trong bước này, bạn sửa đổi lớp Aquarium để hỗ trợ nhiều loại vật tư nước.

  1. Trong tệp Aquarium.kt, hãy xác định một lớp Aquarium, trong đó dấu <T> nằm ở sau tên lớp.
  2. Thêm thuộc tính không thể thay đổi waterSupply thuộc loại T vào Aquarium.
class Aquarium<T>(val waterSupply: T)
  1. Viết một hàm có tên là genericsExample(). Phần này không phải là một phần của lớp nên chúng có thể nằm ở cấp cao nhất của tệp, chẳng hạn như hàm main() hoặc định nghĩa lớp. Trong hàm này, hãy tạo Aquarium và chuyển hàm đó thành WaterSupply. Vì thông số waterSupply là chung chung, bạn phải chỉ định loại trong dấu ngoặc nhọn <>.
fun genericsExample() {
    val aquarium = Aquarium<TapWater>(TapWater())
}
  1. Trong genericsExample(), mã của bạn có thể truy cập vào thủy cungwaterSupply. Vì thuộc loại TapWater nên bạn có thể gọi addChemicalCleaners() mà không cần bất kỳ loại truyền nào.
fun genericsExample() {
    val aquarium = Aquarium<TapWater>(TapWater())
    aquarium.waterSupply.addChemicalCleaners()
}
  1. Khi tạo đối tượng Aquarium, bạn có thể xóa dấu ngoặc nhọn và nội dung giữa các giá trị này vì Kotlin có khả năng dự đoán loại. Vì vậy, không có lý do gì để nói TapWater hai lần khi bạn tạo phiên bản. Loại này có thể được suy ra bởi đối số cho Aquarium; loại này vẫn sẽ tạo Aquarium thuộc loại TapWater.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    aquarium.waterSupply.addChemicalCleaners()
}
  1. Để xem điều gì đang xảy ra, hãy in needsProcessing trước và sau khi gọi addChemicalCleaners(). Dưới đây là hàm đã hoàn thành.
fun genericsExample() {
    val aquarium = Aquarium<TapWater>(TapWater())
    println("water needs processing: ${aquarium.waterSupply.needsProcessing}")
    aquarium.waterSupply.addChemicalCleaners()
    println("water needs processing: ${aquarium.waterSupply.needsProcessing}")
}
  1. Thêm hàm main() để gọi genericsExample(), sau đó chạy chương trình của bạn và quan sát kết quả.
fun main() {
    genericsExample()
}
⇒ water needs processing: true
water needs processing: false

Bước 3: Tạo thông tin cụ thể hơn

Chung chung có nghĩa là bạn có thể chuyển hầu hết mọi thứ, và đôi khi đó là một vấn đề. Trong bước này, bạn sẽ giúp lớp Aquarium cụ thể hơn về những nội dung bạn có thể đưa vào.

  1. Trong genericsExample(), hãy tạo một Aquarium, chuyển một chuỗi cho waterSupply, sau đó in thuộc tính waterSupply của thủy cung.
fun genericsExample() {
    val aquarium2 = Aquarium("string")
    println(aquarium2.waterSupply)
}
  1. Chạy chương trình để quan sát kết quả.
⇒ string

Kết quả là chuỗi bạn đã vượt qua vì Aquarium không đặt bất kỳ giới hạn nào trên T.Mọi loại, kể cả String, đều có thể được chuyển vào.

  1. Trong genericsExample(), hãy tạo một Aquarium khác, chuyển null cho waterSupply. Nếu waterSupply là null, hãy in "waterSupply is null".
fun genericsExample() {
    val aquarium3 = Aquarium(null)
    if (aquarium3.waterSupply == null) {
        println("waterSupply is null")
    }
}
  1. Chạy chương trình và quan sát kết quả.
⇒ waterSupply is null

Tại sao bạn có thể chuyển null khi tạo Aquarium? Bạn có thể thực hiện việc này vì theo mặc định, T là viết tắt của loại Any? có thể có giá trị null, loại ở đầu hệ phân cấp loại. Nội dung sau đây tương đương với nội dung bạn đã nhập trước đó.

class Aquarium<T: Any?>(val waterSupply: T)
  1. Để không cho phép chuyển null, hãy đặt T thuộc loại Any một cách rõ ràng bằng cách xóa ? sau Any.
class Aquarium<T: Any>(val waterSupply: T)

Trong bối cảnh này, Any được gọi là giới hạn chung. Điều này có nghĩa là bất kỳ loại nào cũng có thể được chuyển cho T miễn là không phải null.

  1. Điều bạn thực sự muốn đảm bảo là chỉ có thể chuyển WaterSupply (hoặc một trong các lớp con của lớp này) cho T. Hãy thay thế Any bằng WaterSupply để xác định một hạn chế chung chung hơn.
class Aquarium<T: WaterSupply>(val waterSupply: T)

Bước 4: Thêm chế độ kiểm tra khác

Trong bước này, bạn sẽ tìm hiểu về hàm check() giúp đảm bảo mã của bạn hoạt động như dự kiến. Hàm check() là một hàm thư viện chuẩn trong Kotlin. Hàm này đóng vai trò là một khẳng định và sẽ gửi một IllegalStateException nếu đối số của đối số đánh giá thành false.

  1. Thêm một phương thức addWater() vào lớp Aquarium để thêm nước, trong đó có check() đảm bảo bạn không cần xử lý nước trước.
class Aquarium<T: WaterSupply>(val waterSupply: T) {
    fun addWater() {
        check(!waterSupply.needsProcessing) { "water supply needs processing first" }
        println("adding water from $waterSupply")
    }    
}

Trong trường hợp này, nếu needsProcessing đúng, check() sẽ gửi một ngoại lệ.

  1. Trong genericsExample(), hãy thêm mã để tạo Aquarium với LakeWater, sau đó thêm một ít nước vào thực thể đó.
fun genericsExample() {
    val aquarium4 = Aquarium(LakeWater())
    aquarium4.addWater()
}
  1. Chạy chương trình của bạn và bạn sẽ nhận được ngoại lệ vì nước cần được lọc trước.
⇒ Exception in thread "main" java.lang.IllegalStateException: water supply needs processing first
        at Aquarium.generics.Aquarium.addWater(Aquarium.kt:21)
  1. Hãy thêm một cuộc gọi để lọc nước trước khi thêm vào Aquarium. Hiện tại, khi bạn chạy chương trình, sẽ không có ngoại lệ nào được gửi.
fun genericsExample() {
    val aquarium4 = Aquarium(LakeWater())
    aquarium4.waterSupply.filter()
    aquarium4.addWater()
}
⇒ adding water from generics.LakeWater@880ec60

Phần trên bao gồm những thông tin cơ bản về khái niệm chung. Những nhiệm vụ sau đây bao gồm nhiều chủ đề hơn, nhưng khái niệm quan trọng là cách khai báo và sử dụng một lớp chung chung có một hạn chế chung.

Trong tác vụ này, bạn sẽ tìm hiểu về loại trong và ngoài với loại chung. Loại in là loại chỉ có thể chuyển được vào một lớp, không trả về được. Loại out là một loại chỉ có thể được trả về từ một lớp.

Hãy xem lớp Aquarium và bạn sẽ thấy rằng loại chung chỉ từng được trả về khi nhận thuộc tính waterSupply. Không có phương thức nào lấy giá trị thuộc loại T làm thông số (ngoại trừ để xác định giá trị này trong hàm dựng). Kotlin cho phép bạn xác định chính xác out loại cho trường hợp này, đồng thời có thể dự đoán thêm thông tin về nơi an toàn để sử dụng các loại. Tương tự, bạn có thể xác định in loại cho các loại chung chỉ từng được chuyển vào phương thức, không trả về. Điều này cho phép Kotlin thực hiện các bước kiểm tra bổ sung về độ an toàn của mã.

Loại inout là các lệnh của hệ thống loại Kotlin. Việc giải thích toàn bộ hệ thống loại nằm ngoài phạm vi của chương trình đào tạo này (khó có liên quan); tuy nhiên, trình biên dịch sẽ gắn cờ các loại không được đánh dấu là inout một cách phù hợp, vì vậy bạn cần phải biết về chúng.

Bước 1: Xác định loại không tham gia

  1. Trong lớp Aquarium, hãy đổi T: WaterSupply thành loại out.
class Aquarium<out T: WaterSupply>(val waterSupply: T) {
    ...
}
  1. Trong cùng một tệp, nhưng ngoài lớp, hãy khai báo hàm addItemTo() dự kiến Aquarium trong WaterSupply.
fun addItemTo(aquarium: Aquarium<WaterSupply>) = println("item added")
  1. Gọi addItemTo() từ genericsExample() và chạy chương trình.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    addItemTo(aquarium)
}
⇒ item added

Kotlin có thể đảm bảo rằng addItemTo() sẽ không làm bất cứ loại nào không an toàn với WaterSupply chung, vì loại này được khai báo là loại out.

  1. Nếu bạn xóa từ khóa out, trình biên dịch sẽ báo lỗi khi gọi addItemTo(), vì Kotlin không thể đảm bảo rằng bạn không làm bất cứ điều gì không an toàn với loại đó.

Bước 2: Xác định loại

Loại in tương tự như loại out, nhưng đối với các loại chung chỉ từng được chuyển vào các hàm, sẽ không được trả về. Nếu cố gắng trả về loại in, bạn sẽ gặp lỗi với trình biên dịch. Trong ví dụ này, bạn sẽ xác định một loại in là một phần của giao diện.

  1. Trong Aquarium.kt, hãy xác định một giao diện CleanerT chung chung bị hạn chế ở WaterSupply. Vì đối số này chỉ được dùng làm đối số cho clean(), nên bạn có thể đặt thông số này làm thông số in.
interface Cleaner<in T: WaterSupply> {
    fun clean(waterSupply: T)
}
  1. Để sử dụng giao diện Cleaner, hãy tạo một lớp TapWaterCleaner triển khai Cleaner để dọn dẹp TapWater bằng cách thêm hóa chất.
class TapWaterCleaner : Cleaner<TapWater> {
    override fun clean(waterSupply: TapWater) =   waterSupply.addChemicalCleaners()
}
  1. Trong lớp Aquarium, hãy cập nhật addWater() để lấy Cleaner loại T và làm sạch nước trước khi thêm.
class Aquarium<out T: WaterSupply>(val waterSupply: T) {
    fun addWater(cleaner: Cleaner<T>) {
        if (waterSupply.needsProcessing) {
            cleaner.clean(waterSupply)
        }
        println("water added")
    }
}
  1. Hãy cập nhật mã mẫu genericsExample() để tạo TapWaterCleaner, Aquarium với TapWater, sau đó thêm một ít nước bằng chất tẩy rửa. Ứng dụng sẽ dùng trình dọn dẹp này nếu cần.
fun genericsExample() {
    val cleaner = TapWaterCleaner()
    val aquarium = Aquarium(TapWater())
    aquarium.addWater(cleaner)
}

Kotlin sẽ sử dụng thông tin về loại inout để đảm bảo mã của bạn dùng chung chung. Outin rất dễ nhớ: có thể chuyển out loại ra bên dưới dưới dạng giá trị trả về, loại in có thể được chuyển vào trong dưới dạng đối số.

Nếu bạn muốn tìm hiểu sâu hơn về các loại vấn đề trong loại và loại kết quả giải quyết, tài liệu sẽ trình bày chi tiết về các vấn đề đó.

Trong nhiệm vụ này, bạn sẽ tìm hiểu về các hàm chung và thời điểm sử dụng các hàm này. Thông thường, việc tạo một hàm chung là một ý tưởng tốt bất cứ khi nào hàm lấy đối số của một lớp có loại chung.

Bước 1: Tạo một hàm chung

  1. Trong tệp generic/Aquarium.kt, hãy tạo một hàm isWaterClean() nhận giá trị Aquarium. Bạn cần chỉ định loại chung của thông số; một tùy chọn là sử dụng WaterSupply.
fun isWaterClean(aquarium: Aquarium<WaterSupply>) {
   println("aquarium water is clean: ${aquarium.waterSupply.needsProcessing}")
}

Nhưng điều này có nghĩa là Aquarium phải có thông số loại out để được gọi. Đôi khi, out hoặc in quá hạn chế vì bạn cần sử dụng loại cho cả đầu vào và đầu ra. Bạn có thể xoá yêu cầu out bằng cách đặt hàm này thành giá trị chung.

  1. Để tạo hàm chung, hãy đặt dấu ngoặc nhọn sau từ khoá fun với loại chung T và mọi hạn chế, trong trường hợp này là WaterSupply. Thay đổi Aquarium để bị hạn chế bởi T thay vì trước WaterSupply.
fun <T: WaterSupply> isWaterClean(aquarium: Aquarium<T>) {
   println("aquarium water is clean: ${!aquarium.waterSupply.needsProcessing}")
}

T là một tham số loại của isWaterClean() đang được dùng để chỉ định loại chung của thủy cung. Quy luật này rất phổ biến và bạn nên dành chút thời gian để tìm hiểu.

  1. Gọi hàm isWaterClean() bằng cách chỉ định loại trong dấu ngoặc nhọn ngay sau tên hàm và trước dấu ngoặc đơn.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    isWaterClean<TapWater>(aquarium)
}
  1. Do đối số aquarium dự đoán loại, nên loại không cần thiết, vì vậy, hãy xóa loại đối số này. Chạy chương trình và quan sát kết quả.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    isWaterClean(aquarium)
}
⇒ aquarium water is clean: false

Bước 2: Tạo một phương thức chung có loại đã được sửa đổi

Bạn cũng có thể dùng các hàm chung cho các phương thức, ngay cả trong những lớp có loại chung chung. Ở bước này, bạn thêm một phương thức chung vào Aquarium để kiểm tra xem phương thức đó có loại WaterSupply hay không.

  1. Trong lớp Aquarium, hãy khai báo một phương thức, hasWaterSupplyOfType() nhận một thông số chung R (T đã được sử dụng) bị ràng buộc thành WaterSupply và trả về true nếu waterSupply thuộc loại R. Đây giống như hàm bạn đã khai báo trước đó, nhưng bên trong lớp Aquarium.
fun <R: WaterSupply> hasWaterSupplyOfType() = waterSupply is R
  1. Xin lưu ý rằng R cuối cùng được gạch dưới bằng màu đỏ. Giữ con trỏ lên để xem lỗi là gì.
  2. Để kiểm tra is, bạn cần thông báo cho Kotlin rằng loại này đã được sửa chữa hoặc là thực và có thể dùng trong hàm. Để làm điều đó, hãy đặt inline ở trước từ khóa funreified trước loại chung chung R.
inline fun <reified R: WaterSupply> hasWaterSupplyOfType() = waterSupply is R

Sau khi loại bỏ một loại, bạn có thể sử dụng loại đó như một loại thông thường—vì đó là loại thực sau khi cùng dòng. Điều đó có nghĩa là bạn có thể kiểm tra is bằng loại này.

Nếu bạn không dùng reified ở đây, thì loại sẽ không được "real" đủ để Kotlin cho phép is kiểm tra. Đó là vì các loại không được sửa đổi chỉ có sẵn tại thời điểm biên dịch và không thể sử dụng trong thời gian chạy bởi chương trình của bạn. Chúng tôi sẽ thảo luận thêm trong phần tiếp theo.

  1. Chuyển loại TapWater vào. Giống như gọi các hàm chung, hãy gọi các phương thức chung bằng cách sử dụng dấu ngoặc nhọn có loại đứng sau tên hàm. Chạy chương trình và quan sát kết quả.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    println(aquarium.hasWaterSupplyOfType<TapWater>())   // true
}
⇒ true

Bước 3: Tạo hàm mở rộng

Bạn cũng có thể sử dụng các loại đã sửa đổi cho hàm thông thường và hàm mở rộng.

  1. Bên ngoài lớp Aquarium, hãy xác định một hàm mở rộng trên WaterSupply có tên là isOfType(). Hàm này kiểm tra xem WaterSupply được chuyển có thuộc một loại cụ thể hay không, ví dụ như TapWater.
inline fun <reified T: WaterSupply> WaterSupply.isOfType() = this is T
  1. Gọi hàm mở rộng giống như một phương thức.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    println(aquarium.waterSupply.isOfType<TapWater>())  
}
⇒ true

Với những hàm mở rộng này, bạn không cần phải chọn loại Aquarium (Aquarium hoặc TowerTank hay lớp con khác), miễn là đó là Aquarium. Sử dụng cú pháp star-projection là một cách thuận tiện để chỉ định nhiều kết quả trùng khớp. Và khi bạn sử dụng thao tác chiếu dấu sao, Kotlin sẽ đảm bảo bạn không làm bất kỳ điều gì không an toàn.

  1. Để sử dụng phép chiếu dấu sao, hãy đặt <*> sau Aquarium. Di chuyển hasWaterSupplyOfType() thành hàm mở rộng vì hàm này không thực sự là một phần của API cốt lõi của Aquarium.
inline fun <reified R: WaterSupply> Aquarium<*>.hasWaterSupplyOfType() = waterSupply is R
  1. Thay đổi cuộc gọi thành hasWaterSupplyOfType() và chạy chương trình của bạn.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    println(aquarium.hasWaterSupplyOfType<TapWater>())
}
⇒ true

Trong ví dụ trước, bạn phải đánh dấu loại chung là reified và tạo hàm inline vì Kotlin cần biết về các biến đó trong thời gian chạy, chứ không chỉ là thời gian biên dịch.

Tất cả các loại chung chỉ được sử dụng tại thời điểm biên dịch bằng Kotlin. Điều này cho phép trình biên dịch đảm bảo rằng bạn đang làm mọi thứ một cách an toàn. Theo thời gian chạy, tất cả các loại chung sẽ bị xóa, do đó thông báo lỗi trước đó về việc kiểm tra loại đã xóa.

Hóa ra trình biên dịch có thể tạo mã chính xác mà không cần giữ các loại chung cho đến thời gian chạy. Nhưng điều đó có nghĩa là đôi khi bạn làm điều gì đó, chẳng hạn như is kiểm tra các loại chung chung, nên trình biên dịch không thể hỗ trợ. Đó là lý do Kotlin thêm loại được sửa đổi hoặc thực tế.

Bạn có thể đọc thêm trong bài viết Các loại dữ liệu sửa đổi và loại bỏ trong tài liệu về Kotlin.

Bài học này tập trung vào các thông tin chung, điều quan trọng để làm cho mã linh hoạt hơn và dễ sử dụng lại.

  • Tạo các lớp chung để làm cho mã linh hoạt hơn.
  • Thêm những hạn chế chung để giới hạn các loại được sử dụng chung.
  • Dùng các loại inout với loại chung để kiểm tra loại tốt hơn nhằm hạn chế các loại được chuyển vào hoặc trả về từ lớp.
  • Tạo các hàm và phương thức chung để làm việc với các loại chung. Ví dụ:
    fun <T: WaterSupply> isWaterClean(aquarium: Aquarium<T>) { ... }
  • Dùng các hàm mở rộng chung để thêm chức năng không phải cốt lõi vào một lớp.
  • Đôi khi, bạn cần tạo lại các loại bổ sung vì việc xóa loại. Không giống như các loại chung, các loại đã sửa sẽ tiếp tục có thời gian chạy.
  • Hãy sử dụng hàm check() để xác minh mã của bạn đang chạy như mong đợi. Ví dụ:
    check(!waterSupply.needsProcessing) { "water supply needs processing first" }

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.

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 các câu hỏi sau

Câu hỏi 1

Quy ước nào sau đây quy ước để đặt tên cho loại chung?

<Gen>

<Generic>

<T>

<X>

Câu hỏi 2

Hạn chế đối với các loại được cho phép đối với loại chung được gọi:

▢ hạn chế chung

▢ một hạn chế chung

▢ định nghĩa

▢ giới hạn loại chung

Câu hỏi 3

Có nghĩa là:

▢ Hệ thống đã tính toán tác động thực thi của một đối tượng.

▢ Một chỉ mục nhập bị hạn chế đã được đặt trên lớp này.

▢ Hệ thống đã tạo thông số loại chung thành loại thực.

▢ Một chỉ báo lỗi từ xa đã được kích hoạt.

Chuyển sang bài học tiếp theo: 6. Thao tác chức năng

Để 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?