入门 Kotlin 和 Java 混合开发

一、前沿

如果你学习过其他的编程语言,你就会发现 Java 的语法很是哆嗦,可是我们为什么没有放弃 Java 这门编程语言呢?因为 JVM 是一个非常好的平台,而且 Java 程序员目前在中国所占的比重实在是太高了。这是历史包袱导致的。暂且不说 Python,语法绝对比 Java 简化的不是一个级别,就连 C# 也比 Java 语法优美很多。

我们知道,Java 程序运行过程是这样的:

编写源代码(.java)-> 编译源文件(.class)-> JVM 虚拟机将 class 文件加载进内存,执行

既然如此,如果有一门语言同样能编译成 class 文件,再由 JVM 去加载他,并且这门语言比 Java 语法更加优美简洁,你乐不乐意去使用呢?那么这门优秀的语言就是 Kotlin。Kotlin 他的运程过程是这样的:

编写源代码(.kt)-> 编译源文件(.class)-> JVM 虚拟机将 class 文件加载进内存,执行。

2017年,Google 宣布 Kotlin 成为 Android 官方开发语言,可见Kotlin 之好。

当然,随着 Kotlin 的发展,Kotlin 已经不单单是 JVM 平台上的语言,也可以将 Kotlin 代码编译为 JavaScript,未来还会有 Kotlin Native,可见 Kotlin 绝对是极具潜力。

二、学习 Kotlin 前准备

工欲善其事,必先利其器。首先一定要选择一个好的 IDE,Kotlin 是由 JetBrains 公司开发,那么我们当然要用 JetBrains 公司自家的 IDE 啦,我们就选用 IDEA!相信现在绝大多数 Java 开发者都在用的。

入门 Kotlin 和 Java 混合开发_第1张图片
enter image description here
image.gif

我们新建一个普通项目即可。

如果我们要写 Kotlin 代码,很简单,右键,新建一个 Kotlin 文件即可。

入门 Kotlin 和 Java 混合开发_第2张图片
enter image description here
image.gif

新建文件后,IDE 会提示我们需要配置,按照提示,配置一下即可。

enter image description here
image.gif

三、Kotlin 语法简介

如果你已经学习过了 Java,入门 Kotlin 会很快。

我们先来写一个Hello world!

打开我们新建好的 kt 文件,输入以下代码:

fun main(args: Array) {
    print("hello world")
}

image.gif

我们再来回顾一下 Java 代码:

class HelloWord{
public static void main(String[] args){
System.out.println("Hello world");
     }
}

image.gif

我们对比一下,Kotlin 的代码非常少。我们 Java 中,方法必须写在类中,而我们的 Kotlin 支持包级函数,静态方法不需要写在一个类中。并且 Kotlin 每一句结束不需要分号。

在 Kotlin 中,所有变量声明都需要用 val 或 var 修饰,并且支持类型推断,但你需要注意的是,Kotlin 是一个静态语言。

变量声明格式 : val/var 变量名(:类型,可不写,会自动推断)

image.gif

val i=1val i:Int =1 的效果是一样的。

var i=1val i=1 如果被翻译成 Java 分别就是:

final int i=1
int i=1

image.gif

被 val 修饰的变量编译后会被翻译成类似于 java 中的 final 关键字,这是 Kotlin 一大特点,在 Kotlin 中所有类,方法都会默认是 final 的。

小编这边建议您,初学的时候多看看 Kotlin 翻译成 Java 的字节码。 我们的 IDE 已经有相关插件了:

入门 Kotlin 和 Java 混合开发_第3张图片
enter image description here
image.gif

点击后会出现如图界面:

入门 Kotlin 和 Java 混合开发_第4张图片
enter image description here
image.gif

再点击红色框中的按钮,就会显示我们 Kotlin 被翻译成 Java 的代码了。

优秀的表达式

我们观察以下代码:

入门 Kotlin 和 Java 混合开发_第5张图片
enter image description here
image.gif

他和我们 Java 中的 if else 有哪些不同?

我们表达式也可以有返回值了,注意千万不要写 return xxx,因为 return 就跳出方法了。在 Kotlin 中,条件表达式的最后一句就可以作为返回值,这一个小技能可以说非常实用,类似的还有 try、catch 也有类似的功能。

加强版 switch

