Google最近发布了Android Kotlin Guide,其中包括Kotlin和Java的interp guide,这篇文章主要介绍interp guide。
Java侧的注意事项
- 不要使用kotlin的hard keywords作为方法名,这样会导致kotlin调用Java方法需要使用backticks
val callable = Mockito.mock(Callable::class.java)
Mockito.`when`(callable.call()).thenReturn(/* … */)
符合SAM(single abstract method)规则的函数参数应该放到最后
如果要让方法能够在kotlin中作为property调用,要使用bean style前缀(get, set)
//Java
public final class User {
public String getName() { /* … */ }
public void setName(String name) { /* … */ }
}
//Kotlin
user.name = "Bob" // Invokes user.setName(String)
- 如果方法名符合kotlin中的operator overloading,要使功能与operator匹配
//Java
public final class IntBox {
private final int value;
public IntBox(int value) {
this.value = value;
}
public IntBox plus(IntBox other) {
return new IntBox(value + other.value);
}
}
//Kotlin
val one = IntBox(1)
val two = IntBox(2)
val three = one + two // Invokes one.plus(two)
- non-primitive的入参和出参都要用nullability notation标注,不然在kotlin中只能当成platform type而不能进行nullability检查
Kotlin侧注意事项
当一个文件包含top-level函数和属性时,要用
@file:JvmName("Foo")
去更改生成类的名字,不然会生成**Kt的名称。使用
@file:JvmMultifileClass
将多个声明同名JvmName的的属性和方法合并到一个类里。不用使用Nothing作为generic type
抛出checked exception的函数需要在文档里用
@Throw
标出。当返回shared或者read-only collections时, 要包装在immutable collection中或者做defensive copy
Companion Object中function要用@JvmStatic修饰
Incorrect: no annotation
class KotlinClass {
companion object {
fun doWork() {
/* … */
}
}
}
public final class JavaClass {
public static void main(String... args) {
KotlinClass.Companion.doWork();
}
}
Correct: @JvmStatic annotation
class KotlinClass {
companion object {
@JvmStatic fun doWork() {
/* … */
}
}
}
public final class JavaClass {
public static void main(String... args) {
KotlinClass.doWork();
}
}
- Companion object或者单例object中没有用const修饰的val变量,只能通过getter访问。
使用@JVMField修饰后,可以直接作为静态变量使用
Incorrect: no annotation
class KotlinClass {
companion object {
const val INTEGER_ONE = 1
val BIG_INTEGER_ONE = BigInteger.ONE
}
}
public final class JavaClass {
public static void main(String... args) {
System.out.println(KotlinClass.INTEGER_ONE);
System.out.println(KotlinClass.Companion.getBIG_INTEGER_ONE());
}
}
Incorrect: @JvmStatic annotation
class KotlinClass {
companion object {
const val INTEGER_ONE = 1
@JvmStatic val BIG_INTEGER_ONE = BigInteger.ONE
}
}
public final class JavaClass {
public static void main(String... args) {
System.out.println(KotlinClass.INTEGER_ONE);
System.out.println(KotlinClass.getBIG_INTEGER_ONE());
}
}
Correct: @JvmField annotation
class KotlinClass {
companion object {
const val INTEGER_ONE = 1
@JvmField val BIG_INTEGER_ONE = BigInteger.ONE
}
}
public final class JavaClass {
public static void main(String... args) {
System.out.println(KotlinClass.INTEGER_ONE);
System.out.println(KotlinClass.BIG_INTEGER_ONE;
}
}
因为extension function在Java中会转变为静态函数,所以要用@JvmName
转换成idiomatic的命名
sealed class Optional<T : Any>
data class Some<T : Any>(val value: T): Optional<T>()
object None : Optional<Nothing>()
@JvmName("ofNullable")
fun <T> T?.asOptional() = if (this == null) None else Some(this)
// FROM KOTLIN:
fun main(vararg args: String) {
val nullableString: String? = "foo"
val optionalString = nullableString.asOptional()
}
// FROM JAVA:
public static void main(String... args) {
String nullableString = "Foo";
Optional<String> optionalString =
Optionals.ofNullable(nullableString);
}
- 具有default value参数的函数需要使用
@JvmOverloads
Incorrect: No @JvmOverloads
class Greeting {
fun sayHello(prefix: String = "Mr.", name: String) {
println("Hello, $prefix $name")
}
}
public class JavaClass {
public static void main(String... args) {
Greeting greeting = new Greeting();
greeting.sayHello("Mr.", "Bob");
}
}
Correct: @JvmOverloads annotation.
class Greeting {
@JvmOverloads
fun sayHello(prefix: String = "Mr.", name: String) {
println("Hello, $prefix $name")
}
}
public class JavaClass {
public static void main(String... args) {
Greeting greeting = new Greeting();
greeting.sayHello("Bob");
}
}