[TOC]
什么是kotlin 语言?
Kotlin 是一种在 Java 虚拟机JVM上运行的静态类型编程语言,被称之为 Android 世界的Swift,由 JetBrains 设计开发并开源。
Kotlin 可以编译成Java字节码,也可以编译成 JavaScript,方便在没有 JVM 的设备上运行。
在Google I/O 2017中,Google 宣布 Kotlin 成为 Android 官方开发语言。
kotlin 能做什么?
- 开发Android App
-
服务端开发
既然Kotlin是基于JVM的编程语言,那么自然而然就可以使用所有基于JVM的服务端框架。下面是几个Kotlin官方文档推荐的框架。
-
Spring:一种开源框架,是为了解决企业应用程序开发复杂性问题而创建的。从Spring 5开始,Spring就已经支持Kotlin的新特性了,并可以使用Spring在线生成器(https://start.spring.io)生成基于Kotlin的工程,在Spring boot 2.0中Kotlin也将成为第一类开发语言。。
-
Vert.x:用于建立基于JVM的响应式Web应用的框架。
-
Ktor:由JetBrains发布的一款基于Kotlin的本地Web框架
-
kotlinx.html:是一种DSL(领域专用语言),用于在Web应用中生成HTML。Kotlin服务端框架和kotlinx.html的关系就像JSP和FreeMarker的关系一样,FreeMarker是基于Java的模板引擎。 使用FreeMarker,可以不依赖于HTML或其他技术,可以根据需要生成HTML或其他东西,也就是一种与具体渲染技术无关的技术。
-
-
以JavaScript方式运行 kotlin.js
Kotlin提供了生成JavaScript源代码的能力,也就是将Kotlin代码直接转换为JavaScript代码。目前,支持ECMAScript 5.1标准,未来会支持ECMAScript 6。注意,如果将Kotlin代码转换为JavaScript代码,在Kotlin代码中只能包含Kotlin标准库,不能包含任何JDK API以及任何第三方的Java Library API,任何不属于Kotlin本身(Kotlin语句和标准库)的部分在转换的过程中将被忽略。
-
Kotlin Script 脚本运行 可以像python 和 scala 一样写脚本运行,想想都兴奋呢!
- Kotlin Native
不是 Jni 的概念,它不仅仅是要与底层代码比如 C、C++ 交互,而且还要绕过 Jvm 直接编译成机器码供系统运行。也就是说,Kotlin 准备丢掉 Java 这根拐杖了!当然详细的内容我就不懂了,不敢吹牛了!
为什么要用kotlin
简洁
比如模型对象类:
data class User(var id : Long, var name: String, var email: String, var age Int)
或者
data class User(){
var id : Long = 0
var name: String?= null
var email: String?= null
var age = 0
}
这些内容省去了 getters、 setters、 equals()、 hashCode()、 toString() 以及 copy() 的内容。
看下同样的内容java怎么写的。
public class User {
private long id;
private String name;
private String email;
private String age;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
函数式编程 和 lambda表达式
例 1
//高阶函数变量
var OnOptionListener : (()->Unit) ?= null
//高阶函数接口定义
fun setOnConfirmClickListener(listener : (()->Unit)){
this.OnOptionListener = listener
}
//接口的使用
exitDialog.setOnConfirmClickListener {
//dosomething
}
看看java 的写法
//变量定义
private OnOptionListener listener;
//设置接口监听
public void setOnConfirmClickListener(OnOptionListener listener) {
this.listener = listener;
}
//定义接口
public interface OnOptionListener{
void onOption();
}
commonDialog.setOnConfirmClickListener(new AbsCommonDialog.OnOptionListener() {
@Override
public void onOption() {
//dosomethin
}
});
例 2
Handler().postDelayed({
//dusomething
}, 1000)
//或一行表示
Handler().postDelayed({ /*dusomething*/ }, 1000)
看看java 的写法
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//dosomething
}
}, 1000);
安全
彻底告别那些烦人的 NullPointerException——著名的十亿美金的错误。
补充插播一个改自 StackOverflow 帖子的段子:
杯具啊!我们公司有个职工姓 Null,当用他的姓氏做查询词时,把所有员工查询应用给弄崩溃了! 我该肿么办?
在 1965 年有人提出了这个计算机科学中最糟糕的错误,该错误比 Windows 的反斜线更加丑陋、比 === 更加怪异、比 PHP 更加常见、比 CORS 更加不幸、比 Java 泛型更加令人失望、比 XMLHttpRequest 更加反复无常、比 C 预处理器更加难以理解、比 MongoDB 更加容易出现碎片问题、比 UTF-16 更加令人遗憾。
“我把 Null 引用称为自己的十亿美元错误。它的发明是在1965 年,那时我用一个面向对象语言( ALGOL W )设计了第一个全面的引用类型系统。我的目的是确保所有引用的使用都是绝对安全的,编译器会自动进行检查。但是我未能抵御住诱惑,加入了Null引用,仅仅是因为实现起来非常容易。它导致了数不清的错误、漏洞和系统崩溃,可能在之后 40 年中造成了十亿美元的损失。近年来,大家开始使用各种程序分析程序,比如微软的 PREfix 和 PREfast 来检查引用,如果存在为非 Null 的风险时就提出警告。更新的程序设计语言比如 Spec# 已经引入了非 Null 引用的声明。这正是我在1965年拒绝的解决方案。” —— 《Null References: The Billion Dollar Mistake》托尼·霍尔(Tony Hoare),图灵奖得主
进入正题
Kotlin 可以保护你避免对可空类型的误操作 空的类型在编译时就编译不通过,使用IDEA的话会自动报红。 并且如果你检查类型是正确的,编译器会为你做自动类型转换,如下:
fun calculateTotal(obj: Any) {
if (obj is Invoice)
obj.calculateTotal()
}
一般可空的变量只需要在后面增加?即可放在空指针。
btn?.text = "按钮文字"
user?.let{
tvName.text = it.name
tvAge.text = it.age.toString()
...
}
互操作性
使用 JVM 上的任何现有库,因为有 100% 的兼容性,包括 SAM 支持,任何有java的地方就可以有kotlin。
import io.reactivex.Flowable
import io.reactivex.schedulers.Schedulers
Flowable
.fromCallable {
Thread.sleep(1000) // 模仿高开销的计算
"Done"
}
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.single())
.subscribe(::println, Throwable::printStackTrace)
无论是 JVM 还是 JavaScript 目标平台,都可用 Kotlin 写代码然后部署到你想要的地方
import kotlin.browser.window
fun onLoad() {
window.document.body!!.innerHTML += "<br/>Hello, Kotlin!"
}
工具友好性
IntelliJ 平台下的IDEA (包含AndroidStudio、PHPStrom、PyCharm等) 提供了很多智能提示不全等功能,java 一键转成kotlin,包括你复制java代码粘贴到kotlin文件里,也可以自动转换。
语法糖
遍历
for (l in list) {
println("$l")
}
for ((k, v) in map) {
println("$k -> $v")
}
for (i in 1..100) { ... } // 闭区间:包含100
for (i in 1 until 100) { ... } // 不包含100
for (x in 2..10 step 2) { ... } // 按照步长为2增长
for (x in 10 downTo 1) { ... } //
if (x in 1..10) { ... } //判断x是否在1到10这个区间内
val list = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
list.filter { it%2==0 } // 取偶数
.map{ it*it } // 平方
.sortedDescending() // 降序排序
.take(3) // 取前 3 个
.forEach { println(it) } // 遍历, 打印
类扩展方法
//例 1
fun String.myToLowerCase(): String {
return this.toLowerCase()
}
fun main(args: Array<String>) {
println("this is test".myToLowerCase())
}
//例 2
//冒泡排序的切换元素方法
fun <T> MutableList<T>.swap(index1: Int, index2: Int) {
val tmp = this[index1]
this[index1] = this[index2]
this[index2] = tmp
}
val list = arrayListOf(1, 3, 4, 6, 2, 7, 9, 8, 5, 0)
//原来的冒泡排序
for(i in 0 until list.size){
for(j in i until list.size){
if(list[i] > list[j]){
val tmp = list[i]
list[i] = list[j]
list[j] = tmp
}
}
}
//改进后
for(i in 0 until list.size){
for(j in i until list.size){
if(list[i] > list[j]){
list.swap(i, j)
}
}
}
//单独切换
list.swap(0, 1)
with 语法
val tvName = TextView(this)
with(tvName){
setText("张三")
setSingleLine()
setTextColor(Color.WHITE)
}
对比java的写法
TextView tvName = new TextView(this);
tvName.setText("张三");
tvName.setSingleLine();
tvName.setTextColor(Color.WHITE);
高阶函数
//例 1
//高阶函数接口定义
fun setOnConfirmClickListener(listener : (()->Unit)){
this.OnOptionListener = listener
}
fun setOnConfirmClickListener(listener : ((text : String)->Unit)){
this.OnOptionListener = listener
}
//没有参数和只有一个参数时,调用的时候都可以把参数省略
exitDialog.setOnConfirmClickListener {
//dosomething
}
fun setOnConfirmClickListener(listener : ((id: Long, text : String)->Unit)){
this.OnOptionListener = listener
}
//多个参数时
exitDialog.setOnConfirmClickListener { id, text->
println("id 是 $id, 文字是$text")
}
//例 2
val sum: (Int, Int) -> Int = { x, y -> x+y }
val result = sum(1,2)
// 例3
/**
* 遍历获取路径下的文件
* @param dir 上层路径
* @param dirs 存储文件的列表
* @param condition 条件
*/
fun foreachFiles(dir: File, condition: ((file: File) -> Boolean)? = null) : ArrayList<String>{
val dirs = arrayListOf<String>()
dir.listFiles()?.forEach {
if (it.isDirectory) {
dirs.addAll(foreachFiles(it, condition))
} else {
if (condition != null) {
if (condition(it)) {
dirs.add(it.absolutePath)
}
} else {
dirs.add(it.absolutePath)
}
}
}
return dirs
}
val kotlinSrcDir = File("D:\\java\\workspace\\javatest")
val allFiles = foreachFiles(kotlinSrcDir)
val allKotlinFiles = foreachFiles(kotlinSrcDir, { it.name.endsWith(".kt") })
val allJavaFiles = foreachFiles(kotlinSrcDir, { it.name.endsWith(".java") })
val readmeFiles = foreachFiles(kotlinSrcDir, { it.name == "README.md" })
val allSmallFiles = foreachFiles(kotlinSrcDir, { it.length() < 1024*1024 })
println(allFiles)
println(allKotlinFiles)
println(allJavaFiles)
println(readmeFiles)
println(allSmallFiles)
例3的写法很特别,循环例遍历路径下的所有文件,而条件却写在了参数上,那么调用者可以随意的写入条件,不同的条件就得到不同的结果,这就得到了最大程度的私人定制。
默认参数 + 命名参数
结合默认参数,命名参数消除了构造者。
java 代码
public static void main(String[] args){
TestClass testClass = new TestClass();
testClass.test();
testClass.test("张三");
testClass.test("张三", 18);
testClass.test("张三", 18, 1.72f);
testClass.test("张三", 18, 1.72f, "http://www.xx.com/aa.jpg");
testClass.test("张三", "http://www.xx.com/aa.jpg");
}
public void test(){
// dosomething
}
public void test(String name){
// dosomething
}
public void test(String name, int age){
// dosomething
}
public void test(String name, int age, float height){
// dosomething
}
public void test(String name, int age, float height, String imagePath){
// dosomething
}
public void test(String name, String imagePath){
// dosomething
}
kotlin 代码
fun main(args: Array<String>){
test()
test("张三")
test("张三", 18)
test("张三", 18, 1.72f)
test("张三", 18, 1.72f, "http://www.xx.com/aa.jpg")
test("张三", imagePath = "http://www.xx.com/aa.jpg")
}
fun test(name : String ?= null, age : Int = 0, height : Float = 0f, imagePath : String ?= null){
// dosomething
}
懒加载
val lazySum: Int by lazy {
println("begin compute lazySum ...")
var sum = 0
for (i in 0..100)
sum += i
println("lazySum computed!n")
sum // 返回计算结果
}
fun main(args: Array<String>) {
println(lazySum)
println(lazySum)
}
// 输出:
begin compute lazySum ...
lazySum computed!
5050
5050