Tạo và sử dụng các hàm trong Kotlin

1. Trước khi bắt đầu

Trong một lớp học lập trình trước đó, bạn đã thấy một chương trình đơn giản in Hello, world!. Trong các chương trình bạn đã viết từ trước đến nay, bạn đã biết hai hàm:

  • hàm main() mà bắt buộc phải có trong mỗi chương trình Kotlin. Đây là điểm vào hoặc điểm xuất phát của chương trình.
  • một hàm println() mà bạn đã gọi từ main() sang văn bản đầu ra.

Trong lớp học lập trình này, bạn sẽ tìm hiểu thêm về hàm.

Hàm cho phép bạn chia mã thành các phần có thể sử dụng lại thay vì đưa mọi thứ vào main(). Hàm là một yếu tố nền móng cần thiết để tạo dựng các ứng dụng Android và việc tìm hiểu cách định nghĩa cũng như sử dụng các hàm này là một bước quan trọng trong hành trình trở thành một nhà phát triển Android.

Điều kiện tiên quyết

  • Kiến thức cơ bản về lập trình Kotlin, bao gồm các biến cũng như các hàm println()main()

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

  • Cách định nghĩa và gọi hàm riêng của bạn.
  • Cách trả về các giá trị từ một hàm mà bạn có thể lưu trong biến.
  • Cách định nghĩa và gọi hàm có nhiều tham số.
  • Cách gọi hàm có đối số được đặt tên.
  • Cách đặt các giá trị mặc định cho các tham số của hàm.

Những thứ bạn cần

  • Một trình duyệt web có quyền truy cập vào Kotlin Playground

2. Định nghĩa và gọi hàm

Trước khi tìm hiểu chuyên sâu về các chức năng, hãy xem lại một số thuật ngữ cơ bản.

  • Việc khai báo (hoặc định nghĩa) hàm sẽ sử dụng từ khoá fun và thêm mã vào dấu ngoặc nhọn. Mã này có chứa hướng dẫn cần thiết để thực hiện một nhiệm vụ.
  • Khi gọi một hàm, tất cả các mã có trong hàm đó sẽ được thực thi.

Cho đến nay, bạn đã viết tất cả các mã trong hàm main(). Trên thực tế, hàm main() không được gọi ở bất cứ vị trí nào trong mã. Thay vào đó, trình biên dịch Kotlin sẽ sử dụng hàm này làm điểm xuất phát. Hàm main() chỉ được dùng để thêm mã khác mà bạn muốn thực thi, chẳng hạn như các lệnh gọi đến hàm println().

Hàm println() là một phần của ngôn ngữ Kotlin. Tuy nhiên, bạn có thể định nghĩa các hàm của riêng mình. Nhờ vậy, bạn có thể dùng lại mã của mình nếu cần phải gọi mã này nhiều lần. Hãy lấy chương trình sau làm ví dụ.

fun main() {
    println("Happy Birthday, Rover!")
    println("You are now 5 years old!")
}

Hàm main() bao gồm hai câu lệnh println()—một câu lệnh để chúc mừng sinh nhật Rover và một câu lệnh khác cho biết tuổi của Rover.

Mặc dù Kotlin cho phép bạn đặt tất cả các mã vào hàm main(), nhưng không phải lúc nào bạn cũng muốn làm vậy. Ví dụ: nếu bạn cũng muốn chương trình của mình có chứa lời chúc năm mới, thì hàm chính cũng phải bao gồm các lệnh gọi đến println(). Hoặc bạn có thể muốn gửi lời chúc đến Rover nhiều lần. Bạn chỉ cần sao chép và dán mã hoặc tạo một hàm riêng biệt cho lời chúc sinh nhật. Bạn sẽ thực hiện việc thứ hai. Việc tạo các hàm riêng biệt cho những nhiệm vụ cụ thể mang lại một số lợi ích.

  • Mã có thể sử dụng lại: Thay vì sao chép và dán mã mà bạn muốn dùng nhiều lần, bạn chỉ cần gọi một hàm khi cần.
  • Khả năng đọc: Việc đảm bảo các chức năng thực hiện một và chỉ một nhiệm vụ cụ thể sẽ giúp các nhà phát triển và thành viên nhóm khác cũng như bản thân bạn trong tương lai biết chính xác chức năng của đoạn mã.

