1. Higher-order function là gì ?
- Thuật ngữ "Functional programming" là một dạng lập trình mà ta có thể truyền các hàm như 1 tham số hay return 1 hàm , Higher-order function là một dạng như vậy
Dưới đây là hai kiểu thể hiện cơ bản của Higher-order function :
1. Higher-order function được truyền như tham số
Ta có ví dụ một Higher-order function như sau :
fun printPerson(name: String , printName: (Int?) -> String) { printName(name) }
- Function reference :
fun printName(name: String) { println(name)
}
fun printPerson("VietKFC" , ::printName)
Ta thấy function printName được gọi tham chiếu trong function printPerson()
- Anonymous function :
Ta sẽ truyền như những function bình thường và định nghĩa luôn ở đây , khác chỗ đây sẽ là 1 function không có tên :
fun printPerson("Viet" , fun(name: String?) : Unit { // Do something with name...
})
2. Trả về một function
fun printPerson() : ((Int) -> Unit) { return ::getAge
}
fun getAge(age: Int) { println(age)
}
printPerson()(22)
- Hạn chế khi sử dụng higher-order function :
Các param có dạng function type được truyền vào sẽ tự động tạo ra một Function Object trong memory để lưu các function này , làm tăng số lượng function trong app ở run-time.
private fun makeHighOrder(action: () -> Int) { action() }
private final void makeHighOrder(Function0 action) { action.invoke(); }
Ta thấy sau khi decomplie sang java code thì Object Function0 được sinh ra trong memory.
2. Lambdas
Lambda expression là một hàm ẩn danh được định nghĩa trong {} và có thể coi như một giá trị . Chúng có thể được truyền như một tham số hoặc làm bất cứ điều gì như đối với 1 Object.
val lambda : (Int , Int) -> Int = {a,b -> a+b}
println(lambda(2,3))
Ta cũng có thể truyền Lambda vào Higher-order function :
private fun sumNumber(a: Int , b: Int , plus: (Int,Int) -> Int) { println(plus(a,b))
}
val plus : (Int,Int) -> Int = {a,b -> a+b}
sumNumber(2,3,plus)
- Function literal , cũng là một cách gọi khác của Lambda Function có 1 tham số
val nameList = arrayOf("Viet" , "Hong" , "Huyen" , "Long" , "Quang")
val newName = nameList.filter{ name -> name.contains("n" , ignoreCase = true) } .sortBy{name -> name.length}
- Sự khác nhau giữa Lambda và Anonymous Function :
Anonymous function có thể xử lý và trả về như 1 hàm bình thường , còn Lambda chỉ trả về giá trị cuối cùng , nếu muốn xử lý rẽ nhánh ta cần thêm label để định nghĩa, ví dụ :
val anonymousFunc = fun(s: String) : Boolean {
if(s.contains("v")) return true
return false
}
val lambdaFunction: (String) -> Boolean = _@.com{s ->
if(s.contains("v")) return@lambda true
// nếu ko có label @lambda , câu lệnh if sẽ bị bỏ qua
false
}
3. Closure
-
Closure là những hàm có thể truy cập và sửa đổi những biến được đăng kí bên ngoài phạm vi
-
Lambda expression và Anonymous function cho phép truy cập vào closure và sửa đổi những biến này
Từ Java 8 hoặc Kotlin có sử dụng lambda , ta có ví dụ so sánh như sau :
int sum = 0;
List<Integer> list = Arrays.asList(1,2,3,4,5);
Thread thread = new Thread(() -> { for(int i = 0 ; i< list.size() ; i++) { sum += i; // Báo lỗi khi modify biến sum... }
});
thread.start();
Với Lambda expression :
var sum = 0
val list = listOf(1,2,3,4,5)
val thread = Thread{ list.forEach {sum += i} }
thread.start()
4. Inline function
Quay trở lại vấn đề về việc 1 Function Object sẽ được sinh ra khi sử dụng function như một tham số , Inline Function sinh ra để khắc phục điều này.
Ta thêm "inline" vào trước mỗi Higher-order function
fun showPerson(name: String , getAge: (Int?) -> Int { println(name) println(getAge(22))
}
showPerson("viet" , fun(age: Int?): Int { return 22
})
Thông thường sau khi decomplie , ta có 1 Object tên là Function1 được sinh ra
public final showPerson(Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
INVOKEVIRTUAL com/example/kotlindemo/MainActivity.showPerson(Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
Sau khi sử dụng inline function và decomplie lại ta thấy Object Function1 đã biến mất
LOCALVARIABLE this_$iv Lcom/example/kotlindemo/MainActivity; L1 L15 2
LOCALVARIABLE name$iv Ljava/lang/String; L1 L15 3
LOCALVARIABLE $i$f$showPerson I L2 L15 4
- Tuy nhiên , Inline Function cũng đi kèm một số nhược điểm sau :
- Inline function không thể gọi gián tiếp từ inline function khác hay gọi chính nó
- 1 public inline function chỉ có thể truy cập vào public class và public field của class đó
- Nó có thể sinh ra thêm nhiều code vì implement của nó được gọi ở nhiều nơi
- Nếu dùng nhiều sẽ làm giảm tốc độ truy cập của bộ nhớ đệm
---> Ta nên sử dụng Inline Function cho những khối code ngắn gọn và hạn chế sử dụng chúng bừa bãi