Mở rộng quá dễ dàng!

Chúng ta đều biết rằng một lớp được khai báo là final thì không thể nào extends được đúng không? Ví dụ chúng ta muốn thừa kế lớp Integer để có thêm hàm toBigInteger() chúng ta sẽ làm thế này:

class IntegerEx extends Integer {
    public BigInteger toBigInteger() {
        return BigInteger.valueOf(this);
    }
}

Nhưng rất tiếc chúng ta không thể, vì lớp Integer là một lớp final, với Java thì hiện tại là bó tay, nhưng với Kotlin, mọi việc lại siêu dễ dàng.

Cú pháp

Để tạo được các hàm mở rộng chúng ta cần tạo ra một file .kt và viết hàm mở rộng vào file đó với cú pháp như sau:

fun <tên class cần mở rộng>.<hàm cần mở rộng>(
    [các tham số]
): [kiểu dữ liệu trả về] =
    <Nội dung hàm>

hoặc với cú pháp dành cho hàm phức tạp chúng ta có thể dùng cú pháp này:

fun <tên class cần mở rộng>.<hàm cần mở rộng>(
    [các tham số]
): [kiểu dữ liệu trả về]  {
    <Nội dung hàm>
}

Và khi sử dụng chúng ta sẽ gọi như bình thường:

<đối tượng>.<tên hàm>([các tham số])

Hãy lưu ý các mục trong ngoặc vuông là các mục không bắt buộc phải có, chúng ta có thể bỏ qua khi viết code.

Áp dụng

Nào bây giờ chúng ta hãy quay trở lại với ví dụ thêm hàm toBigInteger trên nhé, chúng ta có thể viết kiểu ngắn:

fun Integer.toBigInteger(): BigInteger =
    BigInteger.valueOf(this.toLong())

hoặc kiểu dài:

fun Integer.toBigInteger(): BigInteger {
    return BigInteger.valueOf(this.toLong())
}

Và sử dụng không khác gì với việc gọi hàm thông thường:

val intValue = Integer.valueOf(10)
val bigIntegerValue = value.toBigInteger()

Giả mã bí ẩn

Những người lần đều tiếp xúc với kiểu extension này đều thấy nó thực sự bí ẩn, trong đó có cả mình, :D, đó là khi mình tiếp xúc với CSharp, lúc đó mình cần viết hàm để format Date sang String và lúc đó quả thực mình đã say nắng CSharp luôn. Sau một hồi sử dụng thì mình bắt đầu cảm thấy tò mò và thử đào sâu xem có đúng với suy nghĩ của mình không, và quả thật nó không khác. Về bản chất thì kotlin hay CSharp khi biên dịch đều có một bước chuyển đổi, và các hàm extension sẽ được chuyển đổi thành các hàm static.

Quay trở lại ví dụ hàm toBigInteger, khi bạn đặt hàm này trong file IntegerExtension.kt thì khi biên dịch, kotlin sẽ tạo ra lớp IntegerExtensionKt thế này:

public class IntegerExtensionKt {
    private IntegerExtensionKt() {}

    public static BigInteger toBigInteger(Integer original) {
        return BigInteger.valueOf(original);
    }
}

Và khi sử dụng nó sẽ gọi hàm thế này:

Integer intValue = Integer.valueOf(10);
BigInteger bigIntegerValue =. IntegerExtensionKt.toBigInteger(value);

Thật đơn giản đúng không mọi người? Hoá ra đây cũng chỉ là một cách viết khác mà thôi, không có gì là magic cả.

Kết luận

Bạn còn nhớ nguyên tắc Open/Close (đóng với việc thay đổi, mở với việc mở rộng) chứ? Với sự xuất hiện của Kotlin đã giúp chúng ta giải được một bài toán vô cùng nan giải, đây là một điểm rất mạnh của Kotlin với các hàm extenstion. Nếu bạn đang dùng kotlin hãy tận dụng triệt để sức mạnh này nhé.