Biểu đồ sau đây thể hiện cú pháp để định nghĩa một hàm.

Cú pháp để định nghĩa một hàm

Định nghĩa hàm bắt đầu bằng từ khoá fun, theo sau là tên hàm, một cặp dấu ngoặc đơn và một cặp dấu ngoặc nhọn. Bên trong dấu ngoặc nhọn là mã sẽ chạy khi hàm được gọi.

Bạn sẽ tạo một hàm mới để chuyển 2 câu lệnh println() ra khỏi hàm main().

  1. Trên trình duyệt, hãy mở Kotlin Playground và thay thế các nội dung bằng mã sau.
fun main() {
    println("Happy Birthday, Rover!")
    println("You are now 5 years old!")
}
  1. Sau hàm main(), hãy xác định một hàm mới có tên là birthdayGreeting(). Hàm này được khai báo bằng cú pháp giống như hàm main().
fun main() {
    println("Happy Birthday, Rover!")
    println("You are now 5 years old!")
}

fun birthdayGreeting() {

}
  1. Chuyển 2 câu lệnh println() từ main() vào dấu ngoặc nhọn của hàm birthdayGreeting().
fun main() {

}

fun birthdayGreeting() {
    println("Happy Birthday, Rover!")
    println("You are now 5 years old!")
}
  1. Trong hàm main(), hãy gọi hàm birthdayGreeting(). Mã hoàn tất của bạn sẽ có dạng như sau:
fun main() {
    birthdayGreeting()
}

fun birthdayGreeting() {
    println("Happy Birthday, Rover!")
    println("You are now 5 years old!")
}
  1. Chạy mã. Bạn sẽ thấy kết quả sau đây:
Happy Birthday, Rover!
You are now 5 years old!

3. Trả về giá trị từ một hàm

Trong những ứng dụng phức tạp hơn, các hàm còn có thể làm nhiều việc, ngoài việc in văn bản.

Các hàm Kotlin cũng có thể tạo dữ liệu được gọi là giá trị trả về. Giá trị này được lưu trong một biến mà bạn có thể dùng ở những nơi khác trong mã.

Khi định nghĩa một hàm, bạn có thể chỉ định kiểu dữ liệu cho giá trị mà mình muốn trả về. Chỉ định kiểu dữ liệu trả về bằng cách đặt dấu hai chấm (:) sau dấu ngoặc đơn, một khoảng trống và tên kiểu (Int, String, v.v.). Sau đó, một dấu cách sẽ được đặt giữa kiểu dữ liệu trả về và dấu ngoặc nhọn mở. Trong phần nội dung của hàm, sau tất cả câu lệnh, bạn sẽ sử dụng câu lệnh trả về để chỉ định giá trị mà mình muốn hàm trả về. Câu lệnh trả về bao gồm từ khoá return nằm trước giá trị (chẳng hạn như biến) mà bạn muốn hàm trả về ở dạng dữ liệu đầu ra.

Dưới đây là cú pháp khai báo hàm có cùng kiểu dữ liệu trả về.

Cú pháp để khai báo hàm có cùng kiểu dữ liệu trả về

Kiểu Unit

Theo mặc định, nếu bạn không chỉ định loại dữ liệu trả về, thì loại dữ liệu trả về mặc định là Unit. Unit có nghĩa là hàm không trả về một giá trị. Unit tương đương với các loại dữ liệu trả về không có giá trị ở các ngôn ngữ khác (void ở Java và C; bộ Void/dữ liệu rỗng () ở Swift; None ở Python, v.v.). Bất kỳ hàm nào không trả về một giá trị đều sẽ trả về Unit. Bạn có thể nhận thấy điều này bằng cách sửa đổi mã để trả về Unit.

  1. Trong phần khai báo hàm cho birthdayGreeting(), hãy thêm dấu hai chấm sau dấu ngoặc đơn đóng và chỉ định loại dữ liệu trả về là Unit.