本人作为一名资深 Java 开发者,确很少用 switch,一来 switch 必须对相同类型进行操作,功能单一,可能还没有 if 强大,而且经常使用会增加代码耦合度,一般会使用多态结合相关的设计模式来代替。

我们来看看 Kotlin 中加强版的 switch 表达式 when:

入门 Kotlin 和 Java 混合开发_第6张图片
enter image description here
image.gif

当满足第一个 case 分支,就会结束这个表达式。同理,他也可能作为一个表达式,每一个分支最后一句可以返回一个值,再给一个变量去接收,是不是比 switch 强大了一万倍呢!

模板字符串

这个功能可是非常实用,我们在 java 中可能常常会有拼接字符串的痛苦,没事,Kotlin 可以解决你的这个痛苦,我们看看这个代码:

入门 Kotlin 和 Java 混合开发_第7张图片
enter image description here
image.gif

我们在字符串中,使用 ${},在花括号中,可以写 Java 代码,这样就达到了模板字符串的效果。当然你也可以这样这样做:

入门 Kotlin 和 Java 混合开发_第8张图片
enter image description here
image.gif

使用 $ 加上变量名可以直接输出这个变量。那么我们如果想输出 $,那么就需要转义了,和 Java 一样,使用 \

包装类和基本数据类型不存在了

在 Java 中类似 int、double 等等所有基本数据类型都有其对应的包装类,在 Kotlin 中不存在了,一律使用 Int、Double、Float、String……

在编译的时候,Kotlin 会根据我们的情况智能选择基本数据类型还是包装类,不需要程序员去操心了。

data class,再见 Java bean

我们在写 JavaBean 一般过程是写上一个 class 类,里面编写若干个字段,然后给上若干个 get/set 方法,有时候还会重写 toString、equals、HashCode 方法。

这些过程其实也有些重复,如果我们使用 Kotlin 为我们提供的 data class 编写一个类:

data class Person(val username:String,val age:Int)

image.gif

这样我们就写好了一个 data class,短短一句话,我们从声明也可以看出,我们给 Person 这个类定义了 username 和 age 两个属性,我们使用 IDEA 自带插件,把他翻译成 Java 代码看看:

public final class Person {    @NotNull    private final String
username;    private final int age;

   @NotNull    public final String getUsername() {
      return this.username;    }

   public final int getAge() {
      return this.age;    }

   public Person(@NotNull String username, int age) {
      Intrinsics.checkParameterIsNotNull(username, "username");
      super();
      this.username = username;
      this.age = age;    }

   @NotNull    public final String component1() {
      return this.username;    }

   public final int component2() {
      return this.age;    }

   @NotNull    public final Person copy(@NotNull String username, int
age) {
      Intrinsics.checkParameterIsNotNull(username, "username");
      return new Person(username, age);    }

   // $FF: synthetic method    // $FF: bridge method    @NotNull   
public static Person copy$default(Person var0, String var1, int var2,
int var3, Object var4) {
      if ((var3 & 1) != 0) {
         var1 = var0.username;
      }

      if ((var3 & 2) != 0) {
         var2 = var0.age;
      }

      return var0.copy(var1, var2);    }

   public String toString() {
      return "Person(username=" + this.username + ", age=" + this.age + ")";    }

   public int hashCode() {
      return (this.username != null ? this.username.hashCode() : 0) * 31 + this.age;    }

   public boolean equals(Object var1) {
      if (this != var1) {
         if (var1 instanceof Person) {
            Person var2 = (Person)var1;
            if (Intrinsics.areEqual(this.username, var2.username) && this.age == var2.age) {
               return true;
            }
         }

         return false;
      } else {
         return true;
      }    } }

image.gif

data class 太厉害了,Kotlin 在编译的时候会自动帮我们加上 get/set 方法,还会为我们加上 tostring、euqals、hashcode 方法。

不过,你要注意的是,Kotlin 类默认是 final 的,所以我的 data class 是不能被继承的,而且他没有无参的构造方法。如果这样直接使用 MyBatis 是会报这个类找不到无参构造方法的错的。

为了解决这些问题,Kotlin 官方推荐了 no-arg 插件来解决这一问题,如果你想让 data class 编译时候去掉 final 关键字,可以使用 Kotlin 官方插件 all-open 插件来解决这一问题。

