Kotlin语法糖--开始
谷歌宣布Kotlin成为Android官方开发语言已经4个多月了,相信你或多或少的已经开始接触Kotlin语言了。 无需纠结会与不会,本系列将带你入门,让你体验到什么叫简洁、高效、快捷。今天先介绍些简单的知识,让你大开眼界
本篇文章涉及到的代码已经提交到Github
Kotlin的“简历”
- 来自于著名的IDE软件开发公司JetBrains(IntelliJ IDEA就是该公司的杰作)
- 起源来自JetBrains的圣彼得堡团队,名称取自圣彼得堡附近的一个小岛(Kotlin Island)
- 一种基于JVM的静态类型编程语言
初探
我个人认为,对一门语言的学习一开始只需要了解一个大概,并对比其他语言对其优秀的地方大致了解即可。那么我们就简单看下Kotlin的语法
定义包
跟Java一样,得先从package开始
新建package
有了包之后这样我们就可以开始代码的编写工作了
定义函数
函数很简单,最开始的关键字fun意思为function,平添几分双关之意
Main函数声明
fun main(args: Array<String>) {
}
对比一下Java中的写法
public static void main(String[] args) {
}
通过对比,你应该很清楚的了解到函数名、入参、返回值的位置了吧
与Java不同的是,Kotlin中函数都是有返回类型的,Main函数也不例外,他的返回类型是Unit,只不过该返回类型可以直接省略,因为它代表无意义的值
fun main(args: Array<String>) : Unit {
}
如果想实现一个返回值为Int类型的函数,那么你可以这样
fun sun(a: Int, b: Int) : Int {
return a+b
}
下面就是Kotlin比Java高明的地方了,函数表达式:他可以将表达式作为函数体并且自动推断返回值类型
fun min(a: Int, b: Int) = a-b
可变变量和不可变变量
这个很简单,var代表可变变量,val代表不可变变量。使用val声明的变量不能在初始化之后再次赋值,它对应的是Java的final变量。默认情况下,应该尽可能地使用val关键字来声明所有的Kotlin变量
var a: Int = 3
var b = "b"
val c = "c"
var d: Int
d=3
如果指定了初始化器,那么在没有指定类型的情况下,Kotlin编译器会分析初始化器表达式的值,并把值的类型作为变量的类型;如果没有指定初始化器,需要显示地指定它的类型,否则编译器无法推断出它的类型。
使用可空值
NPE是Java开发程序中最常见的崩溃了,我们不得不在代码关键地方进行NPE的判断。在Kotlin中空指针异常得到了很好的解决:当你声明一个具体类型的对象时,如果你想给它的值初始化为null,那么一定要在声明的类型后添加?来标识该引用可为空
var valueNull: String = null // 这样是不行的
var valueNull: String? = null
使用字符串模板
Kotlin中一个$连接一个世界,$符号作为属性或者表达式的引用前缀
var a: Int = 3
var a1 = 2;
var b = "b"
var value = "$a+$a1=${a+a1}"
println(value)
使用条件表达式
fun max(a: Int, b: Int) : Int {
if (a>b) {
return a
}
else {
return b
}
}
说真的这样的写法与Java几乎异曲同工,但是Kotlin毕竟说的是表达式,这个不叫表达式,表达式是这样的
fun max1(a: Int, b: Int) = if (a>b) a else b
表达式有值,并且能作为另一个表达式的一部分使用。
语句总是包含着它的代码块中的顶层元素,并且没有自己的值。
当函数体是由单个表达式构成时,可以用这个表达式作为完整的函数体,并且去掉花括号和return语句
适用类型检测及自动类型转换
Kotlin可以在检测完类型之后自动将其类型推断为所使用的类型,但是Java没这个功能。如下的例子中,Java必须再转换一遍才行,而用常人的眼光来看,这个算多此一举了
fun getStringLength(value: Any?) : Int? {
if (value is String) {
return value.length
}
return null
}
public int getStringLength(Object value) {
if (value instanceof String) {
return value.toString().length();
}
return 0;
}
使用for循环
对数组进行遍历以及索引遍历
var arrays:Array<String> = arrayOf("a", "b", "c")
for (array in arrays) {
println(array)
}
for (index in arrays.indices) {
println(index)
}
使用while循环
这个跟Java没有啥太大区别
var index = 0
while (index<arrays.size) {
println(arrays[index])
index++
}
使用When表达式
由于原来的switch/case机制存在局限,故而Kotlin推出了新的关键字,即when/else来处理多路分支的条件判断
fun describe(value: Any?) {
when (value) {
in 0..100 -> println("比101小")
else -> println("比100大")
}
}
从以上代码可以看出when/else与switch/case有以下几点区别:
- 关键字switch被when所取代
- 判断语句“case 常量值:”被“常量值 ->”所取代
- 每个分支后面的break语句被取消了,默认情况下Kotlin一个分支处理完就直接跳出多路语句
- 关键字default被else所取代
使用区间
刚才已经接触了一下区间的概念。Kotlin一般使用in运算符来检测某个数字是否在指定的区间,比如刚才0..100,就是0-100的闭区间
在区间迭代的过程中,我们还可以添加步进规则,比如这样
for (i in 0..100 step 2) {
print(i)
}
println("")
for (i in 9 downTo 0 step 3) {
print(i)
}
这里的step相当于i+2以及i-3
除了..代表左闭右闭区间外,还有关键字until代表左闭右开区间
for (i in 0 until 100) {
print(i)
}
使用集合
集合的遍历与刚才的数组差不多。我们也可以通过in运算符来判断某个元素是否在相应的集合中
var lists = listOf<String>("a", "b", "c")
for (list in lists) {
println(list)
}
if ("a" in lists) {
println("yes")
}
在遍历pair型集合的时候我们还可以这样
val params= hashMapOf<String, String>("key1" to "value1", "key2" to "value2")
for ((k, v) in params) {
println("$k-$v")
}
对于map,如果你想单独操作其中一个Key的Value,那么这样去获取或者修改就行了
println(params["key1"])
params["key1"]="key3"
使用数据类
Kotlin中将数据类与一般的Class区分开来了,展现形式上是多一个data关键字,因为数据类默认将自动提供以下功能:
(1) 所有属性的getters(对于var还有setters)
(2) equal()
(3) hashcode()
(4) toString()
(5) copy()
(6) component1(),component2()等函数
简而言之,就是避免了我们手写getter/setter/toString等方法
data class RequestBean(val url: String, val params: HashMap<String, String>)
看下使用时的优越性
val params= hashMapOf<String, String>("key1" to "value1", "key2" to "value2");
var data=RequestBean("http://www.baidu.com", params)
println(data.component1())
println(data.toString())
执行结果
由于自身实现了toString()函数,所以数据显示相对直观点,这个可是Java没有的
函数默认值参数
我们在Java里如果要调用方法,一般方法要什么参数,我们就得老老实实的将参数传过去。但是Kotlin里就不一定了,他可以在定义函数的时候,给入参设置一个默认值。这样我们就可以不给这个参数赋值,而让函数直接使用这个默认值进行后续操作
fun getTime(time: Date = Date()) {
println(time.time)
}
getTime()
这样还有一个好处,就是避免了不必要的方法重载,让程序执行效率更高
延迟属性
虽然说属性必须定义的时候就初始化,但Kotlin还是提供了一个标识使得变量或常量可以延迟初始化,这个又是Kotlin中的创新。来看看Java代码
static String a2;
public static void main(String[] args) {
System.out.println(a2);
}
你说如果在真实场景下不小心用到了这个a2,那不就抛出空指针了?来看看Kotlin的处理方式
class LazyClass {
val valueA: String by lazy {
"Hello World"
}
lateinit var valueB: String
}
仔细看一下代码,注意变量与常量使用时的不同
这样看倒是没什么, 我们直接来获取valueB吧。会发生什么事情?你会无情的得到一个异常,告诉你还没初始化呢
未初始化valueB
来看看正确的调用方式
val lazyClass=LazyClass()
println(lazyClass.valueA)
lazyClass.valueB="valueB"
println(lazyClass.valueB)
变量与常量使用时的区别:
(1) lazy{}只能用在val类型,lateinit只能用在var类型
(2) lateinit不能用在可空的属性上和基本类型上
(3) lateinit可以在任何位置初始化并且可以初始化多次。而lazy在第一次被调用时就被初始化,想要被改变只能重新定义
扩展函数
我需要扩展一个长时间显示Toast的方法,在Java中一般情况下我们就写一个静态的Utils类来整合这些功能。
public static void longToast(Context context, String message) {
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
}
但是在Kotlin需要重写扩展方法就可以了,来看看代码
fun Context.longToast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
}
使用时候直接在Context及其子类中即可直接使用
longToast("Hello")
需要注意的是Kotlin的函数扩展并不是真正修改了对应的类文件,而是在编译时做了处理,让我们看起来像是在那个类中扩展了方法
创建单例
Kotlin中创建单例的方式很多,这边是一个简单的例子
object Instance {
fun printValue(value: String) {
println(value)
}
}
Instance.printValue("HelloB")
if not null and else
Kotlin中没有三目运算符,但是它提供变种的形式来实现,学名叫做Elvis操作符
var file: File? = File("D:\\学习\\Speeding Up Your Android Gradle Builds (Google IO '17).mp4")
println(file?.name ?: "empty")
这句话就是当file.name不为null的话,就显示file的名字,否则显示“empty”
还有一种写法
file?.let {
println(file?.name)
println(file?.length())
}
如果这个对象不为null的话,那么将会执行let里面的表达式
对一个对象实例调用多个函数(with)
还是用之前的lazyClass对象
with(lazyClass) {
println(valueA)
println(valueB)
}
我们看这里可以直接操作该对象的属性还有函数,让代码更简洁一些了
与with功能差不多的还有apply,不同处是apply函数会在执行接收的函数后返回this。我们对之前的代码改造一下做个对比
var lazyClass1 = with(lazyClass) {
valueB = "valueBwith"
this
}
println(lazyClass1.valueB)
lazyClass1 = lazyClass.apply {
valueB = "valueBapply"
}
println(lazyClass1.valueB)
with与apply对比
Lambda,高阶函数,Streams API,函数式编程支持
我们在Android Studio上不能直接使用Lambda表达式进行开发工作,因为其只支持到Java7,除非我们使用RetroLambda这种第三方库
所谓的Lambda表达式是匿名函数,这使得我们的代码看起来更加的简单
tv_hello = findViewById(R.id.tv_hello) as TextView
tv_hello.setOnClickListener {
Toast.makeText(this, "tv_hello", Toast.LENGTH_SHORT).show()
}
所谓的高阶函数就是可以接受函数作为参数,也可以返回函数作为结果。我假装写一个加减法计算器。Java中如果要写计算器,可能会抽出来一个方法,用switch或者if else分别对加减法进行处理,但是Kotlin的想法与Java不同,请看
fun convertValue(left: String, right: String, transform: (String, String) -> Int) : Int {
return transform(left, right)
}
额,这里其实我们什么操作都没有,就把2个字符串传进高阶函数里面去了而已,最终你要加还是要减,在你编写代码的过程中自个儿实现就行了,灵活性得到了提升
val plus = convertValue("1", "2") {
left: String, right: String ->
val a = left.toInt()
val b = right.toInt()
return@convertValue a+b
}
val min = convertValue("1", "2") {
left: String, right: String ->
val a = left.toInt()
val b = right.toInt()
return@convertValue a-b
}
与Java交互性好
Kotlin和Java都属于基于JVM的编程语言。Kotlin和Java的交互性很好,可以说是无缝连接。这表现在
- Kotlin可以自由的引用Java的代码,反之亦然。
- Kotlin可以现有的全部的Java框架和库
- Java文件可以很轻松的借助IntelliJ的插件转成Kotlin
关于性能
Kotlin的执行效率和Java代码的执行效率理论上一致的。有时候Kotlin可能会显得高一些,比如Kotlin提供了内联函数,可以设置某些高频方法进行inline操作,减少了运行时的进栈出栈和保存状态的开销。
Android Studio搭建
Android Studio 3.0开始完全支持Kotlin,可是Android Studio目前的稳定版还是2.3,所以讲一下2.3上的安装
-
插件下载
Kotlin插件 -
重启IDE之后直接创建Kotlin文件
创建Kotlin文件 -
点击右下角提示
Kotlin配置提示 - 同步代码,完成
到此为止,我们对Kotlin应该有个大概的印象了。后面我们就从基本类型开始一点点的学习吧