在平时开发中不可避免的需要从Java调用Kotlin,今天就学习一下哪些是Java调用Kotlin更加方便的注解。
@JvmStatic
object StringUtil {
init {
println()
}
fun changeTo(value: String) {
}
}
StringUtil.INSTANCE.changeTo(data);
在Java中调用对象声明中的方法时,需要带INSTANCE,不像调用Java静态方法,直接使用类名+方法名,因为Kotlin反编译生成的Java代码就有一个静态INSTANCE实例
public final class StringUtil {
@NotNull
public static final StringUtil INSTANCE;
public final void changeTo(@NotNull String value) {
Intrinsics.checkNotNullParameter(value, "value");
}
private StringUtil() {
}
static {
StringUtil var0 = new StringUtil();
INSTANCE = var0;
System.out.println();
}
}
通过这个实例,可以直接调用类中的方法,如果我们想像调用Java方法那样调用Kotlin 对象声明的方法,应该怎么办呢,此时就需要用到@JvmStatic注解。
object StringUtil {
init {
println()
}
@JvmStatic
fun changeTo(value: String) {
}
}
StringUtil.changeTo(data);
添加@JvmStatic之后可以通过类名直接调用方法,反编译之后的Java代码是什么样呢?
public final class StringUtil {
@NotNull
public static final StringUtil INSTANCE;
@JvmStatic
public static final void changeTo(@NotNull String value) {
Intrinsics.checkNotNullParameter(value, "value");
}
private StringUtil() {
}
static {
StringUtil var0 = new StringUtil();
INSTANCE = var0;
System.out.println();
}
}
changeTo方法变成了静态方法,由此,我们可知@JvmStatic的作用。
@JvmStatic可以用来修饰方法或属性,声明为所注释的方法生成对应的静态方法,为所注释的属性生成静态的getter/setter。
@JvmName
@JvmName可以指定Kotlin生成的字节码中类,属性和方法的命名方式。
我们创建一个Kotlin文件StringUtil.kt,一个顶层函数
package com.lkk.kotlinbasic.myapp
fun changeTo(name: String) {
println(name)
}
StringUtilKt.changeTo("hello");
默认生成的类名为StringUtilKt,反编译后的Java代码如下:
public final class StringUtilKt {
public static final void changeTo(@NotNull String name) {
Intrinsics.checkNotNullParameter(name, "name");
System.out.println(name);
}
}
接下来我们使用@JvmName指定生成的类名
@file:JvmName("StringUtil")
package com.lkk.kotlinbasic.myapp
fun changeTo(name: String) {
println(name)
}
反编译之后的Java代码如下
public final class StringUtil {
public static final void changeTo(@NotNull String name) {
Intrinsics.checkNotNullParameter(name, "name");
System.out.println(name);
}
}
生成的类名正是咱们指定的类名。
@JvmName同样可以用来注解属性
class User {
val hasSystemAccess
get() = ""
}
我们在Java中调用时只能使用user.getHasSystemAccess(),如果我们想修改自动生成get方法的名称,可以使用@JvmName,这里有两种写法
class User {
val hasSystemAccess
@JvmName("hasSystemAccess")
get() = ""
}
class User {
@get:JvmName("hasSystemAccess")
val hasSystemAccess
get() = ""
}
这两种方式都能实现咱们得目的,参考最终生成的Java代码
public final class User {
@JvmName(
name = "hasSystemAccess"
)
@NotNull
public final String hasSystemAccess() {
return "";
}
}
通过使用 @JvmName 注解,Kotlin 会为带注解的项生成具有指定名称(而非默认名称)的字节码,同样可以使用@set:JvmName来修改setter的名称,此处不再赘述。
@JvmOverloads
Kotlin支持参数默认值,但Java不支持,从Java调用Kotlin带默认值的构造方法或函数时需要使用@JvmOverloads注解。
class User(name: String = "hello") {
@JvmOverloads
fun printUser(name: String, age: Int = 18) {
print("$name, $age")
}
}
@JvmOverloads
public final void printUser(@NotNull String name) {
printUser$default(this, name, 0, 2, (Object)null);
}
@JvmOverloads
public final void printUser(@NotNull String name, int age) {
Intrinsics.checkNotNullParameter(name, "name");
String var3 = name + ", " + age;
System.out.print(var3);
}
给方法printUser添加@JvmOverloads之后,生成了这个方法的重载版本。现在在Java代码中既可以调用printUser(@NotNull String name) ,也可以调用printUser(@NotNull String name, int age),没提交@JvmOverloads,只能调用printUser(@NotNull String name, int age)。
我试了一下给构造方法添加@JvmOverloads,添加之前和添加之后好像没有区别。
@JvmField
@JvmField用来注解属性,添加注解之后Kotlin不会为其自动生成getter/setter,并把它作为一个属性暴露。
class User @JvmOverloads constructor(@JvmField val id: Int, name: String = "hello") {
@JvmOverloads
fun printUser(name: String, age: Int = 18) {
print("$name, $age")
}
}
我们为id属性添加了@JvmField注解,可以看到反编译之后的Java代码并没有对应的getter/setter,并且在Java代码中可以直接调用id,而不需要使用getter方法。
对于object中的常量,还是建议使用const。
@Throws
Java 中有“受检异常”的概念,但Kotlin 没有受检异常,这就会导致在Java中调用Kotlin抛出受检异常的函数时报错,此时需要抛出@Throws,指示 Java 代码 Kotlin 函数会抛出异常。
fun openFile(filePath: String) {
val outputFile = File(filePath)
if (!outputFile.canWrite()) {
throw FileNotFoundException("Could not write to file: $filePath")
}
}
user.openFile("");
创建一个抛出异常的方法如上,并在Java侧调用此方法,代码编译时会抛出异常
java.io.FileNotFoundException: Could not write to file:
public final void openFile(@NotNull String filePath) {
Intrinsics.checkNotNullParameter(filePath, "filePath");
File outputFile = new File(filePath);
if (!outputFile.canWrite()) {
throw (Throwable)(new FileNotFoundException("Could not write to file: " + filePath));
}
}
这是编译之后Java代码。
@Throws(IOException::class)
fun openFile(filePath: String) {
val outputFile = File(filePath)
if (!outputFile.canWrite()) {
throw FileNotFoundException("Could not write to file: $filePath")
}
}
我们给openFile添加@Throws(IOException::class)注解,此时看一下反编译之后的Java代码
public final void openFile(@NotNull String filePath) throws IOException {
Intrinsics.checkNotNullParameter(filePath, "filePath");
File outputFile = new File(filePath);
if (!outputFile.canWrite()) {
throw (Throwable)(new FileNotFoundException("Could not write to file: " + filePath));
}
}
try {
user.openFile("");
} catch (IOException e) {
throw new RuntimeException(e);
}
在函数上添加了throws,此时在Java中再调用此方法,需要try-catch或继续往上抛异常。
Over
参考