Tìm hiểu về @JvmStatic, @JvmOverloads và @JvmField trong kotlin

I, Lời mở đầu

  • Nếu bạn là 1 lập trình viên Android, bạn sẽ chọn ngôn ngữ nào Java hay Kotlin ? Đừng lo về vấn đề đó vì mỗi ngôn ngữ sẽ có những thế mạnh riêng.
  • Chúng ta đều biết rằng cả Java và Kotlin đều chạy trên JVM.
  • Một class Kotlin đều có thể compile ra một class Java thông qua Kotlin compiler. Nếu sử dụng Android Studio thì Kotlin compiler sẽ compile Test.kt class thành Test.java class.
  • Đôi khi chúng ta muốn sử dụng những feature của Java hoặc sử dụng Kotlin code ở trong Java file để tối ưu performance cho JVM.
  • Trong bài viết này, chúng ta sẽ đi tìm hiểu về vấn đề này thông qua 3 annotation: @JvmStatic, @JvmOverloads@JvmField.

II, @JvmStatic

  • @JvmStatic là 1 annotation chỉ dùng trong objectcompanion object class: trong java compiled file, Kotlin compiler sẽ compile các member:
    • Function: thành 1 static method trong Java class.
    • Property: thành 1 static variable và getter/setter method cho static variable đó trong Java class.
  • Ví dụ 1:
1
2
3
4
5
6
7
8
9
object JvmStaticTest {
@JvmStatic
var view: DialView? = null

@JvmStatic
fun doSomething(){
// do something here
}
}
  • Sử dụng Kotlin compiler để compile Kotlin class sang Java class, chúng ta được kết quả:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public final class AnnotationTest {
@Nullable
private static DialView view;
public static final AnnotationTest INSTANCE;

@Nullable
public static final DialView getView() {
return view;
}

public static final void setView(@Nullable DialView var0) {
view = var0;
}

@JvmStatic
public static final void doSomething() {
}

// Ọther implementations will be ignored.
}
  • Kotlin compiler compile AnnotationTest object class thành AnnotationTest.java class. Chúng ta thấy:
    • Property view trong AnnotationTest object ⇔ property view và 2 static function getView()/setView() trong AnnotationTest.class.
    • Function doSomething() trong AnnotationTest object ⇔ static function doSomething() trong AnnotationTest.java.

II, @JvmOverloads

  • @JvmOverloads là 1 annotation áp dụng cho function hoặc constructor: trong java compiled file, Kotlin compiler tạo ra các overloading function dựa trên các default parameter values của function gốc.

  • Nếu một method có N parameter và M parameter có giá trị mặc định thì M overload function sẽ được sinh ra:

    • Overloading function thứ 1 có N - 1 parameter. Body của nó là gọi lại function gốc với N - 1 parameter đầu tiên và default value của parameter của parameter cuối cùng.
    • Overloading function thứ 2 có N - 2 parameter. Body của nó là gọi là function gốc với N - 2 parameter đàu tiên và default value của hai parameter cuối cùng.
  • Ví dụ 2: annotate constructor của class DialView với @JvmOverloads.

1
2
3
4
5
6
7
class DialView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
// Do something
}
  • Constructor trong Kotlin class có 3 parameter và 2 default parameter value. Do đó chúng ta sẽ có 2 overloading constructor.
  • Khi không sử dụng @JvmOverloads, class DialView được viết lại như sau:
1
2
3
4
5
6
7
8
9
10
11
class DiaView: View {
constructor(context: Context, attrs: AttributeSet? = null, defAttr: Int = 0) : super(
context, attrs, defAttr
)

// Overloading constructor 1.
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)

// Overloading constructor 2.
constructor(context: Context) : this(context, null, 0)
}
  • Ví dụ 3: annotate function doJvmOverloadsFunction() của class JvmOverloadsTest với @JvmOverloads
1
2
3
4
5
6
7
8
class JvmOverloadsTest {

@JvmOverloads
fun doJvmOverloadsFunction(address: String, location: String? = null, age: Int = 0){
// Do something here
}

}
  • Nếu không sử dụng @JvmOverloads, chúng ta có thể viết lại JvmOverloadTest như sau:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class JvmOverloadsTest {

fun doJvmOverloadsFunction(address: String, location: String? = null, age: Int = 0){
// Do something here
}

// Overloading function 1
fun doJvmOverloadsFunction(address: String, location: String?){
doJvmOverloadsFunction(address, location, 0)
}

// Overloading function 2
fun doJvmOverloadsFunction(address: String){
doJvmOverloadsFunction(address, null, 0)
}

}

III, @JvmField

  • @JvmField là một annotation được sử dụng cho câc field (property, parameter): trong java complied file, kotlin compiler sẽ không sinh ra method getter và setter cho variable, việc thực hiện get và set sẽ thông qua chính variable đó.
  • Do việc thực hiện get và set đều thông qua variable nên field đó là property thì nó không được có visibility modifier là private.
  • Annotation @JvmField có nhiều ý nghĩa hơn khi chúng ta sử dung một field của Kotlin class trong 1 Java class. Hãy xem xét ví dụ 4 và 5 để làm rõ hơn.
  • Ví dụ 4:
1
2
3
4
5
class JvmFieldTest {

var test: Int = 0

}
  • Khi bạn muốn sử dụng test trong Java class Test.java:
1
2
3
4
5
6
7
8
9
10
public class Test {

public void makeJvmFieldTest() {
JvmFieldTest jvmFieldTest = new JvmFieldTest();
// Kotlin compiler automatically generate getter and setter method for `test`.
jvmFieldTest.getTest();
jvmFieldTest.setTest(1);
}

}
  • Ví dụ 5: Viết lại ví dụ 4 và annotate test với JvmField
1
2
3
4
5
6
class JvmFieldTest {

@JvmField
var test: Int = 0

}
  • Khi bạn muốn sử dụng test trong Java class Test.java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Test {

public void makeJvmFieldTest() {
JvmFieldTest jvmFieldTest = new JvmFieldTest();

// There are no more any getter and setter methods
// jvmFieldTest.getTest(); // Compilation errors
// jvmFieldTest.setTest(1); // Compilation errors

// Expose via variable directly
jvmFieldTest.test;
jvmFieldTest.test = 1;
}

}