Hàm tham chiếu

Hàm tham chiếu là một trong những cải tiến tuyệt vời mà chúng ta có được với Kotlin.
Bạn đã biết rằng Kotlin hỗ trợ các chức năng như là một type, có nghĩa là bạn có thể lưu một hàm trong một biến, sử dụng nó như là một đối số chức năng, hoặc thậm chí làm cho một hàm trả về một hàm khác.
Bạn có thể khai báo một hàm cho một biến như sau:
val sum: (Int, Int) -> Int = { x, y -> x + y }
Đây là một hàm có tham số là 2 số nguyên và trả về một số nguyên.
Trong ví dụ này, lambda đang áp dụng một hoạt động bổ sung.
Sau đó bạn có thể có một chức năng chấp nhận một lambda của loại này như một đối số:
fun applyOp(x: Int, y: Int, op: (Int, Int) -> Int): Int = op(x, y)

Đây là lấy hai số nguyên và áp dụng chức năng cho cả hai, vì vậy bạn có thể sử dụng sự kết hợp của cả hai cách này:
applyOp(2, 3, sum)

Bất kỳ hàm nào cũng có thể là một lambda

Vì vậy, cùng với cách mà lambda có thể được truyền như là một đối số hoặc được lưu vào một biến,
chúng ta có thể làm tương tự với các chức năng thông thường.
Trước tiên tôi sẽ giải thích nó với ví dụ trên.
Thay vì một lambda, bạn có một chức năng đơn giản:
fun sum(x: Int, y: Int) = x + y
Hàm này so với hàm trên là như nhau, nhưng thay vì có một biến gán cho một hàm, chúng ta có một hàm.
Bây giờ, nếu bạn gọi hàm theo cách gọi ở trên, nó sẽ lỗi
applyOp(2, 3, sum)
sum ko được xem như 1 lambda, nó chỉ đc định nghĩa để gọi như 1 hàm thông thường
và bây giờ nếu ban muốn gọi với cách như trên chỉ việc thêm :: và trước tên hàm
applyOp (2, 3, :: sum)
bạn có thể gán sự tham chiếu này cho một biến có cùng cấu trúc:
val sumLambda: (Int, Int) -> Int = ::sum
cũng thú vị phải không 😘

Xem xét bytecode

Sử dụng thuộc tính tham chiếu

items
    .sortedBy(MediaItem::title)
    .map(MediaItem::url)
    .forEach(::println)

Sử dụng lambda

items
    .sortedBy(MediaItem::title)
    .map(MediaItem::url)
    .forEach(::println)

Ta sẽ sử dụng công cụ bytecode Kotlin cho 2 đoạn code trên và kiểm tra nó.
Đây là bytecode của code dùng tham chiếu

Iterable $receiver$iv = (Iterable)items;
$receiver$iv = (Iterable)CollectionsKt.sortedWith($receiver$iv, (Comparator)(new HomeContentFragmentKt$getSortedUrls$$inlined$sortedBy$1()));
Collection destination$iv$iv = (Collection)(new ArrayList(CollectionsKt.collectionSizeOrDefault($receiver$iv, 10)));
Iterator var4 = $receiver$iv.iterator();
 
while(var4.hasNext()) {
   Object item$iv$iv = var4.next();
   String var10 = ((MediaItem)item$iv$iv).getUrl();
   destination$iv$iv.add(var10);
}
 
$receiver$iv = (Iterable)((List)destination$iv$iv);
Iterator var2 = $receiver$iv.iterator();
 
while(var2.hasNext()) {
   Object element$iv = var2.next();
   System.out.println(element$iv);
}

và đây là bytecode của đoạn code với lambda

Iterable $receiver$iv = (Iterable)items;
$receiver$iv = (Iterable)CollectionsKt.sortedWith($receiver$iv, (Comparator)(new HomeContentFragmentKt$getSortedUrls$$inlined$sortedBy$1()));
Collection destination$iv$iv = (Collection)(new ArrayList(CollectionsKt.collectionSizeOrDefault($receiver$iv, 10)));
Iterator var4 = $receiver$iv.iterator();
 
while(var4.hasNext()) {
   Object item$iv$iv = var4.next();
   MediaItem it = (MediaItem)item$iv$iv;
   String var11 = it.getUrl();
   destination$iv$iv.add(var11);
}
 
$receiver$iv = (Iterable)((List)destination$iv$iv);
Iterator var2 = $receiver$iv.iterator();
 
while(var2.hasNext()) {
   Object element$iv = var2.next();
   String it = (String)element$iv;
   System.out.print(it);
}

Không có gì đáng ngạc nhiên, mã này khá giống nhau.
Với các tham chiếu hàm chúng ta tiết kiệm được một vài khai báo biến, tương ứng với các biến số bên trong lambdas.
Vì vậy, tôi có thể nói là nó một chút hiệu quả hơn, tuy nhiên cũng ko thực sự to lớn.

Kết luận

Hàm tham chiếu là một tính năng mới mẻ, có thể làm sạch code và làm nó nhiều ngữ nghĩa.
Nhưng điều này đi kèm với việc những người mới dùng Kotlin có thể thấy chúng hơi mơ hồ ngay từ đầu.