此文已由作者申国骏授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
在《Kotlin in Action》一书中有归纳了一些Kotlin对比Java的整洁语法如下:
常规语法 | 整洁语法 | 用到的功能 |
---|---|---|
StringUtil.capitalize(s) | s.capitalize() | 扩展函数 |
1.to("one") | 1 to "one" | 中缀函数 infix |
set.add(2) | set += 1 | 运算符重载 |
map.get("key") | map["key"] | get方法约定 |
file.use({f -> f.read}) | file.use { it.read() } | 括号内lambda外移 |
sb.append("a") sb.append("b") | with(sb) { append(“a") append(“b")} | 带接收者的lambda |
整洁语法换句话来说也是Kotlin的一种编程风格,其他约定俗成的整洁Kotlin编程风格可见官方文档 Idioms。非常建议大家看看Idioms这个文档,里面涵盖了非常Kotlin的使用方式,包括:
Kotlin中的function是一等公民,拥有和变量一样的定义以及传参方式,如以下例子:
fun SQLiteDatabase.inTransaction(func: (SQLiteDatabase) -> Unit) {
beginTransaction()
try {
func(this)
setTransactionSuccessful()
} finally {
endTransaction()
}
}
// 调用的时候就可以如下方法进行调用
db.inTransaction {
it.db.delete("users", "first_name = ?", arrayOf("Jake"))
}
lambda表达式可以声明拥有接收者,例如:
val isEven: Int.() -> Boolean = {
this % 2 == 0
}
fun main() {
print(2.isEven())
}
这种带接收者的lambda实际上也是一种方法定义,不过其优先级比扩展方法要低,如果同时有扩展函数(如下)拥有相同名字,则会优先调用扩展方法。
fun Int.isEven(): Boolean {
return this % 2 != 0
}
这几个关键字其实都是Kotlin的特殊方法,他们可以让lambda里面的代码在相同的接收者中运行,避免冗余代码,他们的声明如下:
public inline fun <T, R> T.let(block: (T) -> R): R {
return block(this)
}
public inline fun <R> run(block: () -> R): R {
return block()
}
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
return receiver.block()
}
public inline fun <T> T.apply(block: T.() -> Unit): T {
block()
return this
}
public inline fun <T> T.also(block: (T) -> Unit): T {
block(this)
return this
}
从声明中可以看出他们有以下区别,假设在以下代码中运行
class MyClass {
fun test() {
val str: String = "..."
val result = str.xxx {
print(this) // 接收者this
print(it) // lambda参数it
42 // 返回结果
}
}
}
方法 | 接收者this | lambda参数it | 返回结果 |
---|---|---|---|
let | this@MyClass | String("...") | Int(42) |
run | String("...") | N\A | Int(42) |
with(*) | String("...") | N\A | Int(42) |
apply | String("...") | N\A | String("...") |
also | this@MyClass | String("...") | String("...") |
以下是DSL和API调用方式的区别
// DSL
dependencies {
compile("junit")
compile("guice")
}
// API
project.dependencies.add("compile", "junit")
project.dependencies.add("compile", "guice")
对比下DSL方式更为简洁且易读。通过上述对lambda的介绍可以发现Kotlin可以完美地支持DSL方式编程,只要少量的扩展方法以及lambda定义既可实现以下方式来构建一段html表格
html {
table {
tr (color = getTitleColor()){
this.td {
text("Product")
}
td {
text("Price")
}
td {
text("Popularity")
}
}
val products = getProducts()
for ((index, product) in products.withIndex()) {
tr {
td(color = getCellColor(index, 0)) {
text(product.description)
}
td(color = getCellColor(index, 1)) {
text(product.price)
}
td(color = getCellColor(index, 2)) {
text(product.popularity)
}
}
}
}
}
具体定义如下:
import java.util.ArrayList
open class Tag(val name: String) {
val children: MutableList<Tag> = ArrayList()
val attributes: MutableList<Attribute> = ArrayList()
override fun toString(): String {
return "<$name" +
(if (attributes.isEmpty()) "" else attributes.joinToString(separator = "", prefix = " ")) + ">" +
(if (children.isEmpty()) "" else children.joinToString(separator = "")) +
"</$name>"
}
}
class Attribute(val name : String, val value : String) {
override fun toString() = """$name="$value" """
}
fun <T: Tag> T.set(name: String, value: String?): T {
if (value != null) {
attributes.add(Attribute(name, value))
}
return this
}
fun <T: Tag> Tag.doInit(tag: T, init: T.() -> Unit): T {
tag.init()
children.add(tag)
return tag
}
class Html: Tag("html")
class Table: Tag("table")
class Center: Tag("center")
class TR: Tag("tr")
class TD: Tag("td")
class Text(val text: String): Tag("b") {
override fun toString() = text
}
fun html(init: Html.() -> Unit): Html = Html().apply(init)
fun Html.table(init : Table.() -> Unit) = doInit(Table(), init)
fun Html.center(init : Center.() -> Unit) = doInit(Center(), init)
fun Table.tr(color: String? = null, init : TR.() -> Unit) = doInit(TR(), init).set("bgcolor", color)
fun TR.td(color: String? = null, align : String = "left", init : TD.() -> Unit) = doInit(TD(), init).set("align", align).set("bgcolor", color)
fun Tag.text(s : Any?) = doInit(Text(s.toString()), {})
Kotlin提供对属性代理的支持,可以将属性的get set操作代理到外部执行。代理的好处有三个:
下面来看比较常用的懒初始化例子:
val lazyValue: String by lazy {
println("computed!")
"Hello"
}
fun main() {
println(lazyValue)
println(lazyValue)
}
以上代码会输出
computed!
Hello
Hello
证明懒加载模块只在第一次调用被执行,然后会将得到的值保存起来,后面访问属性将不会继续计算。这也是在Kotlin中实现单例模式的方式。这种懒初始化的过程也是线程同步的,线程同步方式有以下几种:
public enum class LazyThreadSafetyMode {
/**
* 加锁单一线程初始化Lazy实例
*/
SYNCHRONIZED,
/**
* 初始化代码块会被多次调用,但只有首次初始化的值会赋值给Lazy实例
*/
PUBLICATION,
/**
* 没有线程安全,不保证同步,只能在确保单线程环境中使用
*/
NONE,
}
解构是非常实用的Kotlin提供的将一个对象属性分离出来的特性。 内部实现原理是通过声明为componentN()
的操作符重载实现。对Kotlin中的data
类会自动生成component
函数,默认支持解构操作。以下是解构比较实用的一个例子:
for ((key, value) in map) {
// 使用该 key、value 做些事情
}
先占个位,等我看懂了再来补充 :) 先po一个协程和Rxjava的对比吸引下大家
// RxJava
interface RemoteService {
@GET("/trendingshows")
fun trendingShows(): Single<List<Show>>
}
service.trendingShows()
.scheduleOn(schedulers.io)
.subscribe(::onTrendingLoaded, ::onError)
// Coroutine
interface RemoteService {
@GET("/trendingshows")
suspend fun trendingShows(): List<Show>
}
val show = withContext(dispatchers.io) {
service.trendingShows()
}
通过引入import kotlinx.android.synthetic.main.
实现直接获取xml中ui组件。
anko提供了很多工具类,帮助开发者在Android中更好地使用Kotlin。anko提供了以下实用工具:
startActivity(intentFor<SomeOtherActivity>("id" to 5).singleTop())
toast("Hi there!")
info("London is the capital of Great Britain")
bg()
static 与 伴生对象
在Kotlin中并没有static
这个关键字,如果想要实现类似于Java中static
的用法,需要声明伴生对象companion object。使用object
声明的类实际上是一个单例,可以支持直接调用其中的属性与方法。使用了companion
修饰的object实际上是可以放在其他类内部的单例,因此可以实现类似于Java中static
的效果。至于为什么Kotlin要这样设计,原因是Kotlin希望所有属性都是一个类对象,不做差异化处理,这也是为什么Java中的int、long等基本数据类型在Kotlin中也用Int、Long处理的原因。
默认都是final,除非声明为open
在Kotlin中所有方法默认都是禁止覆盖的,这样的好处是规范了接口设计的安全性,仅开放那些确实在设计中希望子类覆盖的方法。
默认是public,多了internal
在Java中,如果不加可见性修饰的话默认是包内可见,Kotlin中默认都是public。同时Kotlin加入了internal关键字,代表着是模块内可见。这个可见性弥补了使用Java进行模块设计的过程中,可见性设计的缺陷。如果要想在Java中实现仅开放某些方法给外部模块使用,但是这些方法又能在内部模块自由调用,那只能是把这些方法都放到一个包内,显然是一个很不好的包结果设计。Kotlininternal
关键字可以完美解决这个问题。要想在Java调用的时候完全隐蔽Kotlin的方法,可以加上@JvmSynthetic
。
extends
和super
来区分泛型中生产者和消费者,俗称PEST,在Kotlin中对应的是out
和in
。同时Java与Kotlin都会对泛型进行运行时擦除,Kotlin不一样的是可以对inline
方法使用reified
关键字来提供运行时类型。本地方法
由于在Kotlin语言中方法是一等公民,因此可以声明局部生命周期的本地方法,如下例子:
fun dfs(graph: Graph) {
val visited = HashSet<Vertex>()
fun dfs(current: Vertex) {
if (!visited.add(current)) return
for (v in current.neighbors)
dfs(v)
}
dfs(graph.vertices[0])
}
Kotlin online try
Kotlin官方文档
kotlin in action
Android Development with kotlin
Kotlin for Android Developers
在Java项目中引入kotlin在大多数情况下都是无痛的,且可以马上带给我们不一样的快捷高效体验。如果硬是要说出一点Kotlin的问题,我觉得会有几个:
Kotlin是一门优秀的语言,值得大家尝试。
更多网易技术、产品、运营经验分享请点击。