网站首页 > 基础教程 正文
概述
Kotlin/JVM 可以看做是对改进Java的一种积极的尝试,其试图改进Java编程语言中已知的被广泛讨论的缺点与不足。因为我多年前从事过C#的开发,初次看到Kotlin,感觉好多特性C#好多年前就有了。足见Java是多么的传统,不愿意过多的引入语法糖。
关于Kotlin与Java的爱恨情仇此处按下不表,等有机会单独写一篇相关文章。本文仅想从Java惯用者的角度给出Kotlin与Java的一些不同点,这往往对Java惯用者迅速掌握Kotlin是至关重要的。因为Kotlin是对Java的一种改进,其与Java是100%互操作的,所以Java程序员经过短暂的熟悉后往往可以快速入门,进而熟练掌握Kotlin。
人类学习新事物的一个很重要的方法就是类比,如果新事物与旧事物相似性非常大,那么类比学习的效果会更加明显,从Java到Kotlin非常符合这个特征,以你熟悉的Java来类比不熟悉的Kotlin将会事半功倍,让我们开始吧。
本文基于Kotlin 1.4.0 版本
语法差异
Kotlin与Java在语法上存在一些差异,不算特别巨大,下面列出了我认为最需要适应的几条:
Kotlin中的方法和属性可以不包含在类中
我们知道,Java中的一切是以class为基础的,都要在class中,但是Kotlin却不是。下面的代码中,方法、属性以及类共存于同一级,同一个文件中都是允许的。
//属性
var name: String = "ShuSheng007"
//方法
fun sum(x: Int, y: Int): Int {
return x + y
}
//类
class Student{}
12345678910
Kotlin中语句不需要以;结束
println("hello world")
1
Kotlin中数据类型是后置的
//变量声明时类型后置
var name: String = "ShuSheng007"
//方法参数及返回值类型后置
fun sum(x: Int, y: Int): Int {
return x + y
}
1234567
Kotlin方法使用fun关键字定义
fun sum(x: Int, y: Int): Int {
return x + y
}
123
Kotlin的类和方法默认是public final的
类默认不可以被继承,基类中的方法默认不可以被重写,如果想要被继承或者重写需要用open关键字标记
//Human类可以被继承
open class Human{
//eat方法可以被overwrite
open fun eat(){
}
}
123456
Kotlin中类继承和接口实现使用:标记
//类继承
class Man : Human(){
override fun eat() {
super.eat()
}
}
//实现接口
interface action{
fun sleep()
}
class Woman :action{
override fun sleep() {
//...
}
}
12345678910111213141516
Kotlin中使用var,val声明变量及属性,而且可以进行类型推断
在Java中,我们声明一个变量必须先指定其类型,例如
String name= "shusheng007";
1
但是在Kotlin中,编译器可以根据赋值自动推断其类型为String
var name = "shusheng007"
1
Kotlin存在非空与可空类型
这个也是其宣传的一大亮点,尝试去解决价值几十亿美金的问题:NullPointerException
在Kotlin中每个对象默认都是非null的,除非你显式的将其声明为可null
//非空类型
var name1:String="shusheng"
//可空类型
var name2:String?=null
1234
Kotlin中package可以与文件路径不一致
什么意思呢?假设我们有个类文件在 src/…/top/ss007/learn 文件路径中
那么对于Java 此类的包名必须为
package top.ss007.learn
1
而对于Kotlin来说就没有这个限制,可以随便叫,例如叫:就是这么任性
package just.so.wayward
1
确实挺任性的,所以这点建议还是遵守Java的规范更好。
Kotlin中没有受检异常(Checked Exception)
在Java中有很多受检查异常,程序员被强制要求处理它,或者抛给下层调用者处理。但是Kotlin没有这个限制。
Kotlin强调不可变的概念
Kotlin会优先使用不可变对象,例如不可变的集合,不可变的变量。这些不可变对象生成后就只能读取,而不能修改。至于使用不可变对象有很多好处,例如其天然的线程安全性等
以上几条是个人认为的最颠覆我们认知的差异点,熟悉了上面几条后Java程序员就基本上可以看得懂kotlin的代码了
Java中不存在的特性
Kotlin中引入了很多Java中不存在的特性,下面是几条我认为比较重要的
方法类型(Function Type)
在Kotlin中方法是一等公民,意思就是方法就和类一样,类可以干的事,方法都可以干。方法竟然可以作为类型当其他方法的参数传递,当其他方法的返回值类型,当变量的声明类型…这个比较颠覆Java程序员的三观,Java中是没有对应的概念的。如果非要找一个对应物的话,那就是函数接口了。
下面是一个方法类型(两个int 数据 得到一个int,例如1+2=3)。我们完全可以将其看成是java中的一个类,所有可以使用类的地方这家伙都适用,也就是说kotlin中,方法可以当参数传递了,相当于实现了方法引用,牛逼哄哄的。
(Int, Int) -> Int
1
例如如下方法,第3个参数operation的类型就是上面的方法类型
fun calculate(x: Int, y: Int, operation: (Int, Int) -> Int): Int {
return operation(x, y)
}
//如何调用
fun sum(x: Int, y: Int): Int {
return x + y
}
//将sum方法当参数传入了calculate方法中
calculate(4, 5, ::sum)
//我们也可以使用Lambda表达式
calculate(4, 5, { a, b -> a + b })
//当Lambda表达式是方法的最后一个参数时,其可以移到()外面
calculate(4, 5) { a, b -> a + b }
1234567891011121314
那么如何在Java中实现相同的功能呢?
第一步:按照需求定义一个函数接口
下面的接口只有一个抽象方法,是一个函数接口。
@FunctionalInterface
public interface Fun2<P1, P2, R> {
R invoke(P1 p1, P2 p2);
}
1234
第二步:将其作为方法参数类型
public int javaCalculate(int x, int y, Fun2<Integer, Integer, Integer> operation) {
return operation.invoke(x, y);
}
123
经过上面两步就搞定了。
当在Kotlin中调用Java中的方法javaCalculate时,IDE会提示如下信息
第一条是告诉你第三个参数可以使用Kotlin中的函数类型,代码如下
javaCalculate(4, 5) { a, b -> a + b }
1
第二条是比较正统的,告知你需要一个类型为Fun2的参数,代码如下
javaCalculate(4, 5, object : Fun2<Int, Int, Int> {
override fun invoke(p1: Int, p2: Int): Int {
return p1 + p2
}
})
12345
在Java中我们使用new一个匿名类的对象,而在Kotlin中我们需要使用object关键字
属性(Property)
var name:String="ShuSheng007"
1
对应Java类里的私有字段(Field)外加getter和setter方法
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
123456789
数据类 data class
data class User(val name: String, val age: Int)
1
大体相当于Java中的JavaBean,但有细微差别,这也是Kotlin大势宣传其简洁性时经常展示的看家特性
密封类 ( Sealed Classes)
这个比较牛逼,Java中没有对应的东东,其主要配合Kotlin中的when使用。最开始见到它的时候我不是很喜欢它,因为它名叫密封类,却可以被继承,后来发现了它设计的精妙性,真的实质性的解决了开发中常常遇到的困境。
其是一种继承受限的类,例如下面的Human类,其具有如下特点
- 其是abstract类
- 其不可以被直接实例化
- 其子类必须和其处于同一个文件中
sealed class Human {
abstract fun eat(): Unit
open fun program(){
println("I use koltin")
}
}
class Man : Human() {
override fun eat() {
}
override fun equals(other: Any?): Boolean {
return this === other
}
override fun hashCode(): Int {
return System.identityHashCode(this)
}
}
class Woman :Human(){
override fun eat() {
}
override fun program() {
super.program()
}
}
123456789101112131415161718192021222324252627
那它有什么好处呢?主要就是用在when表达式中,和枚举的功能差不多,但是其可以保护状态,枚举是单例的。
扩展方法(Extension functions)与扩展属性(Extension properties)
这个特性也是kotlin非常重要的特性,有着很广泛的应用。通过扩展方法Kotlin有能力不通过继承而为某个类增加新的方法和属性
例如我们想实现一个wrapWithSymbol, 用于在字符串的前后加上<>,如何实现呢?如果是java的话就只能写一个Utils方法了,而Kotlin可以使用扩展方法来给String类增加一个新方法。
其实现方法如下,注意方法前面的那个要扩展的类型,学名叫receiver
fun String.wrapWithSymbol() :String{
return "<$this>"
}
//使用
println("my name is ${"shusheng007".wrapWithSymbol()}")
123456
输出:
my name is <shusheng007>
1
可见已经在目标字符串前后加上了<>,从调用方式来看,wrapWithSymbol方法就像是String类的方法一样。
扩展属性与扩展方法类似:
val <T> List<T>.lastIndex: Int
get() = size - 1
//调用
println(listOf(1,2,3,4).lastIndex)
12345
操作符重载
这个其实还是比较复杂的,也容易被滥用,我认为应该属于进阶知识,此处仅简单的简绍一下。
Java是不支持操作符重载的,其实有没有它也不影响大局。
那什么是操作符重载呢?想要理解这个问题,首先必须理解什么是操作符?
例如我们编程时候经常用到 a++, a--, a+b, a==b ,a in b等等,其中那些++,--,+,== ,in就是操作符。
那什么是重载呢?这些操作符本身都是有自己的含义的,但是我们通过重载方式可以改变这些操作符的含义,这个过程就叫操作符重载。
在kotlin中,可以重载的操作符集合及其对应的方法都是预先定义好的,不能随便重载。下面列出其中的一组:
操作符重载的方法既可以是实例方法,也可以是扩展方法。
我们尝试使用实例方法重载一下上表中的+操作符。有两点需要注意:
- 重载方法必须以operator 标记
- 重载方法名称和参数必须是Kotlin预先定义好的,例如此处的 plus
data class Point(val x: Int, val y: Int){
operator fun plus(p: Point): Point {
return Point(p.x + x,p.y + y)
}
}
//调用
val p1 = Point(1, 2)
val p2 = Point(3, 4)
println(p1 + p2)
12345678910
输出:
Point(x=4, y=6)
1
可见,通过重载+操作符,我们改变了其内在的含义,使得+也能用于对象了。
协程
较复杂,需要单独文章描述
总结
正所谓求同存异,类比学习,世界之事皆如此。又到了点赞收藏的时候了,点赞是对作者的支持,收藏是为自己负责。
- 上一篇: 如何用Kotlin实现MVP架构Android快速开发框架?
- 下一篇: Kotlin 类和对象
猜你喜欢
- 2024-11-19 1-0 Protobuf通信协议.proto文件编写
- 2024-11-19 阿里P8资深架构师耗时一年整理19年Java工程师成神之路
- 2024-11-19 Java修炼终极指南:83. 声明一个Java记录
- 2024-11-19 Effective Java 3rd(Java高效编程)技术要点
- 2024-11-19 Spring源码系列(二)
- 2024-11-19 Java 14 发布了,快来了解下吧
- 2024-11-19 Spring技巧:深入研究Java 14和SpringBoot
- 2024-11-19 IntelliJ IDEA 2023.2 最新变化
- 2024-11-19 数据结构与算法 #18 下跳棋,极富想象力的同向双指针模拟
- 2024-11-19 Spring Bean生命周期你除了会背八股文面试,真的会用了吗?
- 最近发表
- 标签列表
-
- jsp (69)
- gitpush (78)
- gitreset (66)
- python字典 (67)
- dockercp (63)
- gitclone命令 (63)
- dockersave (62)
- linux命令大全 (65)
- pythonif (86)
- location.href (69)
- dockerexec (65)
- tail-f (79)
- queryselectorall (63)
- location.search (79)
- bootstrap教程 (74)
- deletesql (62)
- linuxgzip (68)
- 字符串连接 (73)
- html标签 (69)
- c++初始化列表 (64)
- mysqlinnodbmyisam区别 (63)
- arraylistadd (66)
- mysqldatesub函数 (63)
- window10java环境变量设置 (66)
- c++虚函数和纯虚函数的区别 (66)