在Java与Kotlin之间如何进行互操作详解

前言

目前kotlin是谷歌首推的开发Android的语言,但由于历史原因,我们绝大部分项目依旧还是以Java为主的,也就是说存在Java和Kotlin两种语言同时开发的情况。

有人会说把老项目全部翻译成Kotlin,的确可以怎么做,但是成本还是挺大的。我们只能一点一点慢慢的向kotlin语言迁移。

那么在迁移的过程中就避免不了Java和Kotlin相互调用的情况。即Kotlin调用Java或者Java调用Kotlin。下面我们就来具体看下两者之间相互操作的一些解决方案。

kotlin调用java

可空性(Nullability)

Java默认有数值可空性而kotlin没有,所以在调用Java的方法的时候不知道会不会收到空值。
所以我们在Kotlin中调用Java的时候需要添加 ?或者 !来告诉Kotlin有可能出现空值。

比如这里有一个Java方法,接受一组字符串后返回一组做字符串:

public Set<String> toSet(Collection<String> elements){
 //TODO
}

那么Kotlin在调用的时候是不能确定输入和输出是否可为空的。就需要使用?或者 !来​辅助判断。​

为了方便Kotlin调用,我们通常使用 @NotNull 注解来标识Java代码的非原始参数、字段、返回值。

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

这个Kotlin在调用的时候就明确知道不能为空,这里我们使用的是jetBrain的 @NotNull注解,当然还有其他选择,如下图:

这里还是推荐使用JetBrain或者Android的注解。

前缀属性:(getter、setter)

如果是使用Java bean,那么我们在Kotlin中调用就没有什么问题。

如果你的空参数方法是以get开头的,那么Kotlin就知道这是getter,就可以通过属性名来访问它。

相同的如果是由set开头的单一参数方法,那么Kotlin就知道这是setter,就通过属性名直接赋值。

当然is的工作原理也是和它们类似的。

我们定义一个Java bean:

class User {

 private String name;
 private int age;

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

 public int getAge() {
  return age;
 }

 public void setAge(int age) {
  this.age = age;
 }
}

Kotlin中访问

val user = User()
user.name = "四爷" //赋值
val age = user.age //获取age字段值

关键字(keywords)

kotlin中有很多系统定义的关键字如 fun is in objects、typeof、val、var、when、typealias等。

这些关键字在java是可以被使用的,但是在kotlin却是不行的。

函数或者参数使用了这些关键字,那么kotlin在调用的时候会出现一些问题,比如Java中定义了一个方法名叫 is 的方法。那么在Kotlin中直接调用就会报错。

那么最简单的方法就是重命名Java方法,但如果调用的是三方库的方法,就很难去重命名了。
所以我们另一种解决方式是在Kotlin调用java方法的时候加上 `` 反引号来使用。

Utils.`is`()

但是我们如果能重命名还是重命名,以防止代码出现太多的符号。

避免在任何扩展方法和扩展属性上使用Any

运算符重载(operator Overloading)

在Java不存在运算符重载,而kotlin有。比如:

a+b => a.plus(b)

在kotlin中将运算符 + 翻译为了方法 plus。

如果在Java中使用了同样的方法名称,比如 加(plus)、 减(minus)或者其他运算符名称,那么请务必确保他们与运算符兼容,避免意外调用他们。

Java调用Kotlin

JvmName & JvmMultifileClass

当我们在迁移的时候会将Java的工具类翻译为Kotlin拓展函数或者顶层函数。但是这样处理之后,在Java文件中是无法直接调用的,此时我们需要加注解 @file:JvmName(“文件名称”):

Ext.kt文件

@file:JvmName("ExtUtils")
package com.demo.javaAndKotlin

fun a(): String {
 ...
}

fun b(): String {
 ...
}

这里我们将名称命名为ExtUtils。此外,我们可能还有其他的顶层函数或者扩展函数。按照上面这种方式我们也可以指定一个其他的名称,但是如果我们也想使用ExtUtils这个名称的时候会报错:

Duplicate JVM class name

此时我们需要在不同的文件中加入新的注解 @file:JvmMultifileClass 。意思是将所有的文件合并到一个新的名称为ExtUtils文件中。

ExtOther.kt文件

@file:JvmMultifileClass
@file:JvmName("ExtUtils")
package com.demo.javaAndKotlin

fun c(str: Any): String {
 ...
}

