I, High-order function
- Các kotlin function đều là
first class
: nó có thể trở thànhkiểu dữ liệu
cho 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 function
là 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) -> R
là 1 function type. Do đó nó cũng có thể là kiểu dữ liệu của tham sốoperation
trong 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à
R
và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) -> R
là 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) -> C
có 2 tham số với kiểu dữ liệu lần lượt làA
,B
và 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) -> C
có 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 function
là 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êmsuspend
keyword: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
operation
trong 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
receiver
thì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 literal
hoặ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 literal
bao 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 reference
bao 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
Foo
class 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
f
có 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) -> C
có 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
this
keyword đạ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 |