Kotlin: Tìm hiểu về function type, function literal
Bài đăng này đã không được cập nhật trong 6 năm
Chào mọi người, trong Kotlin có một cải tiến so với Java là các function trong Kotlin có thể được coi như các biến, điều đó có nghĩa là một function đều có thể được định danh, được gán giá trị, được truyền vào hàm như một argument hoặc là giá trị được trả về từ một hàm khác.
Function type
Và để làm được điều đó, các function đó phải có được định danh một kiểu (type) được gọi là function type
. Dưới đây là một số ví dụ về function type
:
()->Unit
: Định danh một function không truyền vào tham số nào và không trả về gì - ví dụ nhưvoid funcA() { ... }
trong java(Int)->Int
: Định danh một function mà có 1 tham số thuộc kiểuinteger
, và trả về kết quả là mộtinteger
, ví dụ trong Java ta cóint funA(int arg) { .... }
()->()->Unit
: Định danh một function không truyền tham số nào và trả về một function khác với kiểu là()->Unit
.
Ta có thể sử dụng function type
giống như một interface
và implement funtion type đó như dưới đây:
class MyFunction: ()->Unit {
override fun invoke() {
println("I am called")
}
}
fun main(args: Array<String>) {
val function = MyFunction()
function() // Prints: I am called
}
Ta cũng có thể sử dụng chúng như một biến local, properties hay là một arguments:
val greet: ()->Unit
val square: (Int)->Int
val producePrinter: ()->()->Unit
Các biến ở trên hiện tại đang chưa có giá trị nào. Để gán giá trị vào các biến đó, một cách đơn giản nhất là sử dụng Function reference ::
, nếu bạn đã dùng đến lambda expression thì chắc cũng có một chút liên hệ với phần này, để gán ta sẽ viết như sau:
fun greetFunction() {
println("Hello")
}
val greet = ::greetFunction
Function literal
Một cách khác để gán giá trị cho các biến trên là sử dụng Lambda expression
hoặc Anonymous function
, hai phương pháp này được gọi chung với tên là Function literal.
Lambda expression
Lambda expression là một cách định nghĩa một hàm rất ngắn gọn. Ta sẽ thử gán các giá trị ở phía trên bằng lambda nhé:
val greet: ()->Unit = { println("Hello") }
val square: (Int)->Int = { x -> x * x }
val producePrinter: ()->()->Unit = { { println("I am printing") } }
// Su dung
greet() // Prints: Hello
println(square(2)) // Prints: 4
producePrinter()() // Prints: I am printing
Ở trong phần tham số của square
ta có thể rút gọn thay vì truyền trực tiếp (Int)->Int
ta có thể truyền kiểu của biến ngay vào bên trong câu lệnh lambda.
Ngoài ra phần tham số của greet
và producePrinter
có thể bỏ bớt mà vẫn đủ để hiểu kiểu của tham số truyền.
Ta có thể rút gọn hàm trên thành như sau:
val greet = { println("Hello") }
val square = { x: Int -> x * x }
val producePrinter = { { println("I am printing") } }
Anonymous function
Anonymous function Cũng là một cách để định nghĩa một function, hãy thử thay ví dụ trên bằng cách sử dụng Anonymous function nhé:
val greet: ()->Unit = fun() { println("Hello") }
val square: (Int)->Int = fun(x) = x * x
val producePrinter: ()->()->Unit = fun() = fun() { println("I am printing") }
Và có thể viết gọn hơn:
val greet = fun() { println("Hello") }
val square = fun(x: Int) = x * x
val producePrinter = fun() = fun() { println("I am printing") }
Sự khác biệt
Nhìn vào 2 ví dụ trên ta có thể thấy lambda expresion và anonymous functions khá là giống nhau, vậy vì sao nó lại tách biệt ra làm 2 thành phần?
Điểm khác biệt cơ bản đó là Anonymous function thì rõ ràng hơn so với lambda expression vì giá trị trả về sẽ được quy định rõ ràng còn ở lambda expression thì giá trị trả về sẽ là giá trị cuối cùng trong câu lệnh ở trong hàm hoặc là Unit
.
Một câu lệnh mà chưa được gắn nhãn nếu sử dụng lambda sẽ không chạy được như ví dụ sau:
val getMessage = { response: Response ->
if(response.code !in 200..299) {
return "Error" // Error! Not allowed
}
response.message
}
Để không bị lỗi ta bắt buộc phải sử dụng một nhãn lambda@ trong câu lệnh cuối cùng
val getMessage = lambda@ { response: Response ->
if(response.code !in 200..299) {
return@lambda "Error"
}
response.message
}
Anonymous function thì giống với các hàm bình thường và cả kiểu trả về và câu lệnh trả về đều cần xác định rõ ràng.
val getMessage = fun(response: Response): String {
if(response.code !in 200..299) {
return "Error" // Error! Not allowed
}
return response.message
}
Thực tế rằng 2 cách trên đều có thể sử dụng được, nhưng bạn nên sử dụng Anonymous function trong trường hợp return nhiều hơn một, còn lambda expression trong trường hợp là một hàm nhỏ và chỉ return ở một chỗ. Ngoài ra còn một số trường hợp nên dùng Anonymous function như trường hợp sau:
fun greet() = { println("Hello") }
greet() // se in ra cai gi?
Câu trả lời là "Không in ra gì cả, bởi vì greet
trả về một hàm thay vì in ra chữ Hello". Vì thế bạn hãy nhớ trường hợp này nên dùng Anonymous function thay vì lambda expression:
fun greet() = fun() { println("Hello") }
Cảm ơn các bạn đã theo dõi bài viết, mong là mọi người có thể hiểu rõ hơn về các cách định nghĩa các hàm trong kotlin và sử dụng nó một cách hợp lý. Bài viết được dịch từ: https://medium.com/@belokon.roman/i-think-it-should-be-mentioned-one-more-case-to-use-lambda-instead-of-anonymous-function-when-we-1f6026878458
All rights reserved