Kotlin与java的互操作

Kotlin中调用Java
在Kotlin中可以使用java代码,而没有任何问题
1、将 Kotlin 中是关键字的 Java 标识符进行转义
一些 Kotlin 关键字在 Java 中是有效标识符:in、 object、 is 等等。 如果一个 Java 库使用了 Kotlin 关键字作为方法,你仍然可以通过反引号()字符转义它来调用该方法: foo.is`(bar)

2、空安全与平台类型
Java 中的任何引用都可能是 null,这使得 Kotlin 对来自 Java 的对象要求严格空安全是不现实的。 Java 声明的类型在 Kotlin 中会被特别对待并称为平台类型。对这种类型的空检查会放宽, 因此它们的安全保证与在 Java 中相同

val list = ArrayList() // 非空(构造函数结果)
list.add(“Item”)
val size = list.size // 非空(原生 int)
val item = list[0] // 推断为平台类型(普通 Java 对象)

平台类型是不可标示的,意味着不能在语言中明确地写下它们。 当把一个平台值赋值给一个 Kotlin 变量时,可以依赖类型推断(该变量会具有推断出的的平台类型, 如上例中 item 所具有的类型),或者我们可以选择我们期望的类型(可空或非空类型均可)

val nullable: String? = item // 允许,没有问题
val notNull: String = item // 允许,运行时可能失败

平台类型不能在程序中显式表述,因此在语言中没有相应语法。 然而,编译器和 IDE 有时需要(在错误信息中、参数信息中等)显示他们,所以我们用一个助记符来表示他们:

  1. T! 表示“T 或者 T?”,
  2. (Mutable)Collection! 表示“可以可变或不可变、可空或不可空的 T 的 Java 集合”,
  3. Array<(out) T>! 表示“可空或者不可空的 T(或 T 的子类型)的 Java 数组”

可以标注泛型类型的类型参数,以便同时为其提供可空性信息。例如,考虑这些 Java 声明的注解:

@NotNull
Set<@NotNull String> toSet(@NotNull Collection<@NotNull String> elements) { …… }

在 Kotlin 中可见的是以下签名:
fun toSet(elements: (Mutable)Collection) : (Mutable)Set { …… }
3、映射类型
Kotlin 特殊处理一部分 Java 类型。这样的类型不是“按原样”从 Java 加载,而是 映射 到相应的 Kotlin 类型。 映射只发生在编译期间,运行时表示保持不变。 Java 的原生类型映射到相应的 Kotlin 类型

Java类型Kotlin类型
Bytekotlin.Byte
shortkotlin.Short
IntIKotlin.Int
LongKotlin.Long
CharKotlin.Char
FloatKotlin.Float
doublekotlin.Double
booleankotlin.Boolean

一些非原生的内置类型也会作映射:

Java类型Kotlin类型
java.lang.Objectkotlin.Any!
java.lang.Cloneablekotlin.Cloneable!
java.lang.Comparablekotlin.Comparable!
java.lang.Enumkotlin.Enum!
java.lang.Annotationkotlin.Annotation!
java.lang.Deprecatedkotlin.Deprecated!
java.lang.CharSequencekotlin.CharSequence!
java.lang.Stringkotlin.String!
java.lang.Numberkotlin.Number!
java.lang.Throwablekotlin.Throwable!

Java 的装箱原始类型映射到可空的 Kotlin 类型:

Java类型Kotlin类型
java.lang.Bytekotlin.Byte?
java.lang.Shortkotlin.Short?
java.lang.Integerkotlin.Int?
java.lang.Longkotlin.Long?
java.lang.Characterkotlin.Char?
java.lang.Floatkotlin.Float?
java.lang.Doublekotlin.Double?
java.lang.Booleankotlin.Boolean?

Kotlin 中的 Java 泛型
Kotlin 的泛型与 Java 有点不同(参见泛型)。当将 Java 类型导入 Kotlin 时,我们会执行一些转换:
● Java 的通配符转换成类型投影,
◆ Foo<? extends Bar> 转换成 Foo<out Bar!>!,
◆ Foo<? super Bar> 转换成 Foo<in Bar!>!;
● Java的原始类型转换成星投影,
◆ List 转换成 List<*>!,即 List<out Any?>!。

和 Java 一样,Kotlin 在运行时不保留泛型,即对象不携带传递到他们构造器中的那些类型参数的实际类型。 即 ArrayList() 和 ArrayList() 是不能区分的。 这使得执行 is-检测不可能照顾到泛型。 Kotlin 只允许 is-检测星投影的泛型类型:

if (a is List<Int>) // 错误:无法检查它是否真的是一个 Int 列表
// but
if (a is List<*>) // OK:不保证列表的内容

Java调用Kotlin
Kotlin 属性会编译成以下 Java 元素:
● 一个 getter 方法,名称通过加前缀 get 算出;
● 一个 setter 方法,名称通过加前缀 set 算出(只适用于 var 属性);
● 一个私有字段,与属性名称相同(仅适用于具有幕后字段的属性)。
例如,var firstName: String 编译成以下 Java 声明:

private String firstName;
public String getFirstName() {
    return firstName;
}
public void setFirstName(String firstName) {
    this.firstName = firstName;
}

实例字段
如果需要在 Java 中将 Kotlin 属性作为字段暴露,那就需要使用 @JvmField 注解对其标注。 该字段将具有与底层属性相同的可见性。如果一个属性有幕后字段(backing field)、非私有、没有 open /override 或者 const修饰符并且不是被委托的属性,那么你可以用 @JvmField 注解该属性。

class C(id: String) {
    @JvmField val ID = id
}
// Java
class JavaClient {
    public String getID(C c) {
        return c.ID;
    }
}

静态字段
在命名对象或伴生对象中声明的 Kotlin 属性会在该命名对象或包含伴生对象的类中具有静态幕后字段。
通常这些字段是私有的,但可以通过以下方式之一暴露出来:
● @JvmField 注解;
● lateinit 修饰符;
● const 修饰符。
使用 @JvmField 标注这样的属性使其成为与属性本身具有相同可见性的静态字段。

class Key(val value: Int) {
    companion object {
        @JvmField
        val COMPARATOR: Comparator<Key> = compareBy<Key> { it.value }
    }
}
// Java
Key.COMPARATOR.compare(key1, key2);
// Key 类中的 public static final 字段

用 const 标注的(在类中以及在顶层的)属性在 Java 中会成为静态字段:

// 在文件 example.kt
object Obj {
    const val CONST = 1
}
class C {
    companion object {
        const val VERSION = 9
    }
}
const val MAX = 239

在Java中
int c = Obj.CONST;
int d = ExampleKt.MAX;
int v = C.VERSION;

静态方法
Kotlin 将包级函数表示为静态方法。 Kotlin 还可以为命名对象或伴生对象中定义的函数生成静态方法,如果你将这些函数标注为 @JvmStatic 的话。 如果你使用该注解,编译器既会在相应对象的类中生成静态方法,也会在对象自身中生成实例方法。 例如:

class C {
    companion object {
        @JvmStatic fun foo() {}
        fun bar() {}
    }
}
foo() 在 Java 中是静态的,而 bar() 不是:
C.foo(); // 没问题
C.bar(); // 错误:不是一个静态方法
C.Companion.foo(); // 保留实例方法
C.Companion.bar(); // 唯一的工作方式

可见性
Kotlin 的可见性以下列方式映射到 Java:
● private 成员编译成 private 成员;
● private 的顶层声明编译成包级局部声明;
● protected 保持 protected(注意 Java 允许访问同一个包中其他类的受保护成员, 而 Kotlin 不能,所以 Java 类会访问更广泛的代码);
● internal 声明会成为 Java 中的 public。internal 类的成员会通过名字修饰,使其更难以在 Java 中意外使用到,并且根据 Kotlin 规则使其允许重载相同签名的成员而互不可见;
● public 保持 public。

@JvmName 解决签名冲突
有时我们想让一个 Kotlin 中的命名函数在字节码中有另外一个 JVM 名称。 最突出的例子是由于类型擦除引发的。

fun List<String>.filterValid(): List<String>
fun List<Int>.filterValid(): List<Int>

这两个函数不能同时定义,因为它们的 JVM 签名是一样的:filterValid(Ljava/util/List;)Ljava/util/List;。 如果我们真的希望它们在 Kotlin 中用相同名称,我们需要用 @JvmName 去标注其中的一个(或两个),并指定不同的名称作为参数:

fun List<String>.filterValid(): List<String>
@JvmName("filterValidInt")
fun List<Int>.filterValid(): List<Int>

在 Kotlin 中它们可以用相同的名称 filterValid 来访问,而在 Java 中,它们分别是 filterValid 和 filterValidInt。
生成重载
通常,如果你写一个有默认参数值的 Kotlin 函数,在 Java 中只会有一个所有参数都存在的完整参数签名的方法可见,如果希望向 Java 调用者暴露多个重载,可以使用 @JvmOverloads 注解。

class Foo @JvmOverloads constructor(x: Int, y: Double = 0.0) {
    @JvmOverloads fun f(a: String, b: Int = 0, c: String = "abc") { …… }
}

对于每一个有默认值的参数,都会生成一个额外的重载,这个重载会把这个参数和它右边的所有参数都移除掉。在上例中,会生成以下代码 :

// 构造函数:
Foo(int x, double y)
Foo(int x)
// 方法
void f(String a, int b, String c) { }
void f(String a, int b) { }
void f(String a) { }

如果一个类的所有构造函数参数都有默认值,那么会为其生成一个公有的无参构造函数。这就算没有 @JvmOverloads 注解也有效。

Java与Kotlin的比较,参照:
https://github.com/MindorksOpenSource/from-java-to-kotlin

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值