fun main() {
    birthdayGreeting()
}

fun birthdayGreeting(): Unit {
    println("Happy Birthday, Rover!")
    println("You are now 5 years old!")
}
  1. Chạy mã và bạn sẽ thấy rằng mọi thứ vẫn hoạt động.
Happy Birthday, Rover!
You are now 5 years old!

Bạn không bắt buộc phải chỉ định loại dữ liệu trả về Unit trong Kotlin. Đối với các hàm không trả về dữ liệu nào hoặc trả về Unit, bạn không cần dùng câu lệnh trả về.

Trả về String từ birthdayGreeting()

Để minh hoạ cách hàm có thể trả về một giá trị, bạn sẽ sửa đổi hàm birthdayGreeting() để trả về một chuỗi thay vì chỉ in kết quả.

  1. Thay thế loại trả về Unit bằng String.
fun birthdayGreeting(): String {
    println("Happy Birthday, Rover!")
    println("You are now 5 years old!")
}
  1. Chạy mã. Bạn sẽ thấy thông báo lỗi. Nếu bạn khai báo kiểu dữ liệu trả về cho một hàm (ví dụ: String), hàm đó phải bao gồm một câu lệnh return.
A 'return' expression required in a function with a block body ('{...}')
  1. Bạn chỉ có thể trả về 1 chuỗi từ một hàm, chứ không phải 2. Thay các câu lệnh println() có 2 biến là nameGreetingageGreeting, bằng cách dùng từ khoá val. Do bạn đã xoá các lệnh gọi đến println() từ birthdayGreeting(), nên việc gọi birthdayGreeting() sẽ không in kết quả nào.
fun birthdayGreeting(): String {
    val nameGreeting = "Happy Birthday, Rover!"
    val ageGreeting = "You are now 5 years old!"
}
  1. Hãy thêm câu lệnh return bằng cách dùng cú pháp định dạng chuỗi (bạn đã tìm hiểu trong lớp học lập trình trước) để trả về một chuỗi từ hàm bao gồm cả lời chúc.

Để định dạng lời chúc trên một dòng riêng biệt, bạn cũng cần dùng ký tự thoát \n. Ký tự thoát này giống như \" mà bạn đã tìm hiểu trong một lớp học lập trình trước. Ký tự \n được thay thế cho một dòng mới để 2 lời chào nằm trên các dòng riêng biệt.

fun birthdayGreeting(): String {
    val nameGreeting = "Happy Birthday, Rover!"
    val ageGreeting = "You are now 5 years old!"
    return "$nameGreeting\n$ageGreeting"
}
  1. Trong main(), vì birthdayGreeting() trả về một giá trị nên bạn có thể lưu kết quả trong một biến chuỗi. Khai báo biến greeting bằng val để lưu trữ kết quả của lệnh gọi birthdayGreeting().
fun main() {
    val greeting = birthdayGreeting()
}
  1. Trong main(), hãy gọi println() để in chuỗi greeting. Hàm main() giờ đây phải có dạng như sau.
fun main() {
    val greeting = birthdayGreeting()
    println(greeting)
}
  1. Chạy mã và bạn sẽ thấy rằng kết quả vẫn giống như trước: Khi giá trị được trả về, bạn có thể lưu kết quả này trong một biến. Tuy nhiên, điều gì sẽ xảy ra nếu bạn gọi hàm birthdayGreeting() bên trong hàm println()?
Happy Birthday, Rover!
You are now 5 years old!
  1. Hãy xoá biến đó rồi chuyển kết quả của lệnh gọi hàm birthdayGreeting() vào hàm println():
fun main() {
    println(birthdayGreeting())
}
  1. Chạy mã rồi quan sát kết quả. Giá trị trả về của lệnh gọi birthdayGreeting() được chuyển trực tiếp vào println().
Happy Birthday, Rover!
You are now 5 years old!

4. Thêm một tham số vào hàm birthdayGreeting()

