一、Kotlin出生背景
2017年,甲骨文对谷歌所谓的安卓侵权使用Java提起诉讼,要求后者赔偿高达90亿美元。于是谷歌在后面的I/O大会上宣布了新决定:Kotlin语言正式成为安卓开发的一级编程语言。资料显示,Kotlin由JetBrains公司开发,于2010年首次推出,次年开源。它与Java 100%互通,并具备诸多Java尚不支持的新特性。Kotlin作为一门高度与Java兼容、并且简洁开发语言,对于我这种后端开发者也有很大的吸引力。
二、Kotlin具有哪些优势
- 因为Kotlin是基于JVM开发的,所以它同时具备了Android 开发、Web浏览器开发、原生Native开发的能力。在Web开发方面,Kotlin可以结合Spring框架使用(这为我们当前业务项目使用Kotlin语言开发提供了条件),也可以编译生成JavaScript模块,方便在一些JavaScript的虚拟机上编译运行。
- Kotlin能够和Java达到100%互通,也就是说,使用Kotlin依旧可以调用 Java已有的代码或库,也可以同时使用Java和Kotlin来混合编写代码。同时,为了方便项目的过渡,JetBrains提供的开发工具可以很简单的实现Java代码到Kotlin的转换。
- 在使用Java编程的过程中,大家聊得最多的话题莫过于如何避免空指针异常(NullPointerException)。针对空指针问题,Kotlin有专门的语法来避免空指针问题。
- Kotlin语法简洁直观,看上去非常像Scala,但更简单易学。同时,Kotlin使用了大量的语法糖,使得代码更加简洁。Kotlin并不遵循特定的编程规范,它借鉴了函数式风格和面向对象风格的诸多优点。
- 使用Kotlin编程,开发人员不必为每个变量明确指定类型,编译器可以在编译的时候推导出某个参数的数据类型,从而使得代码更为简洁。
- 作为JetBrains旗下的产品,JetBrains旗下众多的IDE可以为Kotlin开发提供无缝支持,并相互协作,协同发展。
三、Kotlin与Java的异同
- main方法的差异:猜测很多java程序员在入门的时候写的第一个程序是打印“Hello World”,其写法如下:
如上图,java的main方法只能写在Java类之中。但是换成Kotlin,却可以这样写:
可以发现Kotlin的main方法可以写在类里面,也可以写在类外面。当写在类里面的时候,需要嵌套一层companion object { 静态代码块 },companion object相当于Java中的static。
- Kotlin参数的格式。通过上面的main方法可以看出来,Kotlin的入参的写法是field: Field,而传统的Java写法是Field field。相对于入参,出参的变化更大一些,如下:
fun getSumVal(a: Int, b: Int): Int {
return a + b
}
fun printSumVal1(a: Int, b: Int): Unit {
println(a + b)
}
fun printSumVal2(a: Int, b: Int) {
println(a + b)
}
出参的位置是在入参的右边,格式为:(入参):出参类型。如果没有出参该怎么表示呢。Kotlin中摒除了void关键字,取而代之的是Unit,并且Unit可以省略。
- 新增不可变关键字val和可变关键字var。
fun testVal() {
val a: Int = 1 val b = 2 println("a = $a, b = $b")
var c: Int = 6 c = 8 println("c = $c")
}
如果一个变量被val修饰,那么该变量再被初始化后不能再被修改。如果希望被修改,那么将val关键字改为var即可。
上面的代码中使用到了 Kotlin的字符串模板的功能:字符串字面值可以包含模板表达式 ,即一些小段代码,会求值并把结果合并到字符串中。 模板表达式以美元符($
)开头,由一个简单的名字构成。
val s = "abc"
println("$s.length is ${s.length}") // 输出“abc.length is 3”
- 空值与null检测。
fun testNull() {
var a: Int = 1 var b: Int? = null var c: Int? = getNextVal()
a.toLong()
b?.toLong()
c?.toLong()
}
fun getNextVal(): Int? {
return null
}
a可以确定是非空的,可以直接转成Long类型。但是b和c都是null,编译器可以检测到这两个变量有可能是非空的,于是这两个参数类型后面必须要加问号(?)。当将b和c转为Long类型时,需要也需要在变量名后面加个?,这样便可以避免空指针异常。
- 数组、集合、Map等使用更方便。
对于java,如果要实例化一个数组,可以这么写:
String[] stringArray = new String[]{"red", "white", "blue"};
而Kotlin的写法是:
val array = arrayOf("red", "white", "blue")
可以看出来Kotlin的写法更简洁。
Kotlin的集合和Map的写法更简单。如下:
val list = listOf("red", "white", "blue")
val map = mapOf("color1" to "red", "color2" to "white", "color3" to "blue")
同样遍历集合和Map的方式也很方便。
遍历list:
for (item in list) {
println(item)
}
遍历Map:
for (item in map) {
println(item.key + item.value)
}
- Kotlin的when语句取代Java的Switch语句,如下:
val color = "blue"when (color) {
"red" -> print("color == red")
"white" -> print("color == white")
else -> {
print("color is neither red nor white")
}
}
- Kotlin不支持三元表达式,不过支持If not null and else 缩写,效果和三元表达式差不多,如下:
val array = arrayOf("red", "white", null)
val e = array[2]
println(e?.length ?: 0)
打印出已知数组中第三个元素的长度,比三元表达式还简洁。
- Kotlin类的构造器和实例化
Kotlin 中使用关键字 class 声明类,如:class Invoice { /*……*/ }。类声明由类名、类头(指定其类型参数、主构造函数等)以及由花括号包围的类体构成。类头与类体都是可选的; 如果一个类没有类体,可以省略花括号,如:class Empty。
在 Kotlin 中的一个类可以有一个主构造函数以及一个或多个次构造函数。主构造函数是类头的一部分:它跟在类名(与可选的类型参数)后。如果主构造函数没有任何注解或者可见性修饰符,可以省略这个 constructor 关键字,如下:class Person(firstName: String) { /*……*/ }。主构造函数不能包含任何的代码。初始化的代码可以放到以 init 关键字作为前缀的初始化块(initializer blocks)中。
类也可以声明前缀有 constructor的次构造函数。如果类有一个主构造函数,每个次构造函数需要委托给主构造函数, 可以直接委托或者通过别的次构造函数间接委托。委托到同一个类的另一个构造函数用 this 关键字即可:
class Person(val name: String) {
var children: MutableList<Person> = mutableListOf()
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
如果一个非抽象类没有声明任何(主或次)构造函数,它会有一个生成的不带参数的主构造函数。构造函数的可见性是 public。如果你不希望你的类有一个公有构造函数,你需要声明一个带有非默认可见性的空的主构造函数:
class DontCreateMe private constructor () { /*……*/ }
注意 Kotlin 并没有 new 关键字。要创建一个类的实例,我们就像普通函数一样调用构造函数:
val invoice = Invoice()
val customer = Customer("Joe Smith")
- Kotlin 与 Java之间的 互操作性。
可以在 Kotlin 中自然地调用现存的 Java 代码,并且在 Java 代码中也可以很顺利地调用 Kotlin 代码。比如Java中有一些原生类型:byte, short, int, long, char, float, double, boolean。Kotlin 特殊处理一部分 Java 类型。这样的类型不是“按原样”从 Java 加载,而是 映射 到相应的 Kotlin 类型。 映射只发生在编译期间,运行时表示保持不变。 Java 的原生类型映射到相应的 Kotlin 类型(请记住平台类型):
Java 类型 | Kotlin 类型 |
byte |
kotlin.Byte |
short |
kotlin.Short |
int |
kotlin.Int |
long |
kotlin.Long |
char |
kotlin.Char |
float |
kotlin.Float |
double |
kotlin.Double |
boolean |
kotlin.Boolean |
一些非原生的内置类型也会作映射:
Java 类型 | Kotlin 类型 |
java.lang.Object |
kotlin.Any! |
java.lang.Cloneable |
kotlin.Cloneable! |
java.lang.Comparable |
kotlin.Comparable! |
java.lang.Enum |
kotlin.Enum! |
java.lang.Annotation |
kotlin.Annotation! |
java.lang.CharSequence |
kotlin.CharSequence! |
java.lang.String |
kotlin.String! |
java.lang.Number |
kotlin.Number! |
java.lang.Throwable |
kotlin.Throwable! |
Java 的装箱原始类型映射到可空的 Kotlin 类型:
Java type | Kotlin type |
java.lang.Byte |
kotlin.Byte? |
java.lang.Short |
kotlin.Short? |
java.lang.Integer |
kotlin.Int? |
java.lang.Long |
kotlin.Long? |
java.lang.Character |
kotlin.Char? |
java.lang.Float |
kotlin.Float? |
java.lang.Double |
kotlin.Double? |
java.lang.Boolean |
kotlin.Boolean? |
请注意,用作类型参数的装箱原始类型映射到平台类型: 例如,List<java.lang.Integer>
在 Kotlin 中会成为 List<Int!>
。
集合类型在 Kotlin 中可以是只读的或可变的,因此 Java 集合类型作如下映射: (下表中的所有 Kotlin 类型都驻留在 kotlin.collections
包中):
Java 类型 | Kotlin 只读类型 | Kotlin 可变类型 | 加载的平台类型 |
Iterator<T> |
Iterator<T> |
MutableIterator<T> |
(Mutable)Iterator<T>! |
Iterable<T> |
Iterable<T> |
MutableIterable<T> |
(Mutable)Iterable<T>! |
Collection<T> |
Collection<T> |
MutableCollection<T> |
(Mutable)Collection<T>! |
Set<T> |
Set<T> |
MutableSet<T> |
(Mutable)Set<T>! |
List<T> |
List<T> |
MutableList<T> |
(Mutable)List<T>! |
ListIterator<T> |
ListIterator<T> |
MutableListIterator<T> |
(Mutable)ListIterator<T>! |
Map<K, V> |
Map<K, V> |
MutableMap<K, V> |
(Mutable)Map<K, V>! |
Map.Entry<K, V> |
Map.Entry<K, V> |
MutableMap.MutableEntry<K,V> |
(Mutable)Map.(Mutable)Entry<K, V>! |
Java 的数组按下文所述映射:
Java 类型 | Kotlin 类型 |
int[] |
kotlin.IntArray! |
String[] |
kotlin.Array<(out) String>! |
注意:这些 Java 类型的静态成员不能在相应 Kotlin 类型的伴生对象中直接访问。要调用它们,请使用 Java 类型的完整限定名,例如 java.lang.Integer.toHexString(foo)
。
- Kotlin中的类型检测与类型转换:“is”与“as”
我们可以在运行时通过使用 is
操作符或其否定形式 !is
来检测对象是否符合给定类型。在许多情况下,不需要在 Kotlin 中使用显式转换操作符,因为编译器跟踪不可变值的 is
-检测以及显式转换,并在需要时自动插入(安全的)转换:
fun demo(x: Any) {
if (x is String) {
print(x.length) // x 自动转换为字符串
}
}
“is”是安全的转换操作符,那么“as”就是非安全的转换操作符。通常,如果转换是不可能的,转换操作符会抛出一个异常。因此,我们称之为不安全的。 Kotlin 中的不安全转换由中缀操作符 as完成:
val x: String = y as String
请注意,null 不能转换为 String
因该类型不是可空的, 即如果 y
为空,上面的代码会抛出一个异常。 为了让这样的代码用于可空值,请在类型转换的右侧使用可空类型:
val x: String? = y as String?
为了避免抛出异常,可以使用安全转换操作符 as?,它可以在失败时返回 null:
val x: String? = y as? String
请注意,尽管事实上 as? 的右边是一个非空类型的 String
,但是其转换的结果是可空的。
- 相等性差异
在java中,如果我们想比较两个对象的结构是否相同,会采用equal方法,如果想知道两个对象是否是同一个对象,那就需要用==来比较两个对象的引用。
这一点Kotlin则不大相同。Kotlin会使用==来比较两个对象的结构是否相同,比如像 a == b
这样的表达式会翻译成:a?.equals(b) ?: (b === null),也就是说如果 a
不是 null
则调用 equals(Any?)
函数,否则(即 a
是 null
)检测 b 是否与 null
引用相等。Kotlin中引用相等由 ===
(以及其否定形式 !==
)操作判断。a === b
当且仅当 a
与 b
指向同一个对象时求值为 true。对于运行时表示为原生类型的值 (例如 Int
),===
相等检测等价于 ==
检测。
四、Kotlin异步编程框架协程
- 什么是协程,协程的开发人员 Roman Elizarov 是这样描述协程的:协程就像非常轻量级的线程。线程是由系统调度的,线程切换或线程阻塞的开销都比较大。而协程依赖于线程,但是协程挂起时不需要阻塞线程,几乎是无代价的,协程是由开发者控制的。所以协程也像用户态的线程,非常轻量级,一个线程中可以创建任意个协程。从上面的描述可以看出来,其最大优点是省去了传统 Thread 多线程并发机制中切换线程时带来的线程上下文切换、线程状态切换、Thread 初始化上的性能损耗,能大幅度唐提高并发性能。缺点是本质是个单线程,不能利用到单个CPU的多个核。
- 线程和协程的对比:
线程拥有独立的栈、局部变量,基于进程的共享内存,因此数据共享比较容易,但是多线程时需要加锁来进行访问控制,不加锁就容易导致数据错误,但加锁过多又容易出现死锁。线程之间的调度由内核控制(时间片竞争机制),程序员无法介入控制(即便我们拥有sleep、yield这样的API,这些API只是看起来像,但本质还是交给内核去控制,我们最多就是加上几个条件控制罢了
),线程之间的切换需要深入到内核级别,因此线程的切换代价比较大,表现在:
* 线程对象的创建和初始化
* 线程上下文切换
* 线程状态的切换由系统内核完成
* 对变量的操作需要加锁
协程是跑在线程上的优化产物,被称为轻量级 Thread,拥有自己的栈内存和局部变量,共享成员变量。传统 Thread 执行的核心是一个while(true) 的函数,本质就是一个耗时函数,Coroutine 可以用来直接标记方法,由程序员自己实现切换,调度,不再采用传统的时间段竞争机制。在一个线程上可以同时跑多个协程,同一时间只有一个协程被执行,在单线程上模拟多线程并发,协程何时运行,何时暂停,都是有程序员自己决定的,使用: yield/resume
API,优势如下:
- 因为在同一个线程里,协程之间的切换不涉及线程上下文的切换和线程状态的改变,不存在资源、数据并发,所以不用加锁,只需要判断状态就OK,所以执行效率比多线程高很多
- 协程是非阻塞式的(也有阻塞API),一个协程在进入阻塞后不会阻塞当前线程,当前线程会去执行其他协程任务
程序员能够控制协程的切换,是通过yield
API 让协程在空闲时(比如等待io,网络数据未到达)放弃执行权,然后在合适的时机再通过resume
API 唤醒协程继续运行。协程一旦开始运行就不会结束,直到遇到yield
交出执行权。Yield
、resume
这一对 API 可以非常便捷的实现异步
,这可是目前所有高级语法孜孜不倦追求的。
- 协程的三种启动方式
第一种启动方式:runBlocking:T
runBlocking 方法用于启动一个协程任务,通常只用于启动最外层的协程,例如线程环境切换到协程环境。runBlocking启动的协程任务会阻断当前线程,直到该协程执行结束。如:
fun main() = runBlocking<Unit> { // 开始执行主协程
GlobalScope.launch { // 在后台启动一个新的协程并继续
delay(1000L)
println("World!")
}
println("Hello,") // 主协程在这里会立即执行
delay(2000L) // 延迟 2 秒来保证 JVM 存活
}
第二种启动方式:launch:Job
我们最常用的用于启动协程的方式,它最终返回一个Job类型的对象,这个Job类型的对象实际上是一个接口,它包涵了许多我们常用的方法。例如join()启动一个协程、cancel() 取消一个协程。该方式启动的协程任务是不会阻塞线程的。
val job = GlobalScope.launch { // 启动一个新协程并保持对这个作业的引用
delay(1000L)
println("World!")
}
println("Hello,")
job.join() // 等待直到子协程执行结束
第三种启动方式:async/await:Deferred
1.async和await是两个函数,这两个函数在我们使用过程中一般都是成对出现的。
2.async用于启动一个异步的协程任务,await用于去得到协程任务结束时返回的结果,结果是通过一个Deferred对象返回的。
以上就是kotlin中协程的简单介绍,当然还有更多的特性,等待我们去深挖。
五、Kotlin结合Springboot项目实践
目前Springboot在线生成项目的功能已经支持了Kotlin语言版本,如下:
我们可以快速生成整合springboot+kotlin的项目。查看pom文件,多了2个Kotlin相关依赖:
启动类如下:
和普通的springboot+java项目一样傻瓜化,然后我们就可以开发具体的业务功能了。
相关推荐
使用kotlin语言编写android程序时,使用greendao3.0数据库简介
kotlin使用指南中文版,帮助你快速了解kotlin语言,快速构建属于自己的基于kotlin开发的Android APP。
实现在Kotlin中更方便使用canvas
Kotlin中使用BottomNavigationView实现底部导航+小红点消息提示
“实战”部分包括Kotlin与Java互操作、使用Kotlin集成Spring Boot开发WEB服务端、使用Kotlin集成Gradle开发、使用Kotlin和Anko的Android开发、使用Kotlin DSL、Kotlin文件IO操作与多线程、使用Kotlin Native。...
协程、第10章 Kotlin与Java互操作、第11章 使用Kotlin集成SpringBoot开发Web服务端、第12章 使用Kotlin集成Gradle开发、第13章 使用 Kotlin 和 Anko 的Android 开发、第14章 使用 Kotlin DSL、第15章 Kotlin 文件IO...
“实战”部分包括Kotlin与Java互操作、使用Kotlin集成Spring Boot开发WEB服务端、使用Kotlin集成Gradle开发、使用Kotlin和Anko的Android开发、使用Kotlin DSL、Kotlin文件IO操作与多线程、使用Kotlin Native。...
kotlin学习kotlin学习
Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言,被称之为 Android 世界的Swift,由 JetBrains 设计开发并开源。 Kotlin 可以编译成Java字节码,也可以编译成 JavaScript,方便在没有 JVM 的设备上运行。 ...
Android Kotlin简单使用Demo,使用了viewpager+tablayout+fragment,XUtils网络请求等kotlin的简单函数
kotlin官方中文pdf文档kotlin官方中文pdf文档kotlin官方中文pdf文档kotlin官方中文pdf文档kotlin官方中文pdf文档kotlin官方中文pdf文档kotlin官方中文pdf文档kotlin官方中文pdf文档kotlin官方中文pdf文档kotlin官方...
《Kotlin从入门到进阶实战》从Kotlin 语言的基础语法讲起,逐步深入到Kotlin进阶实战,并在最后配合项目实战案例,重点介绍了使用Kotlin+Spring Boot进行服务端开发和使用Kotlin进行Android应用程序开发的内容,让...
项目中设计到使用kotlin语言,实现了属性动画模式,Activity之间的跳转,布局控件的获取,Fragment碎片的填充,Viewpager填充碎片左右滚动的实现
Fundamental Kotlin (Kotlin基础 源码),源码非常值得参考
Kotlin 四则运算 (加、减、乘、除)
随着google宣布kotlin作为官方开发语言,在Android中使用kotlin的趋势也越来越明显,最近被kotlin的文章轰炸了,所以决定上手试一下,试过之后,感觉靠它灵简直有魔性。特别是一句话写出一个复杂的循环的时候,简直...
Android 使用Kotlin来实现水波纹的自定义View,详情请点击:https://lijuan.blog.csdn.net/article/details/118995489
安卓 Kotlin Coroutine协程 使用方式代码举例: 包含GlobalScope 、CoroutineScope 详细使用代码举例,相关介绍文章,可参考: https://xiaxl.blog.csdn.net/article/details/123383727
kotlin实现的进度条