Kotlin语法笔记(26) -Kotlin 与 Java 共存(1)

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
全球加速 GA,每月750个小时 15CU
EMR Serverless StarRocks,5000CU*H 48000GB*H
简介: Kotlin语法笔记(26) -Kotlin 与 Java 共存(1)

本系列学习教程笔记属于详细讲解Kotlin语法的教程,需要快速学习Kotlin语法的小伙伴可以查看“简洁” 系列的教程

快速入门请阅读如下简洁教程:
Kotlin学习教程(一)
Kotlin学习教程(二)
Kotlin学习教程(三)
Kotlin学习教程(四)
Kotlin学习教程(五)
Kotlin学习教程(六)
Kotlin学习教程(七)
Kotlin学习教程(八)
Kotlin学习教程(九)
Kotlin学习教程(十)

Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)

Kotlin 与 Java 共存(一)

大家好,经过前面的课程,相信大家对 Kotlin 已经有了一个初步的认识,那么我们在项目中究竟应该怎么应用 Kotlin 呢?

首先,我们的项目基本上都是使用 Java 编写的,我们没有精力也没有必要去全部用 Kotlin 重写。其次,Java 作为一门历经考验的语言,自然有它存在的道理,Kotlin 作为崭露头角的新秀,自然也有它发力的方向,我们没必要舍弃哪个,而是让他们共存,各取所长。正像 Kotlin 的设计理念一样:

100% interoperable with Java™

比如,在编写 JavaBean 或者说数据结构类时,用 Java 写起来就要繁琐一些,这样的类我比较倾向于用 Kotlin 编写;编写上层代码时,经常会用到一些接口回调,通常这些回调也只有一个方法,于是我也倾向于使用 Kotlin 编写 —— 而这在 Android 的 UI 层代码中体现的尤为明显;编写 TestCase 也是比较倾向于用 Kotlin 的,我最早认识 Kotlin 就是在编译 Intellij 的源码的时候,他们在两三年前的源码中就开始大量使用 Kotlin 编写 TestCase 了。

再比如,对于一些较为底层的框架性代码,涉及到较多比较 Tricky 的代码时,我更喜欢用 Java 写,因为 Java 对于变量类型、构造方法、泛型参数的限制要小一些等等。

总体来讲就是,你想要追求代码简洁、美观、精致,你应该倾向于使用 Kotlin,而如果你想要追求代码的功能强大,甚至有些黑科技的感觉,那 Java 还是当仁不让的。

说了这么多,还是那句话,让他们共存,各取所长。

那么问题来了,怎么共存呢?虽然一说理论我们都知道,跑在 Jvm 上面的语言最终都是要编成 class 文件的,在这个层面大家都是 Java 虚拟机的字节码,可他们在编译之前毕竟还是有不少差异的,这可如何是好?

正所谓兵来将挡水来土掩,有多少差异,就要有多少对策,这一期我们先讲在 Java 中调用 Kotlin

##1. 属性 我们在 Kotlin 中编写了一个类,当中有一个属性:

data class Person(var name: String)

我们在 Java 中要怎么使用呢?

Person person = new Person("benny");
System.out.println(person.getName());//benny
person.setName("andy");
System.out.println(person.getName());//andy

当然,在 Java 看来,name 这个成员是可以为 null 的,不过如果你胆敢设置一个 null 进去,信不信 Kotlin 跟你抱怨:

Exception in thread "main" java.lang.IllegalArgumentException: Parameter specified as non-null is null: method net.println.kt14.Person.setName, parameter <set-?>
    at net.println.kt14.Person.setName(Person.kt)
    at net.println.kt14.PersonMain.main(PersonMain.java:13)

其实,对于 null 的检查也没什么特别的,用我之前教大家的办法看下 Kotlin 的字节码:

INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V

原来是调用了 Intrinsics.checkParameterIsNotNull 方法,这方法很简单的,大家都可以写得出来:

public static void checkParameterIsNotNull(Object value, String paramName) {
    if (value == null) {
        throwParameterIsNullException(paramName);
    }
}

其实所有空安全的秘密都在这个类里面了,看到 Kotlin 的背后站着强大的 Java,我真是感到欣慰。

那么话说究竟能不能像在 Java 当中那样直接访问 Kotlin 的属性呢?

data class Person(var name: String, @JvmField var age: Int)

我们看到我们的 Person 有年龄啦,不过它与 name 不太一样,多了一个 @JvmField 的注解。

Person person = new Person("benny", 27);
System.out.println(person.getName() + " is " + person.age); //benny is 27
person.setName("andy");
person.age = 26;
System.out.println(person.getName() + " is " + person.age); //andy is 26

看,这就跟在 Java 当中的一样了。所谓有得必有失,用 @JvmField 标注的属性是不可以声明为 private 的,同时也是不可以像其他 Kotlin 属性那样直接自定义 getter 和 setter 的,你只能像 Java 那样自己写:

...
fun getAge(): Int = age

fun setAge(value: Int){
    age = value
}
...

仔细想想,这实在是多此一举了。

