@946898963
2020-07-09T16:17:06.000000Z
字数 7189
阅读 837
Kotlin
a. 当Java中的方法/字段的名称是Kotlin中的关键字时(如: in is object等), 我们可以通过使用反引号`来转义这个关键字
foo.`is`(bar)
b. Java 中的 int 和 Integer 只会对应 Kotlin 中 Int 这一种数据类型
Java 中的 int[] 会对应 Kotlin 中的 IntArray
Java 中的 Integer[] 会对应 Kotlin 中的 Array
其他的基本类型数组定义也是和上面一样的规则
// Java
public void method(int[] intArray) {}
// Kotlin
method(IntArray(10))
c. Kotlin 调用 Java 中的变长参数的方法时, 可以使用 * 来传递/扩展数组
// Java
public void method(int... params) {}
// Kotlin
val intArray = IntArray(10)
method(*intArray) // 传递数组
method(*intArray, 1, 2, 3) // 扩展数组
d. SAM接口的转换
总结: Kotlin中只有调用以Java中定义的SAM接口为参数的方法(不论是Java中还是Kotlin 中定义的)时, 编译器才会SAM转换, 从而我们才可以使用Lambda来传入方法, 否则都要实现 匿名内部类来传入方法
// Java
public interface InterfaceJAVA {
void print(String text);
}
public void printJavaFromJava(InterfaceJAVA interfaceJAVA) {
interfaceJAVA.print("printJavaFromJava");
}
public void printKotlinFromJava(InterfaceKotlin interfaceKotlin) {
interfaceKotlin.print("printKotlinFromJava");
}
// Kotlin
interface InterfaceKotlin {
fun print(text: String)
}
fun printJavaFromKotlin(interfaceJava: InterfaceJAVA) {
interfaceJava.print("printJavaFromKotlin")
}
fun printKotlinFromKotlin(interfaceKotlin: InterfaceKotlin) {
interfaceKotlin.print("printKotlinFromKotlin")
}
// 调用Java中定义的以Java中定义的SAM接口为参数的方法
printJavaFromJava { println(it) }
// 调用Java中定义的以Kotlin中定义的SAM接口为参数的方法
printKotlinFromJava(object : InterfaceKotlin {
override fun print(text: String) {
println(text)
}
})
// 调用Kotlin中定义的以Java中定义的SAM接口为参数的方法
printJavaFromKotlin(InterfaceJAVA { println(it) })
// 调用Kotlin中定义的以Kotlin中定义的SAM接口为参数的方法
printKotlinFromKotlin(object : InterfaceKotlin {
override fun print(text: String) {
println(text)
}
e. Kotlin中所有的异常都是非受检异常, 所以当我们在Kotlin中调用一个Java中的方法时, 需要自己判断是否需要try catch
Kotlin认为Java中的受检异常机制在大型项目中并不能够很好的增加项目的质量, 而且还会降低代码的可读性和复用性, 也会增加开发成本, 所以就把受检异常这个机制给去除了....
// Java
public void methodWithException(int i) throws IOException {
System.out.println("methodWithException start");
if (i == 1) {
throw new IOException();
}
System.out.println("methodWithException end");
}
// Java中调用methodWithException方法时, 必须强制处理IOException这个异常,
// 因为IOException是一个受检异常
try {
methodWithException(0);
} catch (IOException e) {
e.printStackTrace();
}
// Kotlin
// Kotlin中所有异常都不需要强制处理, 因此我们不需要强制的在调用methodWithException
// 的地方去强制的try catch IOException异常
methodWithException(0)
a. Java中调用Kotlin中定义的top-level declarations(顶级的类/方法/变量/常量等)时, 默认使用[Kotlin文件名]KT.[declarations name]的方式调用, 也可以在KT文件中使用JvmName注解来重新定义顶层变量的文件名
// Test.Kt
package org.example
fun getTime() { /*...*/ }
// Java
org.example.TestKt.getTime();
// Test.Kt
@file:JvmName("DemoUtils")
package org.example
fun getTime() { /*...*/ }
// Java
org.example.DemoUtils.getTime();
b. 如果想把多个KT文件中的top-level declarations合并, 可以使用JvmMultifileClass注解来合并多个Kt文件
// oldutils.kt
@file:JvmName("Utils")
@file:JvmMultifileClass
package org.example
fun getTime() { /*...*/ }
// newutils.kt
@file:JvmName("Utils")
@file:JvmMultifileClass
package org.example
fun getDate() { /*...*/ }
// Java
org.example.Utils.getTime();
org.example.Utils.getDate();
c. Java调用Kotlin的属性时如果不想通过getter/setter调用, 而是使用[objectName].[field]的方式调用, 则可以使用JvmField注解
前提是这个字段 1. 不是Private 2. 没有open override const修饰符 3. 不是委托属性
// Kotlin
class Person {
@JvmField var name: String = "石硕"
var height: Int = 190
lateinit var brother: Person
}
// Java
Person person = new Person();
person.name = "猜猜我是谁" // 加了@JvmField注解后就可以直接操作field
person.setHeight(170) // 正常情况下只能使用setter/getter来操作field
person.brother = new Person() // 使用lateinit修饰的field也可以在Java中直接操作
d. Kotlin中的静态属性需要定义在companion object / 自定义的object中.
Java中调用Kotlin声明的静态属性时, 默认使用如下的两种方式:
如果想使用[ClassName].[fieldName] / [ObjectName].[fieldName] 的方式调用Kotlin中定 义的静态变量, 可以使用如下的三种方式:
// Kotlin
class TestStaticField {
companion object {
const val TEXT = "Text."
@JvmField
val TEXT_WITH_JVM_FIELD = "Text with JvmField"
lateinit var TEXT_WITH_LATE_INIT: String
val TEXT_NORMAL = "Text normal"
}
}
// Java
// 调用Const修饰静态属性
System.out.println(TestStaticField.TEXT);
// 调用@JvmField修饰的静态属性
System.out.println(TestStaticField.TEXT_WITH_JVM_FIELD);
// 调用latainit修饰的静态属性
TestStaticField.TEXT_WITH_LATE_INIT = "Text with lateinit";
// 默认的方式使用静态属性
System.out.println(TestStaticField.Companion.getTEXT_NORMAL());
e. Kotlin中的静态方法与静态属性一样需要定义在companion object / 自定义的object中.
Java中调用Kotlin声明的静态方法时, 默认使用如下的两种方式:
如果想使用[ClassName].[methodName] / [ObjectName].[methodName] 的方式调用Kotlin中定义的静态方法, 可以使用@JvmStatic注解
// Kotlin
class TestStaticMethod {
companion object {
@JvmStatic
fun methodWithJvmStatic() {}
fun methodWithoutJvmStatic() {}
}
}
// Java
// 调用@JvmStatic修饰的方法
TestStaticMethod.Companion.methodWithoutJvmStatic();
// 调用未使用@JvmStatic修饰的方法
TestStaticMethod.methodWithJvmStatic();
f. kotlin 的调用规范与 Java 不同, 我们可以通过@JvmName 来设计名称, 使其符合这两种语言的惯例或与各自的标准库命名保持一致。
// Utils.Kt
@JvmName("isStringBlankOrEmpty")
fun String?.isBlankOrEmpty(): Boolean {
return this != null && this.isNotEmpty()
}
// Kotlin
"123".isBlankOrEmpty()
// Java
UtilsKt.isStringBlankOrEmpty("123");
g. 当Kotlin中field的getter/setter与方法重名时, 我们也可以使用@JvmName 来对getter/setter重新命名
// Kotlin
class Person {
val height: Int = 190
val name: String = "石硕"
@JvmName("getNameProp")
get
@get:JvmName("getCountryProp")
@set:JvmName("setCountryProp")
val country: String = "China"
fun getName() = "猜猜我是谁"
}
// Kotlin
Person().name
Person().getName()
// Java
Person person = new Person();
person.getNameProp();
person.getName();
h. Java在调用Kotlin使用默认参数的方法时, 如果方法没有使用@JvmOverloads时, 则该方法只会被编译成一个拥有全部参数的方法来供Java调用.
// Kotlin
fun methodWithDefault(paramsA: Int = 0, paramsB: String = "123", paramsC: Boolean = true) {}
@JvmOverloads
fun methodWithDefaultAndJvmOverloads(paramsA: Int = 0, paramsB: String = "123", paramsC: Boolean = true) {}
// Java
methodWithDefault(1); // 编译错误
methodWithDefault(1, "456", false); // 编译成功
methodWithDefaultAndJvmOverloads(1) // 编译成功
i. Kotlin中所有的异常都是非受检异常, 我们可以通过@Throws注解来通知Java调用该方法时, 需要处理的受检异常是什么.
// Kotlin
fun writeToFile() {
/*...*/
throw IOException()
}
@Throws(IOException::class)
fun writeToFileWithThrows() {
/*...*/
throw IOException()
}
// Java
try {
writeToFile();
}
catch (IOException e) { // 编译错误 编译器获取不到受检异常
e.printStackTrace();
}
try {
writeToFileWithThrows();
}
catch (IOException e) { // 编译成功
e.printStackTrace();
}
j. 当Kotlin的方法中定义的参数为非空类型时, 在方法开始时会使用Intrinsics.checkParameterIsNotNull()方法, 去检查该参数是否为空, 如果为null则会抛出异常, 导致应用Crash.
因此Java在调用Kotlin中如果在Kotlin中定义的方法时, 一定要判断好是否可以传null
// Intrinsics.java
public static void checkParameterIsNotNull(Object value, String paramName) {
if (value == null) {
throwParameterIsNullException(paramName);
}
}
// Kotlin
fun methodA(paramsA:Int?)
fun methodB(paramsA:Int)
// Java
methodA(null) // 正常运行
methodB(null) // 运行时Crash
k. 当从Java中调用Kotlin中含有函数类型的方法时, 如果此函数类型的返回值是Unit, 则在Java的lambda的最后需要返回return Unit.INSTANCE;
// Kotlin
fun printMethod(callback: (str: String) -> Unit) {
callback("Hello World")
}
printMethod { println(it) } // Kotlin中不需要返回Unit.INSTANCE
// Java
printMethod(text -> {
System.out.println(text);
return Unit.INSTANCE; // Java中需要返回Unit.INSTANCE
});
如果想避免这种这种情况只能选择不使用Kotlin的函数类型, 而使用SAM接口. 但是这样做就会使 Kotlin在调用时无法使用lambda, 只能传递使用匿名内部类
// Kotlin
interface InterfaceKotlin {
fun print(text: String)
}
fun printMethod(callback: InterfaceKotlin) {
callback.print("Hello World")
}
// Kotlin只能使用匿名内部类调用
printMethod(object : InterfaceKotlin {
override fun print(text: String) {
println(text)
}
})
// Java中不需要单独返回Unit.INSTANCE
printMethod(text -> {
System.out.println(text);
});
官方说目前没有好的解决方案, 建议在Kotlin的方法中使用函数类型参数, 而不是选择SAM接口类型.