插句题外话,在 Spring5 中,Spring 专门针对 Kotlin 做过很多的优化。比如,AOP 这一底层实现,相信有一定功底的人士都知道,spring 会为这个类使用 CGLIB 技术动态生成一个子类。而我们的 Kotlin 类默认是 final 的,因此,如果我们是用 Kotlin 写的 AOP,想让我们写的 AOP 类能正常运行,就必须在 class 前面加上 open。可每次写很麻烦,我们索引官方也推出了一个 kotlin-spring 插件,可以为这些类自动去 final 关键字。

空指针异常不存在了

Kotlin 中所有类型都区分非空或可空类型,比如可空的 Int,就是 Int?,如果这个类型允许空,我们就在这个类型后面加个 ?,不加 ? 的话则这个类型不可为空!

比如我们定义一个方法某一个参数类型后面我们就指定了 ?,我们可以传递 Null,反之则不可以,或者加上 ! 强制传递。

Kotlin 的绝技实在是太多了,包括还有定义方法的时候,我可以给参数指定默认值,调用的时候也可以指定参数名的时候进行传递,在编译时候,Kotlin 会生成若干个重载的 Java 方法。当然这只是九牛一毛,Kotlin 最强大的就是高阶函数和函数式编程了,并且支持科理化。支持运算符重载,可以为类增加扩展方法,强大到令人发指。

同时,Kotlin 还支持协和编程(目前这块不太成熟),由于篇幅有限,如果大家有兴趣,我可以再做一次专题讲解。

编写单例类

单例模式是 23 种设计模式中非常常用的设计模式之一。在 Kotlin 中也有提供我们非常快捷的方式编写单例类,实现单例仅仅只需要一行代码:

object Singleton

image.gif

是的,你没有看错,只需要写2个单词 ,object 代表声明一个单例类,Singleton 为单例的类名,是不是很Easy?

我们测试一下:

入门 Kotlin 和 Java 混合开发_第9张图片
enter image description here
image.gif

这里补充一下,由于 Kotlin 支持运算符重载,在 Kotlin 中,双等号等同于调用 equals 方法,三等号才是比较内存地址。我们看到这里输出的是 true,则说明,输出的是同一个对象。我们再来看看他字节码,翻译成 Java 的代码:

入门 Kotlin 和 Java 混合开发_第10张图片
enter image description here
image.gif

可以看出,就是一个简单的单例模式。构造方法没有显示出来,其实是私有化的。其实我们还是可以通过反射去修改构造方法访问修饰符来破坏他的单例的。

扩展方法

相信如果你做过多年的 Java 开发,手头上一定有大量的 Util 类,比如 DateUtils、HtmlUtils、StringUtils 等等,其实这都是 Java 这门语言造成的,当你学会扩展方法后,从此告别 Util。

比如我们要判断一个字符串是否长度大于 0,我们无非要判断他不为 null,且长度大于 0,如果用 Java 去实现相信你一定会写一个 StringUtils,那么我们如何用 Kotlin 的扩展方法去实现呢?

我们为 String 扩展一个 isNullOrEmpty 方法,如何返回 false,则说明长度大于 0。

我们在定义方法的时候前面加上对应的类名即可变成扩展方法了。

fun String.isNullOrEmpty():Boolean{
    return !(this!=null&& this.isNotEmpty())
}

image.gif

这个代码可以写在任何地方。

我们写测试类调用:

入门 Kotlin 和 Java 混合开发_第11张图片
enter image description here
image.gif

是不是很自然。向 Util 说再见吧!其实我们的 Kotlin 在 String 类,集合类,IO 类等 JDK 很多类都提供了大量的扩展方法,比如我们学一个文本文件,现在只需要一句话:

val file = System.IO.File("d:/1.txt")
    println(file.readText())

image.gif

readText 就是 Kotlin 为我们写的扩展方法,类似的其实还有很多。因此,使用 Kotlin 一定会大大提高你的工作效率。

运算符重载

在 Kotlin 中,每一个运行符对应一个该类中的方法,如果该类中有相应的方法实现,就可以进行运算符重载。

就连调用方法其实也是一样,比如我们调用 a(b),等同于 a.invoke(b)

