Lập trình Kotlin - Cách viết Hàm

 Sử dụng hàm trong Kotlin

Hàm (Function)

Hàm là 1 tập hợp các đoạn câu lệnh để thực hiện công việc nào đó. Hàm có thể có tên và cũng có thể không. Việc sử dụng hàm sẽ khiến cho code của ta ngắn gọn hơn và không bị lặp đi lặp lại các đoạn code.

Khai báo và gọi hàm

Trong Kotlin, ta định nghĩa hàm sử dụng từ khóa fun:

fun helloMessage(name: String): String {
    return "Hello, $name"
}

Và thử gọi hàm qua câu lệnh:

val helloMessage = helloMessage("Duong Vu")
println(helloMessage) // in ra: Hello, Duong Vu

Trong ví dụ trên, ta đã khai báo hàm helloMessage với 1 tham số đầu vào là name có kiểu String. Hàm này trả về kiểu dữ liệu String. Cách khai báo tham số khi định nghĩa hàm là:

tên_tham_số:kiểu_dữ_liệu

Lưu ý: Hàm có thể có hoặc không có tham số.

Ta xem thử 1 ví dụ khác:

fun sayHello(name: String):Unit{
    print("Hello, $name")
}

Và gọi hàm sayHello:

sayHello("Duong Vu") // in ra: Hello, Dương Vu

Ta thấy là hàm sayHello ở trên cũng tương tự với hàm helloMessage ta viết lúc đầu. Chỉ khác 1 điều là hàm sayHello trả về kiểu dữ liệu Unit (thay vì String) và tự nó print ra câu chào. Bạn có thể thấy hàm sayHello không có câu lệnh return ở cuối hàm. Bởi đơn giản, kiểu dữ liệu Unit là kiểu dữ liệu đặc biệt, khi hàm trả về kiểu dữ liệu này, ta không bắt buộc phải có câu lệnh return để trả về kết quả cho hàm. Unit trong Kotlin giống như Void trong C và Java vậy. Và trong Kotlin, khi bạn định nghĩa 1 hàm mà không khai báo kiểu dữ liệu trả về, thì tức là hàm của bạn đã trả về kiểu Unit:

//Kết quả vẫn giống hệt như khi có khai báo trả về kiểu Unit
fun sayHello(name: String) {
    print("Hello, $name")
}

Định nghĩa giá trị mặc định của tham số:

Ta viết lại hàm sayHello như thế này:

fun sayHello(name: String = "Dương vũ") {
   print("Hello, $name!!!")
}

Như ta thấy, so với hàm đã viết ở phần 1, tham số đầu vào name có thêm định nghĩa = “Dương vũ” điều này tức là “Dương Vũ” là giá trị mặc định của tham số name. Nếu ta gọi hàm sayHello mà không truyền tham số name, Kotlin sẽ sử dụng giá trị mặc định của tham số name đó. Thử gọi hàm:

sayHello() // Kết quả: Hello, Dương Vũ

Còn tất nhiên khi ta truyền giá trị cho tham số name thì giá trị mặc định sẽ không được sử dụng nữa:

sayHello("Mr.Rain") // Kết quả: Hello, Mr.Rain

Việc Kotlin cho phép định nghĩa giá trị mặc định của tham số đã giúp cho ta giảm thiểu số lượng hàm phải viết. Nếu như trong Java, không có tính năng này, ta đã phải viết 2 hàm sayHello(name) và sayHello() để xử lý các trường hợp có và không có tham số đầu vào rồi. Rất tiện đúng không?

Sử dụng tên cho tham số

Sử dụng tên cho tham số là gì và vì sao trong một số trường hợp bắt buộc phải cần đến nó. Hãy cùng mình xem 1 ví dụ. Ta sẽ định nghĩa 1 hàm như sau:

fun getFullName(firstName: String, middleName: String = "Van", lastName: String) {
    print("Full name: $firstName $middleName $lastName");
}

Ở trên ta khai báo 1 hàm với 3 tham số đầu vào: firstNamemiddleNamelastName. Trong đó middleName có giá trị mặc định là Van. Như mình đã nói ở phần trên, nếu không truyền tham số middleName thì Kotlin sẽ sử dụng giá trị mặc định của nó (là Van) đúng không? Giờ ta thử gọi hàm mà không truyền middleName vào xem sao:

