一、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项目一样傻瓜化,然后我们就可以开发具体的业务功能了。
相关推荐
Android Kotlin的使用及简单实例 Android Kotlin是Google官方认证的Android开发语言,自2017年Google I/O大会上宣布Kotlin成为Android的官方语言以来,Kotlin逐渐成为了Android开发者的热门选择。本文将介绍Android...
由于它的非空检查、lambda语法、直接使用id等一些属性让它很快成为众多程序员的首选语言,当然我也不例外,这里特别记录一下使用kotlin的过程和心得:使用前准备在AndroidStudio中安装kotlin插件在project的根build....
这些会议通常汇集了行业内的专家和开发者,分享他们在Android平台上的最新实践、技术突破以及Kotlin编程语言的应用心得。通过观看这些视频,你可以了解到Android生态系统和Kotlin语言的前沿知识。 **Android生态...
学习使用XML进行布局设计,以及通过Java或Kotlin动态操作UI,将有助于提高用户体验。 Android的事件监听和回调机制是另一大重点。理解触摸事件的传递和拦截,以及如何在不同组件间进行通信,例如Intent的使用,能让...
随着社会的发展和城市化进程,垃圾污染成为日益严重的环境问题之一。不正确的垃圾处理和分类不仅对环境造成负面影响,还增加了资源浪费和处理成本。因此,开发一个安卓垃圾分类系统具有重要的背景和意义。...
这个应用包含了完整的源代码、使用手册以及开发者的心得体会,对于学习Android移动软件开发的学生来说,这是一个宝贵的资源。以下是关于这个项目的详细知识点: 1. **Android应用程序开发基础**:首先,你需要了解...
在本项目中,我们主要探讨的是一个针对安卓平台的移动应用程序,它被设计用于帮助学生进行英语四级备考,包括提供源代码、使用手册以及开发者的心得体会。这个应用集成了多种功能,旨在提升用户的英语学习体验。以下...
11. **心得体会**:这部分可能包含了开发者在项目过程中遇到的问题、解决方案以及对Android开发的理解和感悟,是宝贵的经验分享,有助于其他学习者避免类似陷阱。 总的来说,这个安卓期末课程设计项目涵盖了移动...
10. **使用手册和心得体会**:提供的使用手册和开发者的心得体会对用户和学习者来说是一份宝贵的资源,它们解释了App的功能、操作流程以及开发过程中遇到的问题和解决方案。 总的来说,这个Android期末课程设计涵盖...
良好的项目结构可以帮助团队协作,通常包括src/main目录下的Java或Kotlin代码、res目录下的资源文件、build.gradle文件来配置构建选项等。 综上所述,实现一个仿今日头条的Android应用,需要掌握一系列Android开发...
- "纯看妹纸的 Kotlin 开源项目"则展示了一个使用Kotlin编写的特定功能应用。 2. **自定义组件与模仿设计**: - "仿网易严选底部弹出菜单"和"仿知乎内容广告栏"展示了Android UI设计的自定义实现。 - ...
### Android开发介绍、心得及项目知识点详述 #### 一、Android开发概述 Android开发是指为Android操作系统创建移动应用程序的过程。这一过程主要依赖于Java或Kotlin编程语言,结合Android SDK提供的工具和库来实现...
【Android培训学习心得体会】 在Android培训的过程中,我深深地体会到了技术进步的重要性。Android作为一个全球领先的移动操作系统,其学习和掌握不仅是提升个人技能的关键,更是适应信息化时代发展的必要条件。...
对于上层应用程序,开发者通常会使用Java或Kotlin进行Android应用开发。理解Android框架、生命周期管理、多线程处理以及内存管理是必备技能。此外,MTK平台可能提供特定的API或服务,如多媒体编码解码、电源管理等,...
在Android开发中,Java是最主要的编程语言,但随着Kotlin的崛起,现在也成为了一种广泛采用的语言。开发者通常使用集成开发环境(IDE)如Android Studio来编写、测试和调试代码。Android Studio提供了丰富的功能,...
25. 语言扩展:了解JVM上的其他语言,如Kotlin、Groovy,了解它们的优势和应用场景。 26. 性能监控:了解JProfiler、VisualVM等工具,对应用程序进行性能监控和分析。 27. 容器化:熟悉Docker和Kubernetes,实现...
12. 移动开发:iOS的Swift或Android的Java/Kotlin,以及对应的开发环境和工具。 13. 工具和环境:IDE(如Visual Studio Code、IntelliJ IDEA),包管理器(如npm、pip),构建工具(如Webpack、Gradle)等。 以上...
"分享说明.txt" 文件可能包含的是作者对设计过程的一些心得、使用技巧或者是安装和运行源代码的指南。这份文档对于初学者来说尤其重要,因为它提供了项目运行的关键信息,避免了在尝试运行源码时遇到的常见问题。...
7. **Android开发**:Kotlin在Android开发中的应用,如Anko库、Dagger注解、Android KTX等,以及如何使用Kotlin进行Android组件(如Activity、Fragment)的开发。 8. **领域特定语言(DSL)**:Kotlin支持构建...
APP层是Android应用程序的最高层,主要使用Java或Kotlin编写。在这个层次,开发者可以通过调用系统API来实现各种功能,例如控制LED。 ##### 3.1 实现方式 - 创建一个Activity来展示用户界面。 - 在Activity中调用...