Kotlin - Need to know - part 2
Bài đăng này đã không được cập nhật trong 4 năm
Part 1: https://viblo.asia/p/kotlin-need-to-know-part-1-gDVK2pbnlLj
Kotlin được thiết kế với khả năng tương thích với Java. Code Java hiện tại có thể được gọi từ Kotlin theo cách tự nhiên và ngược lại, phía code Kotlin, bạn cũng hoàn toàn có thể gọi code từ Java. Tuy nhiên, có một số khác biệt nhất định giữa Java và Kotlin nên cần lưu ý chúng khi tích hợp giữa Kotlin và Java.
Bài viết này focus việc gọi Java code từ Kotlin class.
Hầu hết code Java có thể được sử dụng bởi Kotlin mà không gặp phải quá nhiều issues
Getters & Setters
Lưu ý 1:
Method phải theo Java convention cho getter
và setter
:
Get
: không chứaargument
và tên method phải bắt đầu bằngget
hoặcis
nếu làBoolean
Set
: chứa 1argument
, và tên bắt đầu bằngset
Phía Kotlin, getter
và setter
của Java sẽ được biểu diễn như là một property
. Ví dụ:
import java.util.Calendar
fun calendarDemo() {
val calendar = Calendar.getInstance()
if (calendar.firstDayOfWeek == Calendar.SUNDAY) { // call getFirstDayOfWeek()
calendar.firstDayOfWeek = Calendar.MONDAY // call setFirstDayOfWeek()
}
if (!calendar.isLenient) { // call isLenient()
calendar.isLenient = true // call setLenient()
}
}
Lưu ý 2:
Nếu trong code Java chỉ có setter
, nó sẽ không là property
bên Kotlin, vì Kotlin hiện chưa support set-only properties
.
Keyword in, object, is
as Java method name
Vì các từ trên đều hợp lệ để đặt tên trong Java nhưng lại là keyword bên phía Kotlin, nên cần phải thêm dấu backtick
(`) trong các keyword này, Ví dụ:
foo.`is`(bar) // call method is() in Java from Kotlin.
Annotating type parameters
Phía Java có thể chú thích (annotate
) kiểu các function, argument hay generic type để cung cấp thông tin nullability
, Ví dụ:
@NotNull
Set<@NotNull String> toSet(@NotNull Collection<@NotNull String> elements) { ... }
và từ phía Kotlin
sẽ nhìn với thông tin:
fun toSet(elements: (Mutable)Collection<String>) : (Mutable)Set<String> { ... }
Lưu ý, việc vô tình hoặc cố tinh thiếu các nullability annotation
từ phía Java, phía Kotlin sẽ nhận được các kiểu Platform Type (hiểu đơn giản là kiểu có thể null hoặc không null trong Kotlin).
// without @NotNull from Java code
fun toSet(elements: (Mutable)Collection<String!>) : (Mutable)Set<String!> { ... }
Java Arrays
Không giống như Java, array trong Kotlin là invariant
, nghĩa là Kotlin không cho phép bạn gán một Array<String>
cho một Array<Any>
, để tránh các lỗi lúc runtime. Và Kotlin cũng cấm luôn việc gán 1 array của một subclass
cho array của superclass
(Java cho phép).
Các array kiểu nguyên thủy ở Java có performance tốt do hạn chế thao tác boxing/unboxing
kiểu. Phía Kotlin có hỗ trợ array tất cả các kiểu nguyên thủy (IntArray
, DoubleArray
...) để handle trường hợp này. Nó không liên quan gì đến class Array
và được compile thành array kiểu nguyên thủy như Java.
public class JavaArrayExample {
public void removeIndices(int[] indices) {
// code here...
}
}
Gọi từ Kotlin để có performance tốt nhất:
val javaObj = JavaArrayExample()
val array = intArrayOf(0, 1, 2, 3) // create an IntArray object
javaObj.removeIndices(array) // passes int[] to method
Java Varargs
public class JavaArrayExample {
public void removeIndicesVarArg(int... indices) { // varargs parameter
// code here...
}
}
Phía Kotlin có thể dùng spread operator
(* ) để truyền một IntArray
val javaObj = JavaArrayExample()
val array = intArrayOf(0, 1, 2, 3)
javaObj.removeIndicesVarArg(*array)
Lưu ý: Hiện tại
null
không thể truyền vào method khai báovarargs
Checked Exceptions
Trong Kotlin, tất cả exception đều là kiểu unchecked
, nghĩa là compiler sẽ không bắt buộc bạn phải catch chúng. Vậy nên, khi gọi một Java method có khai báo một checked exception
, Kotlin không yêu cầu bạn phải catch hay gì cả.
fun render(list: List<*>, to: Appendable) {
for (item in list) {
to.append(item.toString()) // Java would require us to catch IOException here
}
}
Object Methods
Khi import các kiểu của Java sang Kotlin, tất cả các tham chiếu của kiểu java.lang.Object
sẽ thành Any
. Vì Any
không phải là một platform-specific
, nó chỉ định nghĩa 3 method toString(), hashCode(), equals()
, nên để khai báo các đặc tính khác của java.lang.Object
, Kotlin dùng các extensions function
.
wait()/notify()
Chúng không tồn tại trong Any
, và cũng không được khuyến khích sử dụng tron java.util.concurrent
, tuy nhiên nếu cần thì vẫn có thể gọi trong Kotlin bằng cách cast về java.lang.Object
(foo as java.lang.Object).wait()
getClass()
Để get class của một Object, ta có thể dùng extensions property java
trong KClass
hoặc javaClass
val fooClass = foo::class.java
val fooClass2 = foo.javaClass
clone()
Để override clone
, class ở Kotlin cần phải extends từ kotlin.Cloneable
class Example : Cloneable {
override fun clone(): Any { ... }
}
finalize()
Để override finalize
, bạn chỉ cần viết method finalize
mà không kèm từ khóa override
và không được là private
class MyClass {
protected fun finalize() {
// finalization logic
}
}
Accessing static members
Các thành phần static của* Java class* tạo thành companion object
cho các class này trong Kotlin. Chúng ta không thể truyền một companion object
như là giá trị, nhưng có thể truy cập thành phần static cụ thể, ví dụ:
if (Character.isLetter(a)) { ... }
Để truy cập thành phần static của một kiểu Java được map sang một kiểu Kotlin, sử dụng full qulified name
của nó:
java.lang.Boolean.getBoolean("false"); // call static method getBoolean() from Java Boolean
Java Reflection
Java Reflection sử dụng được trong Kotlin class và ngược lại. Để truy cập Java Reflection thông qua java.lang.Class
, có thể sử dụng 1 trong các cách sau:
foo::class.java // instance::class.java
foo.javaClass // instance.javaClass
Foo::class.java // ClassName::class.java
Using JNI with Kotlin
Để khai báo một function được implement trong mã native (C or C++)
, cần đánh dấu nó với từ khóa external
, còn lại các thủ tục khác hoàn toàn giống java khi sử dụng với JNI
, ví dụ:
external fun foo(x: Int): Double
SAM conversions
Giống như Java 8, Kotlin support SAM - Single Abstract Method - conversions
, nghĩa là Kotlin function literals
có thể tự động convert thành triển khai (implementation
) của Java interface
với một non-default
method, miễn sao các kiểu parameter của Java interface method trùng khớp với kiểu parameter trong Kotlin function. (Đọc thêm SAM tại đây)
Bạn có thể sử dụng để tạo instance của các SAM interface:
val runnable = Runnable { println("This runs in a runnable") }
và trong method gọi:
val executor = ThreadPoolExecutor()
// Java signature: void execute(Runnable command) { ... }
executor.execute { println("This runs in a thread pool") }
nếu trong Java class có nhiều method sử dụng nhiều functional interface, bạn có thể chọn một method cần gọi bằng cách sử dụng một adapter function
chuyển đổi một lambda
sang một kiểu SAM cụ thể. Những adapter function
này cũng được compiler tạo ra khi cần:
executor.execute(Runnable { println("This runs in a thread pool") })
Chú ý:
- SAM conversion chỉ hoạt động cho interface chứ không cho abstract class, ngay cả khi các abstract class chỉ có một Single Abstract Method.
- SAM cũng chỉ hoạt động với Java interop (gọi từ Kotlin), Vì Kotlin có các kiểu function phù hợp, việc tự động chuyển các function thành các triển khai của Kotlin interface là không cần thiết và do đó không được hỗ trợ.
TO BE CONTINUED
References:
All rights reserved