竹笋

注册

 

发新话题 回复该主题

Kotlin的互操作Kotlin与J [复制链接]

1#

互操作就是在Kotlin中可以调用其他编程语言的接口,只要它们开放了接口,Kotlin就可以调用其成员属性和成员方法,这是其他编程语言所无法比拟的。同时,在进行Java编程时也可以调用Kotlin中的API接口。

Kotlin与Java互操作

1Kotlin调用Java

Kotlin在设计时就考虑了与Java的互操作性。可以从Kotlin中自然地调用现有的Java代码,在Java代码中也可以很顺利地调用Kotlin代码。

在Kotlin中调用Java的Util的list库。

packagejqiang.Mutual.Kotlinimportjava.util.*fundemo(sourceistInt){vallist=ArrayListInt()for(iteminlist){list.add(item)}for(iin0..source.size-1){list=source}}

基本的互操作行为如下:

1.属性读写

Kotlin可以自动识别Java中的getter/setter;在Java中可以过getter/setter操作Kotlin属性。

自动识别Java中的getter/setter。

packagejqiang.Mutual.Kotlinimportjava.util.*funmain(args:ArrayString){valcalendar=Calendar.getInstance()println(calendar.firstDayOfWeek)if(calendar.firstDayOfWeek==1){//调用getFirstDayOfWeek()方法calendar.firstDayOfWeek=2//调用setFirstDayOfWeek()方法}println(calendar.firstDayOfWeek)}

遵循Java约定的getter和setter方法(名称以get开头的无参数方法和以set开头的单参数方法)在Kotlin中表示为属性。如果Java类只有一个setter,那么它在Kotlin中不会作为属性可见,因为Kotlin目前不支持只写(set-only)属性。

2.空安全类型

Kotlin的空安全类型的原理是,Kotlin在编译过程中会增加一个函数调用,对参数类型或者返回类型进行控制,开发者可以在开发时通过注解

Nullable和

NotNull方式来弥补Java中空值异常。

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

空值实例。

vallist=ArrayListString()//非空(构造函数结果)list.add("Item")valsize=list.size()//非空(原生Int)Valitem=list[0]//推断为平台类型(普通Java对象)

当调用平台类型变量的方法时,Kotlin不会在编译时报告可空性错误,但是在运行时调用可能会失败,因为空指针异常。

item.substring(1)//允许,如果item==null可能会抛出异常

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

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

如果选择非空类型,编译器会在赋值时触发一个断言,这样可以防止Kotlin的非空变量保存空值。当把平台值传递给期待非空值等的Kotlin函数时,也会触发一个断言。总的来说,编译器尽力阻止空值通过程序向远传播(由于泛型的原因,有时这不可能完全消除)。

3.返回void的方法

如果在Java中返回void,那么Kotlin返回的就是Unit。如果在调用时返回void,那么Kotlin会事先识别该返回值为void。

4.注解的使用

JvmField是Kotlin和Java互相操作属性经常遇到的注解;

JvmStatic是将对象方法编译成Java静态方法;

JvmOverloads主要是Kotlin定义默认参数生成重载方法;

file:JvmName指定Kotlin文件编译之后生成的类名。

5.NoArg和AllOpen

数据类本身属性没有默认的无参数的构造方法,因此Kotlin提供一个NoArg插件,支持JPA注解,如

Entity。AllOpen是为所标注的类去掉final,目的是为了使该类允许被继承,且支持Spring注解,如

Componet;支持自定义注解类型,如

Poko。

6.泛型

Kotlin中的通配符“”代替Java中的“?”;协变和逆变由Java中的extends和super变成了out和in,如ArrayList;在Kotlin中没有Raw类型,如Java中的List对应于Kotlin就是List。

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

if(aisListInt)//错误:无法检查它是否真的是一个Int列表if(aisList*)//OK:不保证列表的内容

7.SAM转换

就像Java8一样,Kotlin支持SAM转换,这意味着Kotlin函数字面值可以被自动转换成只有一个非默认方法的Java接口的实现,只要这个方法的参数类型能够与这个Kotlin函数的参数类型相匹配就行。

首先使用Java创建一个SAMInJava类,然后通过Kotlin调用Java中的接口。

Java代码:

packagejqiang.Mutual.Java;importjava.util.ArrayList;publicclassSAMInJava{privateArrayListRunnablerunnables=newArrayListRunnable();publicvoidaddTask(Runnablerunnable){runnables.add(runnable);System.out.println("add:"+runnable+",size"+runnables.size());}PublicvoidremoveTask(Runnablerunnable){runnables.remove(runnable);System.out.println("remove:"+runnable+"size"+runnables.size());}}

Kotlin代码:

packagejqiang.Mutual.Kotlinimportjqiang.Mutual.Java.SAMInJavafunmain(args:ArrayString){varsamJava=SAMInJava()vallamba={print("hello")}samJava.addTask(lamba)samJava.removeTask(lamba)}

运行结果如下:

add:jqiang.Mutual.Kotlin.SamKt$sam$Runnable$bef91c64

c6bsize1remove:jqiang.Mutual.Kotlin.SamKt$sam$Runnable$bef91c64

2bf2dsize1

如果Java类有多个接受函数式接口的方法,那么可以通过使用将Lambda表达式转换为特定的SAM类型的适配器函数来选择需要调用的方法。这些适配器函数也会按需由编译器生成。

vallamba={print("hello")}samJava.addTask(lamba)

SAM转换只适用于接口,而不适用于抽象类,即使这些抽象类只有一个抽象方法。此功能只适用于Java互操作;因为Kotlin具有合适的函数类型,所以不需要将函数自动转换为Kotlin接口的实现,因此不受支持。

2Java调用Kotlin

在Java中可以轻松地调用Kotlin代码。

1.属性

Kotlin属性会被编译成以下Java元素:

getter方法,其名称通过加前缀get得到;

setter方法,其名称通过加前缀set得到(只适用于var属性);

私有字段,与属性名称相同(仅适用于具有幕后字段的属性)。

将Kotlin变量编译成Java中的变量声明。

Kotlin部分代码:

varfirstName:StringJava部分代码:privateStringfirstName;publicStringgetFirstName(){returnfirstName;}publicvoidsetFirstName(StringfirstName){this.firstName=firstName;}

如果属性名称是以is开头的,则使用不同的名称映射规则:getter的名称与属性名称相同,并且setter的名称是通过将is替换成set获得的。例如,对于属性isOpen,其getter会称作isOpen(),而其setter会称作setOpen()。这一规则适用于任何类型的属性,并不仅限于Boolean。

2.包级函数

在jqiang.Mutual.Kotlin包内的example.kt文件中声明的所有函数和属性,包括扩展函数,都被编译成一个名为jqiang.Mutual.Kotlin.ExampleKt的Java类的静态方法。

包级函数调用。

packagejqiang.Mutual.Kotlinfunbar(){println("这只是一个bar方法")}

Java部分代码:

packagejqiang.Mutual.Java;

publicclassexample{

publicstaticvoidmain(String[]args){

jqiang.Mutual.Kotlin.ExampleKt.bar();

}

可以使用

JvmName注解修改所生成的Java类的类名:

file:JvmName("example")packagejqiang.Mutual.Kotlin

那么Java调用时就需要修改类名:

jqiang.Mutual.Kotlin.example.bar();

在多个文件中生成相同的Java类名(包名相同并且类名相同或者有相同的

JvmName注解)通常是错误的。然而,编译器能够生成一个单一的Java外观类,它具有指定的名称且包含来自于所有文件中具有该名称的所有声明。要生成这样的外观,请在所有的相关文件中使用

JvmMultifileClass注解。

file:JvmName("example")

file:JvmMultifileClasspackagejqiang.Mutual.Kotlin

3.实例字段

如果需要在Java中将Kotlin属性作为字段暴露,那么就需要使用

JvmField注解对其进行标注。该字段将具有与底层属性相同的可见性。如果一个属性有幕后字段(BackingField)、非私有的、没有open/override或者const修饰符,并且不是被委托的属性,那么可以使用

JvmField注解该属性。

4.静态方法

Kotlin将包级函数表示为静态方法。如果对这些函数使用

JvmStatic进行标注,那么Kotlin还可以为在命名对象或伴生对象中定义的函数生成静态方法。如果使用该注解,那么编译器既会在相应对象的类中生成静态方法,也会在对象自身中生成实例方法。例如:

classC{

分享 转发
TOP
发新话题 回复该主题