getFullName("Tran", "Nam") //lỗi No value passed for parameter lastName

Lúc này, ta sẽ nhận được thông báo lỗi: No value passed for parameter lastName (chưa truyền tham số lastName). Vì sao? Vì khi này, Kotlin hiểu rằng tham số thứ 2 (có giá trị “Nam“) là middleName, chứ không phải là lastName như ta mong muốn. Khi này ta cần phải làm như sau:

getFullName("Tran", lastName = "Nam") // In ra Full name: Tran Van Nam

Lúc này ta đã chỉ rõ “Nam” là giá trị của tham số lastName chứ không phải của middleName, và middleName do không truyền vào nên sử dụng giá trị mặc định.

Lưu ý: Việc truyền tên tham số trong lúc gọi hàm đôi khi không bắt buộc, nhưng nó làm cho code của ta dễ đọc hơn, vì vậy mình khuyến khích khi sử dụng hàm, hãy cố gắng truyền thêm tên tham số vào, để sau này đọc lại code không bị vất vả.

Hàm với 1 dòng lệnh duy nhất – Single line function

Khi giá trị trả về của 1 hàm có thể viết được bằng 1 dòng lệnh duy nhất, ta có thể bỏ cặp {} và từ khóa return để đơn giản hóa. Ví dụ:

fun getSum(numberOne: Int, numberTow: Int) = numberOne + numberTow

Hàm trên tính tổng của 2 số, và ta chỉ cần duy nhất 1 dòng để định nghĩa hàm, ko cần đến {} và từ khóa return, rất ngắn gọn.

Thêm 1 ví dụ nữa:

fun doubleNumber(number:Int) = 2*number

Hàm không giới hạn tham số

Đôi khi ta không biết trước số lượng tham số cần truyền vào cho hàm. Ta có thể cần truyền 1, 2 hay thậm chí 100 tham số. Lúc này ta cần định nghĩa 1 hàm không giới hạn tham số. Ví dụ như sau:

fun getSum(vararg numbers: Int): Int {
    var sum = 0
    for (n in numbers) {
        sum = sum + n
    }
    return sum
}

Như các bạn thấy ta đã thêm từ khóa vararg vào trước tham số numbers (kiểu Int). Có nghĩa là ở đây ta có thể truyền bao nhiêu tham số (kiểu Int) tùy thích. Và hàm getSum này sẽ tính tổng của tất cả các tham số truyền vào. Thử gọi hàm:

print(getSum(1,2,3)) // In ra 6
print(getSum(1,2,3,4,5)) // In ra 15

Hàm cục bộ

Kotlin cho phép ta định nghĩa hàm trong thân 1 hàm khác.

fun printGreeting(firstName: String, lastName: String) {

   //Bắt đầu định nghĩa hàm cục bộ
   fun getFullName(firstName: String, lastName: String): String {
       return "$firstName $lastName"
   }
   //Sử dụng hàm cục bộ
   println("Hello ${getFullName(firstName, lastName)}")

}

Vẫn là hàm in ra 1 câu chào, đầu vào là firstName  lastNameNhưng lần này, trong thân hàm  printGreeting ta đã định nghĩa thêm 1 hàm khác, getFullName trả về tên đầy đủ dựa vào 2 tham số đầu vào firstName  lastName của hàm printGreeting Và hàm printGreeting sẽ sử dụng giá trị trả về từ hàm getFullName. Thử gọi hàm:

printGreeting("Duong", "Vu") // Kết quả: Hello Duong Vu

Lưu ý: Hàm cục bộ chỉ có thể sử dụng được trong thân hàm mà nó được định nghĩa.

Như vậy là trên đây mình đã giới thiệu cơ bản về khai báo hàm và cách sử dụng hàm trong nhiều trường hợp. Vẫn còn 1 số kiến thức khác nâng cao hơn như lambda function, extension function, … mình sẽ trình bày trong những bài sau, trong những tình huống phù hợp hơn. Còn bây giờ, ta chỉ cần biết được cơ bản về hàm trong Kotlin, vậy là đủ nhỉ ;).