Như đã thấy, khi gọi hàm println(), bạn có thể thêm một chuỗi bên trong dấu ngoặc đơn hoặc chuyển một giá trị vào hàm. Bạn có thể làm như vậy với hàm birthdayGreeting(). Tuy nhiên, trước tiên bạn cần phải thêm một thông số vào birthdayGreeting().

Tham số chỉ định tên của biến và loại dữ liệu mà bạn có thể chuyển vào hàm dưới dạng dữ liệu cần truy cập bên trong hàm. Các tham số được khai báo trong dấu ngoặc đơn sau tên hàm.

Cú pháp để khai báo hàm có tham số và kiểu dữ liệu trả về

Mỗi tham số gồm có một tên biến và kiểu dữ liệu, được phân tách bằng dấu hai chấm và một dấu cách. Nhiều tham số được phân tách bằng dấu phẩy.

Hiện tại, bạn chỉ có thể dùng hàm birthdayGreeting() để gửi lời chúc đến Rover. Bạn sẽ thêm một tham số vào hàm birthdayGreeting() để có thể gửi lời chúc đến tên bất kỳ mà bạn chuyển vào hàm.

  1. Trong dấu ngoặc đơn của hàm birthdayGreeting(), hãy thêm một tham số name thuộc loại String bằng cú pháp name: String.
fun birthdayGreeting(name: String): String {
    val nameGreeting = "Happy Birthday, Rover!"
    val ageGreeting = "You are now 5 years old!"
    return "$nameGreeting\n$ageGreeting"
}

Tham số xác định ở bước trước hoạt động như một biến được khai báo bằng từ khoá val. Bạn có thể dùng tham số này ở vị trí bất kỳ trong hàm birthdayGreeting(). Trong lớp học lập trình trước, bạn đã tìm hiểu cách chèn giá trị của biến vào một chuỗi.

  1. Thay Rover trong chuỗi nameGreeting bằng ký hiệu $, sau đó là tham số name.
fun birthdayGreeting(name: String): String {
    val nameGreeting = "Happy Birthday, $name!"
    val ageGreeting = "You are now 5 years old!"
    return "$nameGreeting\n$ageGreeting"
}
  1. Chạy mã rồi quan sát lỗi. Bây giờ, bạn đã khai báo thông số name, bạn cần chuyển một String khi gọi birthdayGreeting(). Khi gọi hàm nhận một tham số, bạn sẽ chuyển một đối số vào hàm đó. Đối số là giá trị mà bạn chuyển, chẳng hạn như "Rover".
No value passed for parameter 'name'
  1. Chuyển "Rover" vào lệnh gọi birthdayGreeting() trong main().
fun main() {
    println(birthdayGreeting("Rover"))
}
  1. Chạy mã rồi quan sát kết quả. Tên Rover là do tham số name tạo ra.
Happy Birthday, Rover!
You are now 5 years old!
  1. birthdayGreeting() nhận một thông số nên bạn có thể gọi tên đó bằng một tên không phải Rover. Thêm một lệnh gọi khác vào birthdayGreeting() bên trong lệnh gọi tới println(), chuyển đối số "Rex".
println(birthdayGreeting("Rover"))
println(birthdayGreeting("Rex"))
  1. Chạy lại mã và quan sát kết quả thay đổi dựa trên đối số được truyền vào birthdayGreeting().
Happy Birthday, Rover!
You are now 5 years old!
Happy Birthday, Rex!
You are now 5 years old!

5. Hàm có nhiều tham số

Trước đó, bạn đã thêm một tham số để thay đổi lời chúc theo tên. Tuy nhiên, bạn cũng có thể xác định nhiều tham số cho một hàm, ngay cả khi các tham số đó thuộc nhiều loại dữ liệu. Trong phần này, bạn sẽ sửa đổi lời chúc để lời chúc đó cũng thay đổi theo tuổi của chú chó.

Hàm có nhiều tham số