表达式 对应转换
a + b a.plus(b)
a - b a.minus(b)
a * b a.times(b)
a / b a.div(b)
a % b a.mod(b)
a..b a.rangeTo(b)
a[i] a.get(i)
a[i, j] a.get(i, j)
a[i_1, …, i_n] a.get(i_1, … , i_n)
a[i] = b a.set(i, b)
a[i,j] =b a.set(i, j, b)
a[i_1, … , i_n] = b a.set(i_1,… ,o_n,b)
a in b b.contains(a)
a !in b !b.contains(a)
a[i] a.get(i)
a[i, j] a.get(i, j)
a[i_1, …, i_n] a.get(i_1, … , i_n)
a[i] = b a.set(i, b)
a[i,j] =b a.set(i, j, b)
a[i_1, … , i_n] = b a.set(i_1,… ,o_n,b)

我们举个例子,假如我们要计算一个日期后的若干天,我们如果用 Java 去干,相信你一定要写不少于 10 行代码,而且会很痛苦。而在 Kotlin 中,有了运算符重载,我就有了一个很好的思路,让 data+1 就返回一天后的日期,是不是很绝?

说到做到,我们来实现:

根据上面表我们可以看出,+ 要重载的方法是 plus。可是 Date 类是 JDK 为我们提供的,我们如何去为他添加一个方法呢?没错,我们之前讲过,Kotlin 支持类的扩展方法,我们为 Date 类扩展一个 plus 方法,扩展方法和运算符重载结合起来也会被识别。

对了,重点还没有说,如何定义运算符重载呢?仅仅写对应的方法还不足够,还要在 fun 前面加上 operator 关键字。所以,我们写出如下代码:

operator fun Date.plus(nextVal:Int):Date{
    val calendar = GregorianCalendar()
    calendar.time = this
    calendar.add(Calendar.DATE, nextVal)
    return calendar.time
}

image.gif

为了方便查看日期字符串,我们再扩展一个 stringFormat 方法,由于"yyyy-MM-dd"格式太常用,我们再给他一个默认值。

fun Date.stringFormat(formatType:String = "yyyy-MM-dd"):String{
    return SimpleDateFormat(formatType).format(this)
}

image.gif

写测试方法:

入门 Kotlin 和 Java 混合开发_第12张图片
enter image description here
image.gif

绝了,不得不感叹 Kotlin 的伟大!

四、Kotlin 与 Java 混合开发

由于 Kotlin 和 Java 编译成 class 文件后完成一样,所以 Kotlin 和 Java 有着天然的兼容性,100% 兼容。在 Kotlin 中你可以随意的去使用 JVM 中定义好的类,也可以是你自己写好的 Java 类,同一个项目中你可以既有 Java 类,也可以有 Kotlin 类。

比如你定义了一个 data class Person 类,在同一个项目中,你可以新建一个 Java 类,使用 Person person = new Person(“小明”,12); 这完全没有问题的。同样,你用 Kotlin 定义好的类,在 Java 中可以同样使用。所在,从现在开始使用 Kotlin,不要怕,即使有地方你担心 Kotlin 自己不熟悉而实现不了,那你也可以在这时候使用 Java 去实现。100% 兼容,Java 中的所有类库都可以去使用。

这里附上一张 Kotlin 使用 SSM 图:

入门 Kotlin 和 Java 混合开发_第13张图片
enter image description here
image.gif

可以看出,源码是 Kotlin 的,我们同样可以使用 Spring 框架,再附上这个项目源文件。

入门 Kotlin 和 Java 混合开发_第14张图片
enter image description here
image.gif

文件前面图标右下角带 K 的都是 Kotlin 文件,可见,Kotlin 和 Java 是非常好的。

那么,像扩展方法、运算符重载、默认参数这类的我们该用 Java 如何去调用呢?

这里有很遗憾了,由于 Java 语法的限制我们不能像 Kotlin 那样去使用,但这并不代表我们不能用 Java 去调用,我们在 Java 中,只能当成一个普通方法去使用了。

五、Kotlin 与 Java 总结

个人认为 Kotlin 取代 Java 只是时间问题,现如今 Java 的更新速度已经很缓慢了,已经有越来越多的 Java 开发者去转战 Kotlin 这门语言。即使你现在的项目已经用 Java 写的很多,从今天开始你也可以使用 Kotlin,与 Java 百分之一百兼容,这是他的最大的特点。在未来,Kotlin 也必定会走出 JVM 这个平台,准确说,现在已经迈出了,未来会越来越强大。

祝读者好运,感谢大家的阅读!

你可能感兴趣的:(入门 Kotlin 和 Java 混合开发)