第一节 起源概述
面向JVM的语言
我们知道在Java语言中,会将源代码编译成字节码,再依赖各种不同平台上的虚拟机(JVM)来解释执行字节码,从而具有“一次编写,到处运行”的跨平台特性。同时,官方从很早以前就将JVM规范和Java规范分离开了,这意味着只要其他语言在编译过程中生成了字节码,那么照样可以通过JVM在不同平台上运行,这样一来这个语言就借着JVM拥有了跨平台能力。
由于是运行在JVM上的,因此也可以使用Java语言开发的各类API
也就是说虽然Java语言已经足够强大、应用的场景足够广泛,但其还是会在某些特殊场景下力有不逮,于是一些有能力的组织、公司就专门针对那些场景开发了一套基于JVM的新语言,以此来解决各自的需求,同时可以借助JVM来实现跨平台。
目前除了Java外,运行在JVM之上的语言主要还有如下几种:
1、Kotlin
目前已正式成为Android官方支持开发语言。
2、Groovy
Apache组织的Groovy 1.0于2007年1月2日发布。
这门动态语言拥有类似Python、Ruby和Smalltalk中的一些特性,可以作为Java平台的脚本语言使用。
由于其运行在JVM上的特性,Groovy可以使用其他Java语言编写的库。
Groovy的语法与Java非常相似,大多数Java代码也符合Groovy的语法规则,尽管可能语义不同。
3、其它
Jython:是一种完整的语言,它是一个Python语言在Java中的完全实现,Jython不仅给你提供了Python的库,同时也提供了所有的Java类,这使其有一个巨大的资源库。
JRuby:是一个纯Java实现的Ruby解释器。通过JRuby你可以在JVM上直接运行Ruby程序,调用Java的类库。
除此之外还有:Scala、Fantom、Clojure、Rhino、Ceylon。
本节参考阅读:
Kotlin
Kotlin(科特林)是一个用于现代多平台应用的静态编程语言,由 JetBrains 开发。
JetBrains成立于2000年,是一家捷克的软件开发公司,该公司最为人所熟知的产品是Java编程语言开发撰写时所用的集成开发环境:IntelliJ IDEA。
2011年7月,JetBrains推出Kotlin项目,这是一个面向JVM的新语言,它已被开发一年之久。
2012年2月,JetBrains以Apache 2许可证开源此项目。
2016年2月15日,Kotlin v1.0发布,这被认为是第一个官方稳定版本。
2017年在Google I/O大会上,Google宣布在Android上为Kotlin提供一等支持。
2019年在Google I/O大会上,Google宣布今后将优先采用 Kotlin 进行 Android 开发。
谷歌为什么要选择Kotlin?主要是两个原因:
1、谷歌和Oracle的诉讼官司。
2、Kotlin本身做的越来越好。
谷歌与Oracle的官司简述:
1995年5月23日,Sun公司发布了Java,Java语言正式诞生。
1996年1月,第一个JDK1.0诞生。
2009年4月15日,Sun公司被收购前夕,正式发布OpenJDK(OracleJDK的开源版本,完全自由,开放源码)。
2009年4月20日,Oracle以74亿美元的价格收购了Sun。
由于此时期OpenJDK并不是很成熟,因此Google的Android系统是基于OracleJDK来开发的。
2010年9月,Oracle就Google侵权一事向法院提起了诉讼,要求谷歌赔偿88亿美元。
理由很简单,虽然根据协议Android使用的OracleJDK后自身也开源了,但它违反了Java“一次编译,到处运行”的核心理念,在Android修改了很多地方之后,除了源码兼容以外,编译之后的产物APK,是不能在其它的java环境下执行的,而此时Android还称自己的api是java,但其实这已经不符合java的规范了。
另外,安卓上的GUI部分是自己做的,跟java默认的api(也就是swing那些)完全不同,那其实这个就彻底分裂了java,java其实有两个部分,一个是安卓上的java,还有一个是其它的java,那这个就是oracle所不能容忍的,于是就希望Google不要继续只用java的api,或者干脆重新做一个语言,不要再用java了。
2018年3月,美国联邦巡回上诉法院裁决,Google侵犯了Oracle的版权。
2018年8月,Google提出上诉,但美国联邦上诉法院宣布拒绝重新审理此案。
谷歌官方介绍为什么要优先支持Kotlin:
富有表现力且简洁:您可以使用更少的代码实现更多的功能。表达自己的想法,少编写样板代码。在使用Kotlin的专业开发者中,有67%的人反映其工作效率有所提高。
更安全的代码:Kotlin有许多语言功能,可帮助您避免null指针异常等常见编程错误,包含Kotlin代码的Android应用发生崩溃的可能性降低了20%。
可互操作:您可以在Kotlin代码中调用Java代码,或者在Java代码中调用Kotlin代码,Kotlin可完全与Java编程语言互操作,因此您可以根据需要在项目中添加任意数量的Kotlin代码。
结构化并发:Kotlin协程让异步代码像阻塞代码一样易于使用,协程可大幅简化后台任务管理,例如网络调用、本地数据访问等任务的管理。
正如上面介绍的,Kotlin确实存在很多语法糖与高级特性,好是真的好,但笔者认为对于刚入门的新手来说,记忆和理解这些语法糖和特性也是一个不小的挑战,请大家做好心理准备。
另外,我们需要了解的是,Kotlin能用于哪些开发?
用于服务器开发,Kotlin非常适合开发服务器端应用程序,它可以让你编写简明且表现力强的代码,同时保持与现有基于Java的技术栈的完全兼容性以及平滑的学习曲线。
用于Android开发。
用于JavaScript开发,Kotlin/JS提供了转换Kotlin代码、Kotlin标准库的能力,并且兼容JavaScript的任何依赖项,Kotlin/JS的当前实现以ES5为目标。
用于原生开发,Kotlin/Native是一种将Kotlin代码编译为无需虚拟机就可运行的原生二进制文件的技术,Kotlin/Native 支持以下平台:iOS、tvOS、watchOS、Windows(MinGW)、安卓NDK。
用于数据科学、竞技程序设计。
最后,在百度百科上有这么一句“Jetbrains希望这个新语言能够推动IntelliJ IDEA的销售。”
结合笔者学习完Kotlin语言后,得出一个猜测:
1、由于Kotlin是面向虚拟机的语言,且已经迭代多年,所以其和Java语言的相比,在性能上不会差太多。
2、Kotlin提供了非常多的语法糖和新特性,且其完全支持现有的Java类库,旨在讨好程序员。
3、Kotlin可以直接在IntelliJ上通过插件使用,程序员爱上了Kotlin,自然就有可能购买IntelliJ。
本节参考阅读:
- 百度百科 - Kotlin
- 怎么看待谷歌(Google)赢得了跟甲骨文(Oracle)关于安卓使用 Java api 的官司?
- Android 的 Kotlin 优先方法
- Kotlin 官方文档 中文版
环境搭建
从上文可知Kotlin不止可以用来开发Android,显然Kotlin开发的方式也不会只有一种:
使用IntelliJ IDEA
使用Android Studio,开发Android应用程序的官方IDE,它是基于Intellij IDEA开发的IDE,因而其UI和功能都与后者高度相似。
使用Eclipse,需要手动安装Kotlin插件。
使用命令行(就像我们刚学Java时使用的“java”和“javac”来编译源代码一样,Kotlin也提供了两个类似的工具“kotlin”和“kotlinc”)。
由于我们是做Android开发,本文就以Android Studio为开发工具,介绍Kotlin的各类语法。
首当其冲的任务就是,让现有的老Android项目支持Kotlin语言:
1、确保自己手上的AS是比较新的版本。
2、依次点击AS的菜单“Tool -> Kotlin -> Configure Kotlin in Project”。
3、然后按照弹出的对话框一步一步选择就可以了。
上面的操作,本质上就做了三件事:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 在项目根build.gradle里添加kotlin插件
buildscript {
ext.kotlin_version = '1.5.30'
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
// 在app的build.gradle最上方apply插件
apply plugin: 'kotlin-android'
// 在app的build.gradle里引入kotlin的标准库
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
接着随便找一个包,右键创建一个Kotlin文件(注意不是Kotlin类),写入如下代码:1
2
3
4
5
6
7
8package com.kotlintest
// 使用fun关键字定义一个名为main的函数
fun main() {
// 控制台输出字符串
// kotlin中语句的末尾不需要加“;”号
print("Hello World")
}
语句解释:
- 如果你的函数名字叫“main”,那么在其左侧就会出现一个绿色三角号,点击可以直接执行该函数。
- Kotlin的源文件以“kt”为后缀名。
第二节 Kotlin入门
基础语法
范例1:你叫什么啊。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27package com.kotlintest
fun main() {
// 输出字符串,并换行。
println("你好,你叫什么?")
// 从标准输入流System.in中读取一行用户输入。
println("啊,你叫" + readLine() + "啊")
}
/*
上面用到的print、println、readLine都是kotlin内置的函数,它们被放到“kotlin.io”包中。
以下多个包会默认导入到每个Kotlin文件中:
kotlin.*
kotlin.annotation.*
kotlin.collections.*
kotlin.comparisons.*
kotlin.io.*
kotlin.ranges.*
kotlin.sequences.*
kotlin.text.*
根据目标平台还会导入额外的包:
JVM:
java.lang.*
kotlin.jvm.*
JS:
kotlin.js.*
*/
数据类型
Kotlin中提供如下几种基本数据类型:
1 | /* |
范例1:定义变量。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24package com.kotlintest
fun main() {
// Kotlin中有两种变量:可变变量和不可变变量
// 前者使用var定义,此类变量可以任意修改其值
// 后者使用val定义,此类变量只可以赋值1次,若赋值成功后再次尝试修改,则编译报错
// 定义变量的语法格式为:
// var/val <变量名> : <类型> = <初始化值>
var a: Byte = 1
var b: Short = 2
var c: Int = 3
var d: Long = 4
var s: String = "Hello"
println(s)
val f: Byte = 1
// 下面语句会编译报错,因为f是不可变量
f = 2
}
// var全称为:variable
// val全称为:value,使用val定义的局部变量相当于Java中的final关键词。
范例2:自动推算类型。
1 | package com.kotlintest |
范例3:整型数据。
1 | package com.kotlintest |
范例4:浮点数据。
1 | package com.kotlintest |
范例5:数字字面常量。
1 | package com.kotlintest |
范例6:无符号整型。
1 | package com.kotlintest |
范例7:自动类型晋升。
1 | package com.kotlintest |
范例8:字符与布尔。
1 | package com.kotlintest |
范例9-1:字符串。
1 | package com.kotlintest |
范例9-2:字符串。
1 | package com.kotlintest |
范例10:原生数组。
1 | package com.kotlintest |
控制流程
范例1:If表达式。
1 | package com.kotlintest |
范例2:when表达式。
1 | package com.kotlintest |
范例3:for循环。
1 | package com.kotlintest |
范例3:while循环。
1 | package com.kotlintest |
范例4:区间。
1 | package com.kotlintest |
范例5:in关键字的其它用法。
1 | package com.kotlintest |
范例6:break和continue。
1 | package com.kotlintest |
面向对象
类和对象
范例1:类和对象。
1 | package com.kotlintest |
范例2:主构造方法。
1 | package com.kotlintest |
属性
范例1:Getter和Setter。
1 | package com.kotlintest |
范例2:编译期常量。
1 | package com.kotlintest |
函数
范例1:函数的定义。
1 | package com.kotlintest |
范例2:可变参数。
1 | package com.kotlintest |
范例3:单表达式函数。
1 | package com.kotlintest |
范例4:函数作为参数。
1 | package com.kotlintest |
范例5:类内函数作为参数。
1 | package com.kotlintest |
范例6:参数默认值与具名参数。
1 | package com.kotlintest |
继承
范例1:定义父类。
1 | package com.kotlintest |
范例2:调用父类构造函数。
1 | package com.kotlintest |
范例3:函数重写。
1 | package com.kotlintest |
范例3:属性重写。
1 | package com.kotlintest |
范例4:抽象类。
1 | package com.kotlintest |
范例5:接口。
1 | package com.kotlintest |
范例6:接口和抽象类同时出现。
1 | package com.kotlintest |
空类型安全
范例1:不能赋值null。
1 | package com.kotlintest |
范例2:“?
”操作符。
1 | package com.kotlintest |
范例3:“?.
”操作符。
1 | package com.kotlintest |
范例4:“?:
”操作符。
1 | package com.kotlintest |
范例5:“!!
”操作符。
1 | package com.kotlintest |
范例6:“as
”和“as?
”操作符。
1 | package com.kotlintest |
范例7:“is
”操作符。
1 | package com.kotlintest |
扩展
范例1:扩展函数。
1 | package com.kotlintest |
范例2:扩展是静态解析的。
1 | package com.kotlintest |
范例3:函数重名。
1 | package com.kotlintest |
范例4:扩展Any类。
1 | package com.kotlintest |
范例5:中缀表示法。
1 | package com.kotlintest |
操作符重载
范例1:重载入门。
1 | package com.kotlintest |
范例2:Int与Person的运算。
1 | package com.kotlintest |
范例3:equals方法。
1 | package com.kotlintest |
范例4:其它方法。
1 | // 在Kotlin中我们可以重载的操作符包括但不限于以下操作符: |
lambda表达式
范例1:匿名函数与lambda表达式。
1 | package com.kotlintest |
范例2:传递末尾的lambda表达式。
1 | package com.kotlintest |
范例3:指定返回值。
1 | package com.kotlintest |
范例4:真正的匿名函数。
1 | package com.kotlintest |
内部类
范例1:实例内部类和静态内部类。
1 | package com.kotlintest |
范例2:匿名内部类。
1 | package com.kotlintest |
范例3:没有任何父类。
1 | package com.kotlintest |
范例4:单例模式。
1 | package com.kotlintest |
范例5:伴生对象与静态函数。
1 | package com.kotlintest |
数据类
范例1:数据类入门。
1 | package com.kotlintest |
范例2:自己重写函数。
1 | package com.kotlintest |
范例3:屏蔽某个属性。
1 | package com.kotlintest |
范例4:解构。
1 | package com.kotlintest |
范例5:多返回值函数。
1 | package com.kotlintest |
枚举类
范例1:枚举的定义。
1 | package com.kotlintest |
范例2:枚举的应用。
1 | package com.kotlintest |
集合框架
范例1:List。
1 | package com.kotlintest |
范例2:Map。
1 | package com.kotlintest |
范例3:集合的遍历。
1 | package com.kotlintest |
范例4:集合的过滤/筛选。
1 | package com.kotlintest |
范例5:集合的转换。
1 | package com.kotlintest |
范例6:混合运算。
1 | package com.kotlintest |
范例7:flatMap。
1 | package com.kotlintest |
可见性修饰符
在 Kotlin 中有这四个可见性修饰符:private
、 protected
、 internal
和public
,默认可见性是public
。类、对象、接口、构造函数、方法与属性及其setter
都可以有可见性修饰符,getter
总是与属性有着相同的可见性。
范例1:包与顶层声明。
1 | package com.kotlintest |
范例2:类成员。
1 | package com.kotlintest |
范例3:主构造函数。
1 | package com.kotlintest |
范例4:模块。
1 |
|
进阶特性
代理
范例1:代理是干什么用的。
1 | package com.kotlintest |
范例2:代理-属性隐藏。
1 | package com.kotlintest |
范例3:代理 - 属性值。
1 | package com.kotlintest |
范例4:属性代理 - 延迟初始化。
1 | package com.kotlintest |
范例5:属性代理 - 监听值变化。
1 | package com.kotlintest |
泛型
范例1:泛型基础。
1 | package com.kotlintest |
范例2:where子句。
1 | package com.kotlintest |
范例3:不型变。
1 | package com.kotlintest |
范例4:协变。
1 | package com.kotlintest |
范例5:逆变。
1 | package com.kotlintest |
范例6:我自己负责安全问题,请放行。
1 | package com.kotlintest |
范例7:使用处型变。
1 | package com.kotlintest |
反射
范例1:反射的简介。
1 | // 在Kotlin支持两套反射API: |
范例2:反射类。
1 | package com.kotlintest |
范例3:Kotlin和Java字节码。
1 | package com.kotlintest |
范例4:函数、属性、内部类、父类等。
1 | package com.kotlintest |
注解
注解实际上就是一种代码标签,它作用的对象是代码。对于我们来说,使用注解的方式有两种:
1、使用Kotlin或者其他开发者定义好的注解
2、自定义注解
2.1、以Kotlin内置的各类注解作为基础,定义出自己的注解
2.2、使用反射技术在特定的时机下,解析代码上的注解,并依据注解内的各个属性值来决定执行哪些操作。
很显然,日常开发中,我们很少会去自定义注解,且网上自定义注解的资料也一大堆,因此本文只会去介绍一些常用的注解的使用方法。
范例1:@Transient注解。
1 | data class Student( |
范例2:@Synchronized注解。
1 | // 如果你想创建同步函数,可以使用这个注解 |
范例3:@JvmStatic注解。
1 | // 对于老项目来说,打算使用Kotlin后,一开始不可能将所有代码都改为Kotlin |
协程
协程可以让异步代码同步化,可以降低异步程序的设计复杂度,由于笔者事务繁忙,且日常开发暂时没有设置到这块,所以暂时搁浅。
第三节 Android中的Kotlin
范例1:Application的单例对象。
1 | class App : Application() { |
范例2:let、run、also、apply函数。
1 | class MainActivity : BaseActivity() { |
本文参考阅读: