12/08/2018, 15:57

Writing Java-friendly Kotlin code (Phần 2)

Chức năng Rất phổ biến thư viện với một số lớp Utils class. Trong Kotlin hầu như luôn luôn coi đó là các chức năng mở rộng. Utils thường có những cặp phương thức này: fun List<Int>.printReversedSum() { println(this.foldRight(0) { it, acc -> it + acc }) } fun List<String>.print ...

Chức năng Rất phổ biến thư viện với một số lớp Utils class. Trong Kotlin hầu như luôn luôn coi đó là các chức năng mở rộng. Utils thường có những cặp phương thức này:

fun List<Int>.printReversedSum() {
  println(this.foldRight(0) { it, acc -> it + acc })
}

fun List<String>.printReversedSum() {
  println(this.foldRight(StringBuilder()) {
    it, acc -> acc.append(it)
  })
}

Trong bytecode trên, cả hai sẽ có cùng một chữ ký để xóa:

public static final void printReversedSum(@NotNull List $receiver)

Chúng ta có thể khắc phục điều đó bằng cách sử dụng chú thích giống nhau:

fun List<Int>.printReversedSum() {
  println(this.foldRight(0) { it, acc -> it + acc })
}
@JvmName("printReversedConcatenation")
fun List<String>.printReversedSum() {
  println(this.foldRight(StringBuilder()) {
    it, acc -> acc.append(it)
  })
}

Chú ý: Nó không chỉ là cần thiết đối với người Java-caller, nhưng nó sửa chữa một lỗi trình biên dịch. Từ đó tất cả mã Kotlin trở nên giống mã bytec JVM tương tự như chính mã Java, bạn sẽ nhận được collision ở cấp bytecode và mã này sẽ không biên dịch

Files Theo như chúng ta biết, tập tin các chức năng mở rộng sẽ biến thành một lớp với các method static trong bytecode. Vì vậy, chúng tôi sẽ không cảm thấy nhiều sự khác biệt trong cách sử dụng của họ từ Java. Ngoại trừ một - lớp này sẽ được đặt tên cho tệp đính kèm với hậu tố Kt. Ví dụ: tệp tin sau

//reverser.kt
package util
fun String.reverse() = StringBuilder(this).reverse().toString()

Sẽ biến thành lớp ReverserKt. Hãy đổi tên nó thành một cái gì đó có ý nghĩa hơn đối với việc sử dụng Java, như ReverserUtils. Dễ dàng:

//reverser.kt
@file:JvmName("ReverserUtils")
package util
fun String.reverse() = StringBuilder(this).reverse().toString()

Bằng cách chỉ định chú thích quen thuộc này trước khi khai báo gói và với mục tiêu chú thích thích hợp, chúng tôi sẽ tạo mã Java-caller của chúng tôi tốt hơn.

@JvmMultifileClass Trường hợp sử dụng @JvmName cuối thường đi cùng với @JvmMultifileClass. Nếu chúng ta sử dụng cùng một tệp @file:JvmName trong một số tệp của cùng một gói, cố gắng làm cho nội dung của chúng có thể truy cập được từ cùng một lớp JVM, chúng ta cần @file:JvmMultifileClass trong mỗi files đó. Nếu không, một số lớp JVM có cùng tên lớp đầy đủ sẽ được tạo ra, mà sẽ không biên dịch. Dưới đây là ví dụ từ Kotlin Stdlib:

//Collections.kt
@file:kotlin.jvm.JvmMultifileClass
@file:kotlin.jvm.JvmName("CollectionsKt")

package kotlin.collections
-----
//Iterables.kt
@file:kotlin.jvm.JvmMultifileClass
@file:kotlin.jvm.JvmName("CollectionsKt")
package kotlin.collections

Constants, @JvmField Nếu public API của thư viện của chúng tôi bao gồm bất kỳ hằng số nào, chúng tôi có thể đưa chúng tới Java bằng nhiều cách:

  • const -- về cơ bản tạo ra một hằng số, không có accessors. Chỉ hoạt động đối với các nguyên thủy cấp cao nhất hoặc cấp đối tượng và String s.
  • @JvmField -- tạo ra một backing field accessible trên chính nó, không có accessors. Làm việc với var s thì tốt. Đối với tất cả các thuộc tính của object có các trường sao lưu, các trường này được tạo ra dưới dạng static trên một enclosing class level, điều này làm cho chúng trở thành public static.

CHÚ THÍCH! Khai báo một thuộc tính đối tượng như lateinit cũng làm cho trường sao lưu của nó public (trong khi giữ tất cả các accessors), mà có thể không phải là những gì bạn muốn cho một public API, do đó, nên chú ý.

0