##2. object 我们知道在 Kotlin 当中最简单的单例就是 object 了,可是 Java 并没有这样的特性。那我们要怎么访问 object 呢?

object Singleton{
    fun printHello(){
        println("Hello")
    }
}

如果大家看过之前的单例那一期,我们给大家看了 object 的字节码:

public final static Lnet/println/kt14/Singleton; INSTANCE

它实际上生成了一个静态实例 INSTANCE,所以我们在 Java 当中访问一个 Kotlint object 也就很简单了:

Singleton.INSTANCE.printHello();

##3. 默认参数的方法

Kotlin 的方法可以有默认参数,这样可以省掉很多方法的重载(我们把重写继承自父类的方法叫做覆写 override,名字相同参数不同的方法叫做重载 overload),可 Java 是没有这个特性的。Kotlin 的默认参数通常在 Java 当中是被忽略掉的,例如我们定义这样一个 Kotlin 类:

class Overloads{

    fun overloaded(a: Int, b: Int = 0, c: Int = 1){
        ...
    }

}

在 Java 中访问它的带有默认参数的方法时,必须传入完整的实参列表。

new Overloads().overloaded(0, 0, 1);

不过,现实也不是如此的残酷,如果我们稍作修改,事情就会好起来:

class Overloads{

    @JvmOverloads
    fun overloaded(a: Int, b: Int = 0, c: Int = 1){
        ...
    }

}

这样的话,在 Java 看来 overloaded 方法就多了两个小兄弟,分别是:

...
fun overloaded(a: Int, b: Int = 0)

fun overloaded(a: Int)
...

这样的话,我们在 Java 中也可以愉快地使用默认参数带来的便利了。

##4. 包方法

Java 没有包方法,如果有的话,倒也没那么多事儿了。 Kotlin 的包方法会被默认编译到一个名为:包名+KT 的类当中,比如:

Package.kt

package net.println.kt14

fun printHello(){
    println("Hello")
}

编译完之后的字节码反编译成 Java 之后是:

package net.println.kt14;

import kotlin.Metadata;

@Metadata(
   mv = {1, 1, 1},
   bv = {1, 0, 0},
   k = 2,
   d1 = {"\u0000\b\n\u0000\n\u0002\u0010\u0002\n\u0000\u001a\u0006\u0010\u0000\u001a\u00020\u0001¨\u0006\u0002"},
   d2 = {"printHello", "", "production sources for module Kt14_main"}
)
public final class PackageKt {
   public static final void printHello() {
      String var0 = "Hello";
      System.out.println(var0);
   }
}

所以如果我们想要调用这样的方法,就可以像普通静态方法一样引用就可以了:

public class CallPackageMethod {
    public static void main(String... args) {
        PackageKt.printHello();
    }
}

5. 扩展方法

扩展方法实际上更像是一种语法糖,本质上其实是第一个参数为扩展类的实例而已。比如我们为 String 写了一个扩展方法:

ExtensionMethod.kt

fun String.notEmpty(): Boolean{
    return this != "" //注意 Kotlin 的字符串比较与 Java 的差别
}

我们在 Java 当中怎么访问这个方法呢?

public class CallExtenstionMethod {
    public static void main(String... args) {
        System.out.println(ExtensionMethodKt.notEmpty("Hello"));
    }
}

6. Internal 的类和成员

我在之前的视频当中一直没有提到过的一个点:Kotlin 其实对访问权限做了调整。除了把默认访问权限改为 public 之外,还提供了一个模块内可见的 internal。一旦某个成员或者类被标记为 internal,那么模块之外的类是无法访问到这个成员或者类的,而对于模块内的其他成员或者类来说,它们则相当于 public —— 这个特性对于 sdk 开发者来说是相当友好的,举个例子,Android 源码中经常会有被标注为 @Hide 的成员,这些成员在 android.jar 当中不会有,不过他们却存在于 framework 的源码中,如果 Java 有 internal 这样的访问控制能力,那么 Android SDK 的开发者大可不必费尽周折搞出个 @Hide 注解并在打包 android.jar 的时候去掉这些成员。

介绍完 internal 之后,我们来看看模块内的 Java 代码如何访问 Kotlin 中的 internal 成员或者类,首先我们定义一个类用 internal 修饰。

internal class InternalClass {
    fun printHello(){
        println("Hello")
    }
}

在模块内写一些 Java 方法来访问它:

public class CallInternalClass {
    public static void main(String... args) {
        InternalClass internalClass = new InternalClass();
        internalClass.printHello();
    }
}

没有问题的,那模块外呢?我们把同样的代码放到了另外一个模块当中,这个模块依赖了我们之前的模块,发现这段 Java 代码仍然可以用,当然,Kotlin 只有在模块内才可以访问到 InternalClass 这个类。那这是不是说 Kotlin 在兼容 Java 方面有缺陷呢?结论不要下的太早,你就算能访问到这个类,又有什么用呢?

如果我们稍微改一下 InternalClass:

internal class InternalClass {
    internal fun printHello(){
        println("Hello")
    }
}

