Kotlin
是一门运行在JVM
之上的语言。
它由Jetbrains
创建,而Jetbrains
则是诸多强大的工具(如知名的Java IDE IntelliJ IDEA
)背后的公司。Kotlin
是一门非常简单的语言,其主要目标之一就是提供强大语言的同时又保持简单且精简的语法。
其主要特性如下所示:
Android
来说非常重要。项目所需要的库应该尽可能的小。Android
对于方法数量有严格的限制,Kotlin
只额外增加了大约6000个方法。Kotlin
可与Java
语言无缝通信。这意味着我们可以在Kotlin
代码中使用任何已有的Java
库;因此,即便这门语言还很年轻,但却已经可以使用成百上千的库了。除此之外,Kotlin
代码还可以为Java
代码所用,这意味着我们可以使用这两种语言来构建软件。你可以使用Kotlin
开发新特性,同时使用Java
实现代码基的其他部分。var
声明可变参数,val
声明不可变参数 var
:var
是一个可变变量,这是一个可以通过重新分配来更改为另一个值的变量。这种声明变量的方式和Java
中声明变量的方式一样。 val
: val
是一个只读变量,这种声明变量的方式相当于java
中的final
变量。一个val
创建的时候必须初始化,因为以后不能被改变。 上段代码: var name = "ztz" println(name) name = "yif" println(name) val finalValue = "i v"; println(finalValue); 结果如下: ztz yif ivJava
最大的一个问题就是null
。如果没有对变量或是参数进行null
判断,那么程序当中就有可能抛出大量的NullPointerException
,然而在编码时这些又是难以检测到的。Kotlin
使用了显式的null
,这会强制我们在必要时进行null
检查。Kotlin
是null
安全的。如果一个类型可能为null
,那么我们就需要在类型后面加上一个?
。这样,每次在使用该类型的变量时,我们都需要进行null
检查。比如说,如下代码将无法编译通过:
var artist: Artist? = null?
artist.print()
第2行会显示一个错误,因为没有对变量进行null
检查。我们可以这样做:
if (artist != null) {
? artist.print()?
}
这展示了Kotlin
另一个出色的特性:智能类型转换。如果检查了变量的类型,那就无需在检查作用域中对其进行类型转换。这样,我们现在就可以在if
中将artist
作为Artist
类型的变量了。这对于其他检查也是适用的。还有一种更简单的方式来检查null
,即在调用对象的函数前使用?
。甚至还可以通过Elvis
运算符?
提供另外一种做法:
val name = artist?.name ?: ""
在Java
中,如果想要创建数据类或是POJO
类(只保存了一些状态的类),我们需要创建一个拥有大量字段、getters
与setters
的类,也许还要提供toString
与equals
方法:
public class Artist {
private long id;
private String name;
private String url;
private String mbid;
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 getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getMbid() {
return mbid;
}
public void setMbid(String mbid) {
this.mbid = mbid;
}
@Override public String toString() {
return "Artist{" +
"id=" + id +
", name='" + name + '\'' +
", url='" + url + '\'' +
", mbid='" + mbid + '\'' +
'}';
}
}
在Kotlin
中,上述代码可以写成下面这样:
data class Artist (?
var id: Long,
var name: String,
var url: String,
var mbid: String)
Kotlin
使用属性而非字段。基本上,属性就是字段加上其getter
与setter
。
Kotlin
提供了一些非常棒的互操作特性,这对于Android
开发帮助非常大。其中之一就是拥有单个方法的接口与lambda
表达式之间的映射。这样,下面这个单击监听器:
view.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View) {
toast("Click")?
}
?})
可以写成这样:
view.setOnClickListener { toast("Click") }
此外,getters
与setters
都会自动映射到属性上。这并不会造成性能上的损失,因为字节码实际上只是调用原来的getters
与setters
。如下代码所示:
supportActionBar.title = title
textView.text = title
contactsList.adapter = ContactsYifAdapter()
Lambda
表达式会在极大程度上精简代码,不过重要的是借助于Lambda
表达式,我们可以做到之前无法实现或是实现起来非常麻烦的事情。借助于Lambda
表达式,我们可以以一种更加函数式的方式来思考问题。Lambda
表达式其实就是一种指定类型,并且该类型定义了一个函数的方式。比如说,我们可以像下面这样定义一个变量:
val listener: (View) -> Boolean
该变量可以声明一个函数,它接收一个view
并返回这个函数。我们需要通过闭包的方式来定义函数的行为:
val listener = { view: View -> view is TextView }
上面这个函数会接收一个View
,如果该view
是TextView
的实例,那么它就会返回true
。由于编译器可以推断出类型,因此我们无需指定。还可以更加明确一些:
val listener: (View) -> Boolean = { view -> view is TextView }
借助于Lambda
表达式,我们可以抛弃回调接口的使用。只需设置希望后面会被调用的函数即可:
fun asyncOperation(value: Int, callback: (Boolean) -> Unit) {
...
callback(true)?
}
asyncOperation(5) { result -> println("result: $result") }
还有一种更加简洁的方式,如果函数只接收一个参数,那就可以使用保留字it
:
asyncOperation(5) { println("result: $it") }
lambda`本身作为一等公民,它是有类型的。比如下面这个加法表达式sum的类型为`(Int, Int) -> Int。
val sum: (Int, Int) -> Int = { x, y -> x+y }
一个变量有类型是再自然不过的事。而高阶函数的入参与返回值既然是lambda
,那其类型奇怪一点也很正常。
fun main(args: Array<String>) {
// 自定义高阶函数, lambda 表达式 作为入参
listOf("1", "2", "3", "4").myForEach { println(it) }
// 自定义高阶函数, lambda 表达式 作为返回值
// getLogger()("I'm a Closure")
var logger = getLogger()
logger("I'm a Closure")
}
/**
* 接受一个 lambda 表达式, 作为遍历任务
*/
fun <T> List<T>.myForEach(doTask: (T) -> Unit){
for(item in this)
doTask(item)
}
/**
* 返回一个 lambda 表达式(闭包), 如: 日志输出工具 logger
*/
fun getLogger(): (String) -> Unit{
// return { println(it) }
return fun (it: String){
println(it)
}
}
PS: 看到getLogger()
这种用法,你大概意识到可以像js
那样写闭包了。
顾名思义,它便是指接口可以和抽象类一样,有方法体的默认实现。 我把它归结在语法糖里,是因为java8
中早已有了一模一样的东西,对应的关键字叫default
。
interface A {
fun foo() { println("A") } // 默认实现, 打印"A"
fun bar()
}
interface B {
fun foo() { println("B") }
fun bar() { println("bar") }
}
// 多继承时,显式指定 super<A>.foo() 以去冲突
class D : A, B {
override fun foo() {
super<A>.foo()
super<B>.foo()
}
override fun bar() {
super.bar()
}
}
流式集合操作符,算是很普遍了,任何语言里都有,然而不支持函数式的话,写起来比较臃肿。 如下例子,一些操作符的衔接,使得操作逻辑十分清晰,之后需求变动,比如降序改为升序,也只需改动sortedDescending()
一行,十分灵活。
fun main(args: Array<String>){
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) } // 遍历, 打印
}
// 输出:
100
64
36
拓展这个东西,貌似是以装饰者模式来做的。它的效果是在不改源码的基础上,添加功能。比如我们要在Activity
上加一个toast()
,完全不用卸载基类里。这样简化了很多工作,尤其是对一些已打成jar
包的类。
fun Activity.toast(msg: String) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
}
/**
* @author yif
*
* 函数拓展, 属性拓展
*/
fun main(args: Array<String>) {
val list = listOf("1", "2", "3", "4")
// 函数拓展
list.myForEach { println(it) }
// 属性拓展
println("last: ${list.lastItem}")
}
/**
* 拓展 List 类, 加一个自定义的遍历方法
*/
fun <T> List<T>.myForEach(doTask: (T) -> Unit){
for(item in this)
doTask(item)
}
/**
* 拓展 List 类, 加一个自定义的长度属性
*/
val <T> List<T>.lastItem: T
get() = get(size - 1)
// 输出:
1
2
3
4
last: 4
它把属性的get()
、set()
代理给了一个类,以便可以在get()
和set()
时做一些额外的操作。如:
lazySum
可能需要复杂的运算,我们把它代理给lazy
。 可以看到,只有第一次加载进行了计算,之后都是直接取值,提高了效率。 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: ArrayAnko
是Kotlin
团队开发的一个库,旨在简化Android
开发。其主要目标在于提供一个DSL
,使用Kotlin
代码来声明视图: verticalLayout { val name = editText() button("Say Hello") { onClick { toast("Hello, ${name.text}!") } } } 它还提供了其他一些很有用的特性。比如说,导航到其他Activity
和传值到其它界面: startActivity("id" to res.id, "name" to res.name) startActivity