我们在Ext.kt文件中也加入@file:JvmMultifileClass注解,我们就可以在Java文件中直接使用ExtUtils来调用 a(),b(),c()方法了。

JvmField

在 kotlin中我们使用的数据类即 data class 是不需要指定getter和setter的,可以直接通过字段名来访问它们。但是如果是在Java文件中调用data class依旧是需要使用getter和setter方法进行调用的。这里我们是可以修改他们的,那就是使用 @JvmField 注解,通过注解,可以直接将字段暴露出去进行访问。​

data class Person(

 @JvmField var name: String,
 @JvmField var age: Int
)

//java中调用
Person person = new Person("",1);
person.name = "";
person.age = 10;

但是也有例外就是lateinit修饰的字段会自动暴露,无需指定@JvmField注解。还有const修饰的字段也是一样会自动暴露。

另外,如果我们想在Java中调用setName的时候修改这个属性名称不叫setName,这里我们需要使用@set:JvmName 注解。同理修改getName使用@get:JvmName 。需要注意的是,指定了@set:JvmName或者@get:JvmName注解后不需要在指定@JvmField了。

data class Person(

 @set:JvmName("changeName")
 var name: String,
 @JvmField var age: Int,
 @get:JvmName("likesPink")
 var likesPink: Boolean
){
 lateinit var address:String
}

JvmStatic

当我们将Java文件的静态方法迁移到Kotlin中时,我们会将其放在 companion object中,但是这样处理之后在Java文件中无法直接调用,得通过companion对象实例方法来调用。

class MyService {
 internal fun doWork() {
  ...
 }

 companion object {
  fun schedule(context: Context) {
   ...
  }
 }
}

//在Java中调用
MyService.Companion.schedule(this);

幸运的是Kotlin提供了 @JvmStatic 注解。他会让Kotlin在编译器完成类封装后生成一个静态方法。

class MyService {
 internal fun doWork() {
  ...
 }

 companion object {
   @JvmStatic
  fun schedule(context: Context) {
   ...
  }
 }
}

//在Java中调用
MyService.schedule(this);

JvmOverloads

在Kotlin中我们可以给函数的参数设置默认值,即默认参数。但是这个功能在Java中是没有的。如果不做任何处理,那么在Java中调用函数的时候,就必须每个参数都要传入。那么我们设置的默认参数就没有任何意义了。

所以,Kotlin给我们提供了 @JvmOverloads注解,使用这个注解后,会让Kotlin编译器按照从左向右的顺序依次为每一个可选参数生成重载。

@JvmOverloads
fun Bitmap.resize(width: Int, height: Int = 200) {

}

//java调用
ExtUtils.resize(bitmap,100);

这里我们在Kotlin中很容易就理解了Bitmap.resize方法的含义,但是ExtUtils.resize这样调用的时候,方法名不够明确。所以我们可以使用@JvmName注解来指定名称。

@JvmName("resizeBitmap")
@JvmOverloads
fun Bitmap.resize(width: Int, height: Int = 200) {

}
//java调用
ExtUtils.resizeBitmap(bitmap,100);

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。

(0)

