I, High-order function
- Các kotlin function đều là
first class: nó có thể trở thànhkiểu dữ liệucho variable, tham số hơặc kết quả return của high-order function. - Ta gọi đó là
function type(kiểu dữ liệu function). High-order functionlà function lấy function khác làm tham số hoặc trả về 1 function.- Chú ý: high-order function sẽ khiến tạo thêm các function type object trong memory. Do đó để tối ưu bộ nhớ, chúng ta có thể sử dụng
inline function. - Để hiểu rõ hơn, chúng ta sẽ đi phân tích high-order function fold():
- Ví dụ 1:
1 | inline fun <T, R> Iterable<T>.fold(initial: R, operation: (R, T) -> R): R { |
- Hàm
fold()nhận 2 tham số:- 1,
initial: có kiểu làR - 2,
operation: có kiểu là(R, T) -> R?(R, T) -> Rlà 1 function type. Do đó nó cũng có thể là kiểu dữ liệu của tham sốoperationtrong high-order functionfold().
- 1,
- Ta có
(R, T) -> Rđịnh nghĩa cho 1 function type có:- 1, Hai tham số là với kiểu dữ liệu là
RvàT. - 2, Giá trị trả về có kiểu dữ liệu là
R.
- 1, Hai tham số là với kiểu dữ liệu là
II, Function types
- Ở ví dụ 1,
(R, T) -> Rlà function type nên nó cũng như các kiểu dữ liệu thông thường khác:val operation: (R, T) -> R = ... - Khi sử dụng function type, bạn cần chú ý:
- 1, Function type đều có:
danh sách kiểu dữ liệu các tham sốvàkiểu dữ liệu của giá trị trả về. Ví dụ(A, B) -> Ccó 2 tham số với kiểu dữ liệu lần lượt làA,Bvà kiểu dữ liệu của giá trị trả về làC. - 2, Danh sách kiểu dữ liệu của tham số có thể rỗng như
() -> A. Kiểu của dữ liệu trả về phải được xác định dù đó có làUnit. - 3, Function type có thể có thêm kiểu dữ liệu của
receiver. Ví dụA.(B) -> Ccó kiểu dữ liệu của receiver là A, kiểu dữ liệu của tham số là B và kiểu dữ liệu của giá trị trả về là C. - 4, Function type có thể null, bạn chỉ cần thêm
()?vào nguyên mẫu. Ví dụ((A, B) -> C)?. - 5,
Suspending functionlà function có kiểu dữ liệu function đặc biệt (mình sẽ nói về công cụ tuyệt vời này sau), bạn phải thêmsuspendkeyword:suspend () -> A,suspend A.(B) -> C.
- 1, Function type đều có:
- Chú ý: chúng ta có thể thêm tên cho các tham số như
(x: Int, y: Int) -> Point. Nó giúp bạn cho bạn viết document thuận tiện hơn. - Ví dụ 2: chúng ta thêm tên cho các tham số cho kiểu dữ liệu của
operationtrong hàmfold()ở ví dụ 1
1 | // It's ok to name for `R` type with `acc`. |
- Ví dụ 3: kiểu dữ liệu function nhận thêm
receiverthìthisđại diện cho receiver.
1 | // Only one parameter in the function type so `it` is implicitly used. |
III, Khởi tạo kiểu function type
- Để tạo ra 1 instance của function type, chúng ta có thể sử dụng:
function literalhoặccallable reference. - Ngoài ra còn có cách khác là tạo custom class implement kiểu dữ liệu function như 1 interface. Bạn phải override
invoke().
1, Function literal
Function literalbao gồm có:- Lambada expression có dạng
{a, b -> a + b} - Anonymous function có dạng
fun(s: String): Int { return s.toIntOrNull() ?: 0 }.
- Lambada expression có dạng
- Ví dụ 4:
1 | val list = listOf(1, 2, 3, 4) |
2, Callable reference
Các
callable referenceđều thêm::vào trước định danh của chúng.Kiểu dữ liệu funtion có thể được khởi tạo với
Callable referencebao gồm:- function reference có dạng
::isOdd,String::toInt - property reference có dạng
::x,List<Int>::size - constructor reference có dạng
::Foo
- function reference có dạng
Ví dụ 5: sử dụng function reference khởi tạo function type in ra kết quả
[1, 3]
1 | fun isOdd(x: Int) = x % 2 != 0 |
- Ví dụ 6: sử dụng property reference khởi tạo function type in ra kết quả
[1, 2, 3]
1 | fun main() { |
- Ví dụ 7: sử dụng constructor reference khởi tạo function type. Nó gọi constructor của
Fooclass trong để khởi tạofactory
1 | class Foo |
III, Sử dụng variable của function type
Chúng ta có thể gọi variable
fcó kiểu dữ liệu function bằng:- Thông qua
invoke()làf.invoke(). - Gọi trực tiếp là
f()
- Thông qua
Nếu f có thể null thì ta chỉ dùng được
f?.invoke()Đối với function type có receiver, chúng ta xem ví dụ 8.
Ví dụ 8:
1 | val stringPlus: (String, String) -> String = String::plus |
IV, Function literal với receiver
- Như trên mình có nói rõ, function type có thể được khởi tạo bởi function literal (labamda expression hoặc anonymous function).
- Do đó, function type có receiver
A.(B) -> Ccó thể được khởi tạo bằngfunction literal với recevier. - Nói cách khác, lambada expression hay anonymous function sẽ nhận
thiskeyword đại diện cho receiver object. - Ví dụ 9:
thisđại diện cho receiver object nênplus()là 1 function được gọi bởi receiver object.
1 | // Using lambada expression |