结果我们发现,Java 代码无论在模块内还是模块外,都无法访问到 printHello 方法(尽管编译器提示有个叫 printHello$production_sources_for_module_Kt14_Kt14_main ,字节码当中也确实有这个方法,不过我们还是无法编译通过。

结论就是,internal 修饰符与 Java 的兼容性方法比较差,如果你的项目中有 Java 代码依赖 Kotlin 代码,那么被依赖的部分需要慎用 internal。

7. 小结

总体来讲,Java 依赖 Kotlin 的代码并不是件难事,绝大多数的场景我们并不会觉得二者混用在一起会有什么不舒服,相反,时间久了,你甚至会觉察不到二者的共存。这一期视频就到这里,下一期,我们讲如何在 Kotlin 当中调用 Java,谢谢大家。

相关文章
|
4天前
|
SQL 人工智能 安全
【灵码助力安全1】——利用通义灵码辅助快速代码审计的最佳实践
本文介绍了作者在数据安全比赛中遇到的一个开源框架的代码审计过程。作者使用了多种工具,特别是“通义灵码”,帮助发现了多个高危漏洞,包括路径遍历、文件上传、目录删除、SQL注入和XSS漏洞。文章详细描述了如何利用这些工具进行漏洞定位和验证,并分享了使用“通义灵码”的心得和体验。最后,作者总结了AI在代码审计中的优势和不足,并展望了未来的发展方向。
|
13天前
|
存储 弹性计算 人工智能
阿里云Alex Chen:普惠计算服务,助力企业创新
本文整理自阿里云弹性计算产品线、存储产品线产品负责人陈起鲲(Alex Chen)在2024云栖大会「弹性计算专场-普惠计算服务,助力企业创新」中的分享。在演讲中,他分享了阿里云弹性计算,如何帮助千行百业的客户在多样化的业务环境和不同的计算能力需求下,实现了成本降低和效率提升的实际案例。同时,基于全面升级的CIPU2.0技术,弹性计算全线产品的性能、稳定性等关键指标得到了全面升级。此外,他还宣布了弹性计算包括:通用计算、加速计算和容器计算的全新产品家族,旨在加速AI与云计算的融合,推动客户的业务创新。
|
11天前
|
编解码 Java 程序员
写代码还有专业的编程显示器?
写代码已经十个年头了, 一直都是习惯直接用一台Mac电脑写代码 偶尔接一个显示器, 但是可能因为公司配的显示器不怎么样, 还要接转接头 搞得桌面杂乱无章,分辨率也低,感觉屏幕还是Mac自带的看着舒服
|
18天前
|
存储 人工智能 缓存
AI助理直击要害,从繁复中提炼精华——使用CDN加速访问OSS存储的图片
本案例介绍如何利用AI助理快速实现OSS存储的图片接入CDN,以加速图片访问。通过AI助理提炼关键操作步骤,避免在复杂文档中寻找解决方案。主要步骤包括开通CDN、添加加速域名、配置CNAME等。实测显示,接入CDN后图片加载时间显著缩短,验证了加速效果。此方法大幅提高了操作效率,降低了学习成本。
2896 10
|
13天前
|
存储 缓存 关系型数据库
MySQL事务日志-Redo Log工作原理分析
事务的隔离性和原子性分别通过锁和事务日志实现,而持久性则依赖于事务日志中的`Redo Log`。在MySQL中,`Redo Log`确保已提交事务的数据能持久保存,即使系统崩溃也能通过重做日志恢复数据。其工作原理是记录数据在内存中的更改,待事务提交时写入磁盘。此外,`Redo Log`采用简单的物理日志格式和高效的顺序IO,确保快速提交。通过不同的落盘策略,可在性能和安全性之间做出权衡。
1578 12
|
5天前
|
人工智能 关系型数据库 Serverless
1024,致开发者们——希望和你一起用技术人独有的方式,庆祝你的主场
阿里云开发者社区推出“1024·云上见”程序员节专题活动,包括云上实操、开发者测评和征文三个分会场,提供14个实操活动、3个解决方案、3 个产品方案的测评及征文比赛,旨在帮助开发者提升技能、分享经验,共筑技术梦想。
730 99
|
1月前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
18天前
|
人工智能 Serverless API
AI助理精准匹配,为您推荐方案——如何快速在网站上增加一个AI助手
通过向AI助理提问的方式,生成一个技术方案:在网站上增加一个AI助手,提供7*24的全天候服务,即时回答用户的问题和解决他们可能遇到的问题,无需等待人工客服上班,显著提升用户体验。
1478 9
|
6天前
|
SQL 存储 人工智能
【产品升级】Dataphin V4.3重大升级:AI“弄潮儿”,数据资产智能化
DataAgent如何助理业务和研发成为业务参谋?如何快速低成本的创建行业数据分类标准?如何管控数据源表的访问权限?如何满足企业安全审计需求?
357 0
【产品升级】Dataphin V4.3重大升级:AI“弄潮儿”,数据资产智能化
|
3天前
|
人工智能 自然语言处理 程序员
提交通义灵码创新实践文章,重磅好礼只等你来!
通义灵码创新实践征集赛正式开启,发布征文有机会获得重磅好礼+流量福利,快来参加吧!
203 7