Các hàm bậc cao rất hữu ích và chúng thực sự có thể cải thiện reusability
mã. Tuy nhiên, một trong những mối quan tâm lớn nhất về việc sử dụng chúng là hiệu quả. Các biểu thức Lambda được biên dịch thành các lớp (thường là các lớp ẩn danh) và việc tạo đối tượng trong Java là một thao tác nặng. Chúng ta vẫn có thể sử dụng các hàm bậc cao một cách hiệu quả, trong khi vẫn giữ được tất cả các lợi ích, bằng cách làm cho các hàm nội dòng.
ở đây có chức năng nội tuyến thành hình ảnh
Khi một hàm được đánh dấu là inline
, trong quá trình biên dịch mã, trình biên dịch sẽ thay thế tất cả các lệnh gọi hàm bằng phần thân thực của hàm. Ngoài ra, các biểu thức lambda được cung cấp dưới dạng đối số được thay thế bằng phần thân thực của chúng. Chúng sẽ không được coi là hàm mà là mã thực tế.
Tóm lại: - Inline -> thay vì được gọi, chúng được thay thế bằng mã nội dung của hàm tại thời điểm biên dịch ...
Trong Kotlin, việc sử dụng một hàm làm tham số của một hàm khác (được gọi là các hàm bậc cao) cảm thấy tự nhiên hơn trong Java.
Tuy nhiên, sử dụng lambdas có một số nhược điểm. Vì chúng là các lớp ẩn danh (và do đó, các đối tượng), chúng cần bộ nhớ (và thậm chí có thể thêm vào tổng số phương thức của ứng dụng của bạn). Để tránh điều này, chúng ta có thể nội dòng các phương pháp của mình.
fun notInlined(getString: () -> String?) = println(getString())
inline fun inlined(getString: () -> String?) = println(getString())
Từ ví dụ trên : - Hai hàm này làm hoàn toàn giống nhau - in ra kết quả của hàm getString. Một là nội tuyến và một không.
Nếu bạn kiểm tra mã java đã dịch ngược, bạn sẽ thấy rằng các phương pháp hoàn toàn giống nhau. Đó là bởi vì từ khóa nội tuyến là một chỉ dẫn cho trình biên dịch để sao chép mã vào trang web gọi.
Tuy nhiên, nếu chúng ta đang chuyển bất kỳ loại hàm nào sang một hàm khác như bên dưới:
//Compile time error… Illegal usage of inline function type ftOne...
inline fun Int.doSomething(y: Int, ftOne: Int.(Int) -> Int, ftTwo: (Int) -> Int) {
//passing a function type to another function
val funOne = someFunction(ftOne)
/*...*/
}
Để giải quyết vấn đề đó, chúng ta có thể viết lại hàm của mình như sau:
inline fun Int.doSomething(y: Int, noinline ftOne: Int.(Int) -> Int, ftTwo: (Int) -> Int) {
//passing a function type to another function
val funOne = someFunction(ftOne)
/*...*/}
Giả sử chúng ta có một hàm bậc cao hơn như sau:
inline fun Int.doSomething(y: Int, noinline ftOne: Int.(Int) -> Int) {
//passing a function type to another function
val funOne = someFunction(ftOne)
/*...*/}
Ở đây, trình biên dịch sẽ yêu cầu chúng ta không sử dụng từ khóa nội tuyến khi chỉ có một tham số lambda và chúng ta đang chuyển nó cho một hàm khác. Vì vậy, chúng ta có thể viết lại hàm trên như sau:
fun Int.doSomething(y: Int, ftOne: Int.(Int) -> Int) {
//passing a function type to another function
val funOne = someFunction(ftOne)
/*...*/
}
Lưu ý : -chúng tôi cũng phải xóa từ khóa noinline vì nó chỉ có thể được sử dụng cho các hàm nội tuyến!
Giả sử chúng ta có chức năng như thế này ->
fun intercept() {
// ...
val start = SystemClock.elapsedRealtime()
val result = doSomethingWeWantToMeasure()
val duration = SystemClock.elapsedRealtime() - start
log(duration)
// ...}
Điều này hoạt động tốt nhưng phần logic của hàm bị ô nhiễm với mã đo lường khiến đồng nghiệp của bạn khó làm việc hơn với những gì đang xảy ra. :)
Đây là cách một hàm nội tuyến có thể giúp mã này:
fun intercept() {
// ...
val result = measure { doSomethingWeWantToMeasure() }
// ...
}
inline fun <T> measure(action: () -> T) {
val start = SystemClock.elapsedRealtime()
val result = action()
val duration = SystemClock.elapsedRealtime() - start
log(duration)
return result
}
Bây giờ tôi có thể tập trung vào việc đọc mục đích chính của hàm intercept () là gì mà không bỏ qua các dòng mã đo lường. Chúng tôi cũng được hưởng lợi từ tùy chọn sử dụng lại mã đó ở những nơi khác mà chúng tôi muốn
inline cho phép bạn gọi một hàm có đối số lambda trong một bao đóng ({...}) thay vì truyền tham số lambda like (myLamda)