Kotlin语法糖--开始

谷歌宣布Kotlin成为Android官方开发语言已经4个多月了,相信你或多或少的已经开始接触Kotlin语言了。 无需纠结会与不会,本系列将带你入门,让你体验到什么叫简洁、高效、快捷。今天先介绍些简单的知识,让你大开眼界
本篇文章涉及到的代码已经提交到Github

Kotlin的“简历”

  • 来自于著名的IDE软件开发公司JetBrains(IntelliJ IDEA就是该公司的杰作)
  • 起源来自JetBrains的圣彼得堡团队,名称取自圣彼得堡附近的一个小岛(Kotlin Island)
  • 一种基于JVM的静态类型编程语言

初探

我个人认为,对一门语言的学习一开始只需要了解一个大概,并对比其他语言对其优秀的地方大致了解即可。那么我们就简单看下Kotlin的语法

定义包

跟Java一样,得先从package开始


Kotlin语法糖--开始_第1张图片
新建package

有了包之后这样我们就可以开始代码的编写工作了

定义函数

函数很简单,最开始的关键字fun意思为function,平添几分双关之意

Main函数声明

fun main(args: Array) {
    
}

对比一下Java中的写法

public static void main(String[] args) {
        
}

通过对比,你应该很清楚的了解到函数名、入参、返回值的位置了吧
与Java不同的是,Kotlin中函数都是有返回类型的,Main函数也不例外,他的返回类型是Unit,只不过该返回类型可以直接省略,因为它代表无意义的值

fun main(args: Array) : 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 = arrayOf("a", "b", "c")
for (array in arrays) {
    println(array)
}
for (index in arrays.indices) {
    println(index)
}
使用while循环

这个跟Java没有啥太大区别

var index = 0
while (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("a", "b", "c")
for (list in lists) {
    println(list)
}
if ("a" in lists) {
    println("yes")
}

在遍历pair型集合的时候我们还可以这样

val params= hashMapOf("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)

看下使用时的优越性

val params= hashMapOf("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语法糖--开始_第2张图片
    Kotlin插件
  • 重启IDE之后直接创建Kotlin文件


    创建Kotlin文件
  • 点击右下角提示


    Kotlin语法糖--开始_第3张图片
    Kotlin配置提示
  • 同步代码,完成

到此为止,我们对Kotlin应该有个大概的印象了。后面我们就从基本类型开始一点点的学习吧

参考文章

为什么我要改用Kotlin

你可能感兴趣的:(Kotlin语法糖--开始)