Kotlin 概述: 开启 Kotlin 之旅
Google在2017年的IO大会上宣布,将Android开发的官方语言更换为Kotlin。
Java的辉煌与阴影
1995年,当年如日中天的Sun公司发布了Java语言,引起了巨大的轰动,与当时主流的C语言和Basic语言比起来,Java语言简单、面向对象、稳定、与平台无关、解释型、多线程、动态等特点,就像是打开了一个新的世界,一时间风靡全球,云集者众,微软为了模仿Java搞出C#语言,Netscape为了赶时髦硬塞出一个JavaScript语言,IBM则捏着鼻子做了Java IDE Eclipse(日蚀,呵呵)。直到现在,Java在编程世界里还占据着举足轻重的地位,Andy Rubin在开发Android系统时,也很自然地采用了Java和C (C 负责NDK开发)作为开发语言。
但是,Java毕竟是20多年前的语言了,虽然有不断扩展更新,但是底层设计思想是很难改动的,这就导致它很难实现一些新的语言特性,例如函数式编程、Lambda 表达式、流式API、高阶函数、空指针安全等(虽然Java8实现了部分特性,但是Android还不怎么支持Java8),这些新的语言特性大受好评,可以说解放了编程的生产力,这其实也说明了一个事实:开发效率/时间是软件公司真正的瓶颈,任何能压缩代码量,提高开发效率的举措,都应该受到重视。
而且,Android还存在Java版权危机的问题,收购了Sun公司的Oracle曾向Google索要巨额的Java版权费,这可能也加快了Google寻找Android开发替代语言的动作。
苹果公司已经在用Swift语言替代Object-C语言,Google也找到了替代Java的语言,也就是JetBrains公司(Android Studio也是用该公司的Intelli J改的)主推的Kotlin。
其实,Swift和Kotlin还挺相似的,有一篇Swift is like Kotlin对这两种语言做过简单的对比。
Kotlin的出现
Kotlin也是基于JVM设计的编程语言,算是对Java的温和改良,她是一个开源项目的成果,拥有很高的声望,很多公司、组织、业界大犇都很喜欢她,Square公司的 JakeWharton 大神(Dagger、ButterKnife、Retrofit、OkHttp...之父)就专门写了篇Using Project Kotlin for Android为Kotlin站台。
相对Java来说,Kotlin在编写代码时有如下优势:代码简洁高效、函数式编程、空指针安全、支持lambda表达式、流式API等。
在执行效率上,Kotlin和Java具有同样的理论速度(都是编译成JVM字节码)。
另外,新语言必须考虑兼容性,为了与存量项目代码和谐共处,Kotlin和Java是互相完美兼容的,两种代码文件可以并存,代码可以互相调用、文件可以互相转换,库文件也可以无障碍地互相调用,据说使用Kotlin基本不会带来额外的成本负担。
编程语言本质上还是工具,要运用工具提高效率和质量 (鏈接:https://www.jianshu.com/p/bb53cba6c8f4)
Java 有哪些问题?
- 空引用(Null references):连空引用的发明者都成这是个 billion-dollar 错误(参见)。不论你费多大的功夫,你都无法避免它。因为 Java 的类型系统就是不安全的。
- 原始类型(Raw types):我们在开发的时候总是会为了保持兼容性而卡在范型原始类型的问题上,我们都知道要努力避免 raw type 的警告,但是它们毕竟是在语言层面上的存在,这必定会造成误解和不安全因素。
- 协变数组(Covariant arrays):你可以创建一个 string 类型的数组和一个 object 型的数组,然后把 string 数组分配给 object 数组。这样的代码可以通过编译,但是一旦你尝试在运行时分配一个数给那个数组的时候,他就会在运行时抛出异常。
- Java 8 存在高阶方法( higher-order functions ),但是他们是通过 SAM 类型 实现的。SAM 是一个单个抽象方法,每个函数类型都需要一个对应的接口。如果你想要创建一个并不存在的 lambda 的时候或者不存着对应的函数类型的时候,你要自己去创建函数类型作为接口。
- 泛型中的通配符:诡异的泛型总是难以操作,难以阅读,书写,以及理解。对编译器而言,异常检查也变得很困难。
- 不够灵活,缺乏扩展能力:我们不能给不是我们自己写的 types、classes 或者 interfaces 增加新的方法。长时间以来,我们都会采用 util 类,杂乱无章地堆砌着我们代码或者或者揉在同一个 util package 里面。如果这是解决方案的话,它肯定不理想。
- 语法繁琐,不够简洁:Java 肯定不是最简洁的语言。这件事本身不是件坏事,但是事实上存在太多的常见的冗余。这会带来潜在的错误和缺陷。在这之前,我们还要处理安卓 API 带来的问题。
Kotlin 是什么?
- Kotlin 编程语言是一种现代语言,简洁,安全,实用,专注于与Java代码的互操作性。
它几乎可以在今天使用Java的任何地方使用:用于服务器端开发,Android应用程序等等。
Kotlin与所有现有的Java框架100%兼容,并且具有良好的工具支持。它是一种实用的语言,具有非常低的学习曲线,可以被Java开发人员快速掌握。 Kotlin代码不仅可以编译为JVM字节码,还可以编译为JavaScript和Native代码
Kotlin Features
- Lambda 表达式
- 数据类 (Data classes)
- 函数字面量和内联函数(Function literals & inline functions)
- 函数扩展 (Extension functions)
- 空安全(Null safety)
- 智能转换(Smart casts)
- 字符串模板(String templates)
- 主构造函数(Primary constructors)
- 类委托(Class delegation)
- 类型推断(Type inference)
- 单例(Singletons)
- 声明点变量(Declaration-site variance)
- 区间表达式(Range expressions)
核心的目标
- 简约:帮你减少实现同一个功能的代码量。
- 易懂:让你的代码更容易阅读,同时易于理解。
- 安全:移除了你可能会犯错误的功能。
- 通用:基于 JVM 和 Javascript,你可以在很多地方运行。
- 互操作性:这就意味着 Kotlin 和 Java 可以相互调用,目标是 100% 兼容。
基本语法
定义包
代码语言:javascript复制package com.fooimport foo.Bar
import foo.*
定义函数
代码语言:javascript复制fun sum(a: Int, b: Int): Int { return a b
}fun sum(a: Int, b: Int) = a b
定义局部变量
- val
fun localVariables() {
val a: Int = 1
val b = 1 // 自动推导类型为Int
val c: Int // 没有初始值时需要显式制定变量类型
c = 1 // 初始复制
// c = 2 这个是错误的,val定义的只读变量不可重新赋值}
val大致相当于Java里的final,C/C 里的const,Swift里的let,ES6中的const, 使用val定义的是不可重新赋值的变量。
- var
- 定义可重复赋值(读写)的变量使用var关键字。var定义的变量就是大部分编程语言里的普通变量,可读写,可重新赋值,Swift也是使用var。 ES6中用的是 let。注意哦。
fun mutableVariables() {
var x = 5 // 类型自动推导为Int
x = 1}
字符串模板
- Kotlin支持许多动态语言早就支持的字符串模板,与Groovy和Swift类似
fun strTemplate() {
val name = "Miufox"
val age = 6 print("I have a cat, name is $name age:$age")
val a:Int = 2016
val b = 40 print("sum($a $b) = ${a b}")
var args = arrayOf("Cat", "Dog", "Rabbit") print("Hello ${args[0]}")
}
条件表达式
- if
- Kotlin中的if语句和Java类似
fun max(a: Int, b: Int): Int { if (a > b) return a else
return b
}
使用if表达式,上面的函数可以简化成这样
代码语言:javascript复制fun max(a: Int, b: Int) = if (a > b) a else b
- when
- Kotlin增加的Java没有的when表达式,支持强大的类型匹配功能,这里是一个简单的例子
fun cases(obj: Any) {
when (obj) {
1 -> print("One") "Hello" -> print("Greeting")
is Long -> print("Long")
!is String -> print("Not a string") else -> print("Unknown")
}
}
空值检查
- Kotlin中,如果一个值可能为null就必须显式标示为nullable,使用问号?,下面的函数返回可能为null
fun parseInt(str: String): Int? { // ...}
使用返回值可能为null的函数
代码语言:javascript复制fun testInt(args: Array<String>) { if (args.size < 2) {
print("Two integers expected") return
}
val x = parseInt(args[0]) // Int?
val y = parseInt(args[1])//Int?
if (x != null && y != null) { // null检查之后,这里自动类型转换为非空值
print(x * y)
}
}
类型检查和自动转换
- is操作符用于检查某个对象是否是指定的类型,检查完成后自动转换为指定的类型,无需再显式转换
fun getStringLength(obj: Any): Int? { if (obj is String) { // `obj` 自动转换为 `String`
return obj.length
} // 在类型检查的if分支外 obj依然是 `Any` 类型
return null}
下面的例子可能更清晰一些
代码语言:javascript复制fun getStringLength(obj: Any): Int? { if (obj !is String) return null
// `obj` 自动转换为 `String` 类型
return obj.length
}
甚至可以这样写
代码语言:javascript复制fun getStringLength(obj: Any): Int? { // && 右边 obj 已经自动转换为 String 类型
if (obj is String && obj.length > 0) return obj.length return null}
循环语句
- for
- for循环使用in操作符,相当于Java的冒号
fun forLoop1(args: Array<String>) { for (arg in args) {
print(arg)
}
}
或者这样写,带下标遍历数组
代码语言:javascript复制fun forLoop2(args: Array<String>) { for (i in args.indices) {
print(args[i])
}
}
- while
- while循环和Java类似
fun whileLoop1(args: Array<String>) { var i = 0
while (i < args.size){
print(args[i ])
}
}
- Range表达式
fun range1(x: Int, y: Int) { if (x in 1..y - 1) { print("OK")
}
}
使用集合的简单例子
- Kotlin支持Java的集合类型,但比Java强大很多
// 集合类型fun names1(names: Array<String>) { for (name in names) {
println(name)
}
}// 检查是否包含fun names2(text: String, names: Array<String>) { if (text in names) {
print("Yes")
}
}// 使用lambda表达式fun names3(names: Collection<String>) {
names.filter { it.startsWith("A") }
.sortedBy { it }
.map { it.toUpperCase() }
.forEach { print(it) }
}
list.filter { it > 10 }.map { element -> element * 2 }
实用 Kotlin 代码速查
- 创建Model
- Kotlin中称作数据类(Data Class)
data class Customer(val name: String, val email: String)
会自动生成 getter/setter/equals/hashCode/toString/copy等
- 函数参数支持默认值
fun foo(a: Int = 0, b: String = "") { // do something}
- 集合数据过滤
fun filters(list: Collection<Int>) {
val positives1 = list.filter { x -> x > 0 } // 也可以这样
val positives2 = list.filter { it > 0 }
}
- 字典数据遍历
fun maps(map: Map<String, String>) { for ((k, v) in map) {
println("$k -> $v")
}
}
- 不可变List
fun readOnlyList() {
val list = listOf("a", "b", "c") // list.add("d") 错误:不能修改元素}
- 不可变Map
fun readOnlyMap() {
val map1 = mapOf("a" to 1, "b" to 2, "c" to 3)
println(map1["a"]) // map2["key"] = 123 错误:不能修改元素}
- 扩展属性和函数
fun Int.biggerThanTen(): Boolean { return this > 10}// 测试一下扩展函数fun extensions() { 100.biggerThanTen() 5.biggerThanTen()
}
- 单例对象
object Singleton {
val name = "Name"}
- 可空类型的使用
//nullable用法fun testNullable() {
val files = File("Test").listFiles()
println(files?.size)
println(files?.size ?: "empty")
}
- when表达式
fun transform(color: String): Int { return when (color) { "Red" -> 0
"Green" -> 1
"Blue" -> 2
else -> throw IllegalArgumentException("Invalid color param value")
}
}
- 异常的使用和Java几乎一样
fun tryCatch() {
val result = try { // do something
} catch (e: ArithmeticException) { throw IllegalStateException(e)
} // Working with result}
- if表达式
fun ifExp(param: Int) {
val result = if (param == 1) { "one"
} else if (param == 2) { "two"
} else { "three"
}
}
- with的用法
class Turtle {
fun penDown() { } fun penUp() {} fun turn(degrees: Double) { } fun forward(pixels: Double) { }
}// 如果你要调用某个对象的多个函数,可以这样用fun withExp() {
val turtle = Turtle()
with(turtle) { //draw a 100 pix square
penDown() for (i in 1..4) {
forward(100.0)
turn(90.0)
}
penUp()
}
}
基本数据类型&可空类型
基本数据类型
- 数字类型
- Kotlin提供下列内置类型,与Java的基本数据类型是对应的
Type BitWidth
Double 64
Float 32
Long 64
Int 32
Short 16
Byte 8
- 字面量
- 十进制: 123 Long类型 以L结尾: 123L 十六进制: 0x0F 二进制: 0b00001011 注意:不支持八进制
- 浮点数默认是Double: 123.5, 123.5e10 Float类型以F或f结尾: 123.5f
- 类型转换
- Int类型不是Long类型的子类型,下面的代码无法通过编译:
fun conversation1() {
val a: Int? = 1 // Int 包装类型 (java.lang.Integer)
// val b: Long? = a // Long 包装类型 (java.lang.Long)
// print(a == b) // 结果是false,因为两者类型不一样
val b: Byte = 1 // OK, literals are checked statically
// val i: Int = b // ERROR
val i: Int = b.toInt() // OK: 显式转换}
所有的数字类型都支持下列转换
代码语言:javascript复制oByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
toChar(): Char
- Char字符类型
- 统计唐诗三百首中古人最喜欢使用的字排行榜
- 布尔值 Boolean
- &&
- ||
- !
- true
- false
- 两个值
- 三种操作
- 数组 Array
- get和set;[ ]
- size属性
- 定义和使用
//数组的部分接口class Array<T> private constructor() {
val size: Int
fun get(index: Int): T
fun set(index: Int, value: T): Unit
fun iterator(): Iterator<T>// ...}
代码语言:javascript复制// 创建数组fun testArray1() {
val asc = Array(5, { i -> (i * i).toString() })
}// Kotlin的数组是不可变的(Java 的数组可变;泛型的协变,逆变)// 不允许将Array<String>赋值给Array<Any>// Kotlin还有ByteArray, ShortArray, IntArray等类型fun testArray2() {
val x: IntArray = intArrayOf(1, 2, 3)
x[0] = x[1] x[2]
}
- 字符串
- 通过[i]访问单个字符
fun testString1(str: String) { for (c in str) {
println(c)
}
}
代码语言:javascript复制* 字符串字面量
代码语言:javascript复制fun testString2() {
val s = "Hello, world!n"
// 或者三引号
val text = """
for (c in "foo")
print(c)
"""}
- 三引号
// 三引号
val text = """
for (c in "foo")
print(c)
"""
- 字符串模板
fun stringTemplate() {
val i = 10
val s = "i = $i" // evaluates to "i = 10"
val x = "abc"
val str = "$x.length is ${x.length}" // "abc.length is 3"
// 可以包含反义字符
val price = "${'$'}9.99"}
可空类型
- ?. 运算符
var a :String? = "a"a.?length
- Elvis 表达式
val a = a ?: ""
Kotlin 函数式编程
Kotlin 面向对象编程
Kotlin 强大的标准库
Kotlin 与 Java互操作
Kotlin Spring Boot 服务端开发
Kotlin JavaScript 开发
Kotlin 多平台开发
Kotlin Native
Kotlin 协程
Kotlin 响应式编程
Kotlin 开发者社区
国内第一Kotlin 开发者社区公众号,主要分享、交流 Kotlin 编程语言、Spring Boot、Android、React.js/Node.js、函数式编程、编程思想等相关主题。