Các tham số đã định nghĩa được phân tách bằng dấu phẩy. Tương tự, khi gọi một hàm có nhiều tham số, bạn cũng sẽ phân tách các đối số được chuyển vào bằng dấu phẩy. Hãy xem ví dụ thực tế.

  1. Sau tham số name, hãy thêm một tham số age thuộc loại Int vào hàm birthdayGreeting(). Thông tin khai báo hàm mới phải có hai tham số nameage, được phân tách bằng dấu phẩy:
fun birthdayGreeting(name: String, age: Int): String {
    val nameGreeting = "Happy Birthday, $name!"
    val ageGreeting = "You are now 5 years old!"
    return "$nameGreeting\n$ageGreeting"
}
  1. Chuỗi lời chào mới phải sử dụng thông số age. Cập nhật hàm birthdayGreeting() để sử dụng giá trị của thông số age trong chuỗi ageGreeting.
fun birthdayGreeting(name: String, age: Int): String {
    val nameGreeting = "Happy Birthday, $name!"
    val ageGreeting = "You are now $age years old!"
    return "$nameGreeting\n$ageGreeting"
}
  1. Chạy hàm này rồi xem các lỗi trong kết quả:
No value passed for parameter 'age'
No value passed for parameter 'age'
  1. Sửa đổi 2 lệnh gọi đến hàm birthdayGreeting() trong main() để chuyển vào tham số tuổi khác cho mỗi chú chó. Chuyển vào 5 cho tham số tuổi của Rover và 2 cho tham số tuổi của Rex.
fun main() {
    println(birthdayGreeting("Rover", 5))
    println(birthdayGreeting("Rex", 2))
}
  1. Chạy mã. Giờ bạn đã chuyển vào các giá trị cho cả hai tham số, nên kết quả sẽ phản ánh tên và tuổi của từng chú chó khi bạn gọi hàm.
Happy Birthday, Rover!
You are now 5 years old!
Happy Birthday, Rex!
You are now 2 years old!

Chữ ký hàm

Cho đến nay, bạn đã biết cách xác định tên hàm, dữ liệu đầu vào (tham số) và kết quả đầu ra. Tên hàm cùng với dữ liệu đầu vào (tham số) được gọi chung là chữ ký hàm. Chữ ký hàm bao gồm mọi thông tin trước kiểu dữ liệu trả về và xuất hiện trong đoạn mã sau đây.

fun birthdayGreeting(name: String, age: Int)

Các tham số (phân tách bằng dấu phẩy) đôi khi được gọi là danh sách tham số.

Bạn thường thấy các cụm từ này trong tài liệu về mã do các nhà phát triển khác viết. Chữ ký hàm cho bạn biết tên hàm và loại dữ liệu có thể truyền.

Bạn đã tìm hiểu nhiều cú pháp mới liên quan đến việc định nghĩa hàm. Hãy xem sơ đồ tóm tắt cú pháp hàm sau đây.

Sơ đồ tóm tắt cú pháp hàm

6. Đối số được đặt tên

Trong các ví dụ trước, bạn không cần chỉ định tên tham số, name hoặc age, khi gọi hàm. Tuy nhiên, bạn có thể làm vậy nếu muốn. Ví dụ: bạn có thể gọi một hàm có nhiều tham số hoặc chuyển các đối số vào theo thứ tự khác, chẳng hạn như đặt tham số age trước tham số name. Khi bạn bao gồm tên thông số khi gọi một hàm, hàm này được gọi là đối số được đặt tên. Hãy thử dùng một đối số được đặt tên bằng hàm birthdayGreeting().

  1. Sửa đổi lệnh gọi cho Rex để dùng các đối số được đặt tên như trong đoạn mã sau. Bạn có thể làm việc này bằng cách thêm tên tham số, theo sau là dấu bằng rồi đến giá trị (ví dụ: name = "Rex").
println(birthdayGreeting(name = "Rex", age = 2))
  1. Chạy mã và bạn sẽ thấy rằng kết quả không thay đổi:
Happy Birthday, Rover!
You are now 5 years old!
Happy Birthday, Rex!
You are now 2 years old!
  1. Sắp xếp lại thứ tự các đối số được đặt tên. Ví dụ: đặt đối số được đặt tên age trước đối số được đặt tên name.