相关推荐

  • 对比Java讲解Kotlin中?.与!!.的区别

    前言 本文主要介绍了关于Kotlin中?.与!!.的区别,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧 1.?. //kotlin: a?.foo() //相当于java: if(a!=null){ a.foo(); } 2.!!. //kotlin: a!!.foo() //相当于java: if(a!=null){ a.foo(); }else{ throw new KotlinNullPointException(); } 时间宝贵的同学可以不要看下面的了(` _ `)

  • Kotlin 与 Java基本语法对比

    Kotlin 与 Java基本语法对比 Kotlin比Java更年轻,但它是一个非常有前途的编程语言,它的社区不断增长. 每个人都在谈论它,并说它很酷. 但为什么这么特别? 我们准备了一系列文章,分享我们在Kotlin开发Android应用程序的经验. 我们将讨论Kotlin与Java在语法,可用性,UI性能和异步性方面的区别,以便您可以决定哪种语言最适合您. 让我们从一些基本的语法差异开始. 这是第一个: 1. 使用Kotlin,你可以用更少的代码做更多 Kotlin的一个主要优点是它的简洁.

  • kotlin改善java代码实例分析

    序 本文主要举几个kotlin如何改善java代码的例子 字符串字面值及模板 字符串字面值 @Test fun testStringLiterals(){ val a = """if(a > 1) { | return a |}""".trimMargin() println(a) val b = """Foo Bar""".trimIndent() println(b) } 有了

  • Kotlin 基础教程之注解与java中的注解比较

    Kotlin 的注解完全兼容 Java 的注解. 声明注解 @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.EXPRESSION) @Retention(AnnotationRetention.SOURCE) @MustBeDocumented annotation class Fancy 可以通过向注解类添加元注解(meta

  • kotlin和Java的相互调用示例详解

    前言 互操作就是在Kotlin中可以调用其他编程语言的接口,只要它们开放了接口,Kotlin就可以调用其成员属性和成员方法,这是其他编程语言所无法比拟的.同时,在进行Java编程时也可以调用Kotlin中的API接口. 1.在kotlin中调用Java方法 Kotlin和Java是两种不同的语言,所以在互相调用的时候,会有一些特殊的语法.kotlin中对象属性默认就带有setter和getter方法,所以在kotlin中调用Java时直接变量名点属性就可获取到属性的setter和getter的一

  • Kotlin中单例模式和Java的对比浅析

    前言 单例模式,一直以来是我们在日常开发中最常用的一种设计模式,更是面试中非常重要,也非常容易被问到的问题.在日常开发中,大家常用的语言还是Java,但今天我给大家带来的是在Kotlin语言中,单例模式是怎么编写的,并且会对比Java方式,下面话不多说了,来一起看看详细的介绍吧 一.懒人写法(恶汉式) java中 public class Singleton{ public static final Singleton instance = new Singleton(); public Sin

  • 详解Kotlin中如何实现类似Java或C#中的静态方法

    大家可以在网络上搜到不少这样的文章,官方推荐是包级函数,也有人说用伴生对象(companion class).这些都是不错的选择,但并不完善,我们在不同的情况下有更好的选择.我总结了几种方法,分别是:包级函数.伴生对象.扩展函数和对象声明.这需要大家根据不同的情况进行选择. 一.包级函数 Kotlin和Java及C#不同的是,可以在包里面直接声明函数.做法和类中是一样的,这里就不多说了,的确是一个非常好的选择.适用于函数不需要不包内部的类进行数据共享的方法. 二.伴生对象 从语义上来讲,伴生函数

  • Kotlin传递可变长参数给Java可变参数实例代码

    本文研究的主要是Kotlin传递可变长参数给Java可变参数的方法,具体实现代码如下. 定义Java可变参数方法 package com.tcl.john.studymvvm.utils; /** * 调用Java方法的工具类 * Created by ZhangJun on 2017/10/25. */ public class CallJavaUtils { public static int addNumbers(String name, int... args) { int result

  • Kotlin与Java的主客观对比分析

    Kotlin Kotlin是一门相对比较新的JVM语言,JetBrains自2011年以来一直在积极地开发. 多年来,该语言在Android社区受到的关注度越来越高,并在Google IO 2017大会之后成为Android开发领域最热门的话题.这次大会宣布,Android正式支持Kotlin. 遗憾的是,虽然已经有许多关于Kotlin的文章,但并没有多少客观信息,许多开发人员仍然在苦思冥想,迁移到Kotlin是否是一条正确的道路. 在本文的剩余部分,我将尝试提供一个在将Kotlin作为Java

  • 在Java与Kotlin之间如何进行互操作详解

    前言 目前kotlin是谷歌首推的开发Android的语言,但由于历史原因,我们绝大部分项目依旧还是以Java为主的,也就是说存在Java和Kotlin两种语言同时开发的情况. 有人会说把老项目全部翻译成Kotlin,的确可以怎么做,但是成本还是挺大的.我们只能一点一点慢慢的向kotlin语言迁移. 那么在迁移的过程中就避免不了Java和Kotlin相互调用的情况.即Kotlin调用Java或者Java调用Kotlin.下面我们就来具体看下两者之间相互操作的一些解决方案. kotlin调用jav

  • java中生成任意之间数的随机数详解

    这篇文章主要介绍了java中生成任意之间数的随机数详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 public static int cssjs(int a,int b) { Random rand=new Random(); int zhi; zhi=rand.nextInt(b)%(b-a+1)+a; return zhi; } 我们观察其Random对象的nextInt(int)方法,发现这个发现这个方法将生成 0 ~ 参数之间随机取

  • Kotlin中常见的符号详解

    前几年的Google I/O大会上,Google正式宣布,Kotlin将会成为Android开发的官方支持语言.除了Android外,Kotlin还可以完全作为服务端开发的语言,比如在未来的Spring 5就将对Kotlin提供强大的支持.以及浏览器编程语言,与JS进行交互. Kotlin是一门静态语言,支持多种平台,包括移动端.服务端以及浏览器端,此外,Kotlin还是一门融合了面向对象与函数式编程的语言,支持泛型.安全的空判断,并且Kotlin与Java可以做到完全的交互. 现在介绍Kotl

  • kotlin 协程上下文异常处理详解

    目录 引言 一.协程上下文 1.CoroutineContext 2.CorountineScope 3.子协程继承父协程 二.协程的异常传递 1.协程的异常传播 2.不同上下文(没有继承关系)之间协程异常会怎么样? 3.向用户暴露异常 三.协程的异常处理 使用SupervisorJob 异常捕获器CoroutineExceptionHandler Android中全局异常的处理 引言 从前面我们可以大致了解了协程的玩法,如果一个协程中使用子协程,那么该协程会等待子协程执行结束后才真正退出,而达

  • Kotlin Service服务组件开发详解

    目录 服务简介 服务的创建 服务的启动方式 Service的生命周期 Activity和Service进行通信 实现前台Service 服务简介 服务是Android中的四大组件之一,它能够长期在后台运行且不提供用户界面.即使用户切到另一应用程序,服务仍可以在后台运行. 服务的创建 (1)创建Service子类 class MyService : Service() { override fun onBind(intent: Intent): IBinder { TODO("Return the

  • Java并发编程(CyclicBarrier)实例详解

    Java并发编程(CyclicBarrier)实例详解 前言: 使用JAVA编写并发程序的时候,我们需要仔细去思考一下并发流程的控制,如何让各个线程之间协作完成某项工作.有时候,我们启动N个线程去做一件事情,只有当这N个线程都达到某一个临界点的时候,我们才能继续下面的工作,就是说如果这N个线程中的某一个线程先到达预先定义好的临界点,它必须等待其他N-1线程也到达这个临界点,接下来的工作才能继续,只要这N个线程中有1个线程没有到达所谓的临界点,其他线程就算抢先到达了临界点,也只能等待,只有所有这N

  • Kotlin开发Android应用实例详解

    Kotlin开发Android应用实例详解 相关文章:关于Kotlin语言的基础介绍: http://www.jb51.net/article/114086.htm 我们简单的知道了Kotlin这门新语言的优势,也接触了一些常见的语法及其简单的使用,相信你会对它有浓厚的兴趣,暂且理解为对它感兴趣吧,哈哈哈.那么,我们该如何在Android中应用这门新的语言呢?今天的这篇文章带你学习使用Kotlin开发Android应用,并对比我们传统语言Java,让你真真切切的感受到他的美和优雅. 配置 项目g

  • java中注解机制及其原理的详解

    java中注解机制及其原理的详解 什么是注解 注解也叫元数据,例如我们常见的@Override和@Deprecated,注解是JDK1.5版本开始引入的一个特性,用于对代码进行说明,可以对包.类.接口.字段.方法参数.局部变量等进行注解.它主要的作用有以下四方面: 生成文档,通过代码里标识的元数据生成javadoc文档. 编译检查,通过代码里标识的元数据让编译器在编译期间进行检查验证. 编译时动态处理,编译时通过代码里标识的元数据动态处理,例如动态生成代码. 运行时动态处理,运行时通过代码里标识

  • java 单播、广播、组播详解及实例代码

    java 单播.广播.组播详解及实例代码 在当前网络通信中(TCP/IP也不例外)有三种通信模式:单播.广播.组播(又叫多播, 个人感觉叫多播描述的有点不恰当),其中多播出现的时间最晚,但同时具备单播和广播的优点,最具有发展前景. 一.通信方式分类: 1.单播:单台主机与单台主机之间的通信: 2.广播:单台主机与网络中所有主机的通信: 3.组播:单台主机与选定的一组主机的通信: 二.单播:    单播是网络通信中最常见的,网络节点之间的通信 就好像是人们之间的对话一样.如果一个人对另外一个人说话

  • 基于Java class对象说明、Java 静态变量声明和赋值说明(详解)

    先看下JDK中的说明: java.lang.Object java.lang.Class<T> Instances of the class Class represent classes and interfaces in a running Java application. An enum is a kind of class and an annotation is a kind of interface. Every array also belongs to a class tha

随机推荐