println(birthdayGreeting(age = 2, name = "Rex"))
  1. Chạy mã và bạn sẽ thấy rằng kết quả vẫn như cũ. Mặc dù bạn đã thay đổi thứ tự của các đối số, nhưng các giá trị tương tự vẫn được chuyển vào các tham số đó.
Happy Birthday, Rover!
You are now 5 years old!
Happy Birthday, Rex!
You are now 2 years old!

7. Đối số mặc định

Các tham số của hàm cũng có thể chỉ định các đối số mặc định. Có thể Rover là chú chó bạn yêu thích, hoặc bạn muốn gọi một hàm bằng các đối số cụ thể trong hầu hết các trường hợp. Khi gọi một hàm, bạn có thể chọn bỏ qua đối số mang giá trị mặc định. Trong trường hợp này, giá trị mặc định sẽ được dùng.

Để thêm đối số mặc định, hãy thêm toán tử gán (=) sau loại dữ liệu cho tham số và đặt một giá trị cho đối số này. Sửa đổi mã của bạn để sử dụng đối số mặc định.

  1. Trong hàm birthdayGreeting(), hãy đặt tham số name thành giá trị mặc định "Rover".
fun birthdayGreeting(name: String = "Rover", age: Int): String {
    return "Happy Birthday, $name! You are now $age years old!"
}
  1. Trong lệnh gọi đầu tiên tới birthdayGreeting() cho Rover trong main(), hãy đặt đối số được đặt tên age thành 5. Vì tham số age được định nghĩa sau name nên bạn cần dùng đối số được đặt tên age. Nếu không có đối số có tên, Kotlin sẽ giả định thứ tự các đối số giống như thứ tự những tham số được xác định. Việc sử dụng đối số có tên là nhằm đảm bảo Kotlin nhận được Int cho tham số age.
println(birthdayGreeting(age = 5))
println(birthdayGreeting("Rex", 2))
  1. Chạy mã. Lệnh gọi đầu tiên tới hàm birthdayGreeting() sẽ in tên "Rover" vì bạn chưa chỉ định tên. Lệnh gọi thứ hai đến birthdayGreeting() vẫn sử dụng giá trị Rex mà bạn đã chuyển cho name.
Happy Birthday, Rover! You are now 5 years old!
Happy Birthday, Rex! You are now 2 years old!
  1. Xoá tên khỏi lệnh gọi thứ hai đến hàm birthdayGreeting(). Xin nhắc lại rằng vì name bị bỏ qua nên bạn cần sử dụng một đối số có tên cho độ tuổi.
println(birthdayGreeting(age = 5))
println(birthdayGreeting(age = 2))
  1. Chạy mã và bạn sẽ thấy rằng cả hai lệnh gọi tới birthdayGreeting() đều in "Rover" dưới dạng tên vì không có đối số có tên nào được truyền vào.
Happy Birthday, Rover! You are now 5 years old!
Happy Birthday, Rover! You are now 2 years old!

8. Kết luận

Xin chúc mừng! Bạn đã tìm hiểu cách định nghĩa và gọi hàm trong Kotlin.

Tóm tắt

  • Hàm được định nghĩa bằng từ khoá fun và chứa các đoạn mã có thể sử dụng lại.
  • Hàm giúp tạo ra các chương trình lớn để duy trì và tránh việc lặp lại mã không cần thiết.
  • Hàm có thể trả về một giá trị mà bạn có thể lưu trữ trong biến để sử dụng sau này.
  • Hàm có thể nhận các tham số là các biến có sẵn trong nội dung hàm.
  • Đối số là các giá trị mà bạn truyền vào khi gọi một hàm.
  • Bạn có thể đặt tên cho đối số khi gọi một hàm. Khi sử dụng các đối số được đặt tên, bạn có thể sắp xếp lại thứ tự các đối số đó mà không ảnh hưởng đến kết quả.
  • Bạn có thể chỉ định một đối số mặc định để có thể bỏ qua đối số khi gọi hàm.