Kotlin精髓

前语

从谨慎地在项目中引进kotlin到悉数转为kotlin开发咱们用了大概半年的时刻。这中心阅历了从在一个小功用中测验运用到完全运用kotlin完成了大版别开发的进程。运用办法也从仅仅地用java风格写kotlin代码,慢慢地变成运用kotlin风格去编写代码。 转载请注明来源「申国骏」

到目前为止,kotlin的引进至少没有给咱们带来不必要的麻烦,在慢慢品尝kotlin语法糖的进程中,咱们领会到了能给开发者真实带来好处的一些特性。本文就是对这些咱们认为是精华的一些特性的进行总结,期望能给还在犹疑是否要开端学习kotlin或许刚开端编写kotlin可是不知道该如何运用kotlin的人们先一睹kotlin的高雅风貌。

Kotlin规划哲学

KotlinConf 2018 – Conference Opening Keynote by Andrey Breslav 上讲的Kotlin规划理念:

Kotlin精髓
Kotlin具有强大的IDE厂商Intellij和Google的支撑,确保了其务实、简练、安全和与JAVA互操作的良好规划理念。

其间务实表明了Kotlin并没有首创一些当时没有或群众不太熟悉的规划理念,而是吸收了许多其他言语的精华,而且供给强大的IDE支撑,能真实便利开发者运用到实践项目之中。

简练首要指的是Kotlin支撑隐藏例如getter、setter等Java样板代码,而且有许多的标准库以及灵敏的重载和扩展机制,来使代码变得愈加直观和简练。

安全首要是说空值安全的控制以及类型自动检测,帮助削减NullPointerException以及ClassCastException。

与Java互操作认为这能够与Java彼此调用、混合调试以及同步重构,一起支撑Java到kotlin代码的自动转化。

空值安全

Kotlin类型分为可空和非可空,赋值null到非可空类型会编译犯错

fun main() {
    var a: String = "abc"
    a = null // compilation error
    var b: String? = "abc" 
    b = null // ok
}

对空的操作有以下这些

Kotlin精髓

运用安全调用运算符 ?: 能够防止Java中许多的空值判别。以下是一个比照的比如:

// 用Java完成
public void sendMessageToClient(
    @Nullable Client client,
    @Nullable String message,
    @NotNull Mailer mailer
) {
    if (client == null || message == null) return;
    PersonalInfo personalInfo = client.getPersonalInfo();
    if (personalInfo == null) return;
    String email = personalInfo.getEmail();
    if (email == null) return;
    mailer.sendMessage(email, message);
}
// 用Kotlin完成
fun sendMessageToClient(
    client: Client?, 
    message: String?, 
    mailer: Mailer
){
    val email = client?.personalInfo?.email
    if (email != null && message != null) {
        mailer.sendMessage(email, message)
    }
}

扩展

扩展函数

扩展函数是Kotlin精华特点之一,能够给别人的类添加办法或许特点,使得办法调用愈加天然和直观。经过扩展函数的特性,Kotlin内置了许多的辅佐扩展办法,十分有用。下面咱们经过这个比如看一下

fun main() {
    val list = arrayListOf<Int>(1, 5, 3, 7, 9, 0)
    println(list.sortedDescending())
    println(list.joinToString(
        separator = " | ",
        prefix = "(",
        postfix = ")"
    ) {
        val result = it + 1
        result.toString()
    })
}

其间sortedDescending以及joinToString都是Kotlin内置的扩展办法。 上述的函数会输出

Kotlin精髓

Kotlin内部的完成如下

public fun <T : Comparable<T>> Iterable<T>.sortedDescending(): List<T> {
    return sortedWith(reverseOrder())
}
public fun <T> Iterable<T>.joinToString(separator: CharSequence = ", ", prefix: CharSequence = "", postfix: CharSequence = "", limit: Int = -1, truncated: CharSequence = "...", transform: ((T) -> CharSequence)? = null): String {
    return joinTo(StringBuilder(), separator, prefix, postfix, limit, truncated, transform).toString()
}

可见sortedDescendingjoinToString都是对Iterable<T>类目标的一个扩展办法。

咱们也能够自己完成一个自界说的扩展函数如下:

fun Int.largerThen(other: Int): Boolean {
    return this > other
}
fun main() {
    println(2.largerThen(1))
}

上述代码输出为true
经过扩展函数咱们以十分直观的办法,将某个类目标的东西类直接运用该类经过"."办法调用。
当然扩展函数是一种静态的完成办法,不会对本来类目标的办法进行掩盖,也不会有正常函数的子类办法掩盖父类办法现象。

扩展特点

扩展特点与扩展函数类似,也是能够直接给类目标添加一个特点。例如:

var StringBuilder.lastChar: Char
        get() = get(length -1)
        set(value: Char) {
            this.setCharAt(length -1, value)
        }
fun main() {
    val sb = StringBuilder("kotlin")
    println(sb.lastChar)
    sb.lastChar = '!'
    println(sb.lastChar)
}

无论是扩展函数仍是扩展特点,都是对Java代码中utils办法很好的改变,能够防止多处类似功用的util界说以及使得调用更为直观。

调集

经过扩展的办法,Kotlin对调集类供给了十分丰富且有用的许多东西,只有你想不到,没有你做不到。下面咱们经过 Kotlin Koans 上的一个比如来阐明一下:

data class Shop(val name: String, val customers: List<Customer>)
data class Customer(val name: String, val city: City, val orders: List<Order>) {
    override fun toString() = "$name from ${city.name}"
}
data class Order(val products: List<Product>, val isDelivered: Boolean)
data class Product(val name: String, val price: Double) {
    override fun toString() = "'$name' for $price"
}
data class City(val name: String) {
    override fun toString() = name
}

以上是数据结构的界说,咱们有一个超市,超市有许多顾客,每个顾客有许多笔订单,订单对应着一定数量的产品。下面咱们来经过调集的操作来完成以下使命。

操作符 作用
filter 将调集里的元素过滤,并回来过滤后的元素
map 将调集里的元素一一对应转化为另一个元素
“`
// 回来商铺中顾客来自的城市列表
fun Shop.getCitiesCustomersAreFrom(): Set = customers.map { it.city }.toSet()

// 回来住在给定城市的一切顾客 fun Shop.getCustomersFrom(city: City): List = customers.filter { it.city == city }

操作符 | 作用
---|---
all | 判别调集中的一切元素是否满意某个条件,都满意回来true
any | 判别调集中是否有元素满意某个条件,有则回来true
count | 回来调集中满意某个条件的元素数量
find |查找调集中满意某个条件的一个元素,不存在则回来null

// 假如超市中一切顾客都来自于给定城市,则回来true fun Shop.checkAllCustomersAreFrom(city: City): Boolean = customers.all { it.city == city }

// 假如超市中有某个顾客来自于给定城市,则回来true fun Shop.hasCustomerFrom(city: City): Boolean = customers.any{ it.city == city}

// 回来来自于某个城市的一切顾客数量 fun Shop.countCustomersFrom(city: City): Int = customers.count { it.city == city }

// 回来一个住在给定城市的顾客,若无回来null fun Shop.findAnyCustomerFrom(city: City): Customer? = customers.find { it.city == city }

操作符 | 作用
---|---
flatMap | 将调集的元素转化为别的的元素(非一一对应)

// 回来一切该顾客购买过的产品调集 fun Customer.getOrderedProducts(): Set = orders.flatMap { it.products }.toSet()

// 回来超市中至少有一名顾客购买过的产品列表 fun Shop.getAllOrderedProducts(): Set = customers.flatMap { it.getOrderedProducts() }.toSet()

操作符 | 作用
---|---
max | 回来调集中以某个条件排序的最大的元素
min | 回来调集中以某个条件排序的最小的元素

// 回来商铺中购买订单次数最多的用户 fun Shop.getCustomerWithMaximumNumberOfOrders(): Customer? = customers.maxBy { it.orders.size }

// 回来顾所购买过的最贵的产品 fun Customer.getMostExpensiveOrderedProduct(): Product? = orders.flatMap { it.products }.maxBy { it.price }

操作符 | 作用
---|---
sort | 根据某个条件对调集元素进行排序
sum | 对调集中的元素依照某种规矩进行相加
groupBy | 对调集中的元素依照某种规矩进行组合

// 依照购买订单数量升序回来商铺的顾客 fun Shop.getCustomersSortedByNumberOfOrders(): List = customers.sortedBy { it.orders.size }

// 回来顾客在商铺中购买的一切订单价格总和 fun Customer.getTotalOrderPrice(): Double = orders.flatMap { it.products }.sumByDouble { it.price }

// 回来商铺中居住城市与顾客的映射 fun Shop.groupCustomersByCity(): Map<City, List> = customers.groupBy { it.city }

操作符 | 作用
---|---
partition | 根据某种规矩将调集中的元素分为两组
fold | 对调集的元素依照某个逻辑进行一一累计

// 回来商铺中未送到订单比送达订单要多的顾客列表 fun Shop.getCustomersWithMoreUndeliveredOrdersThanDelivered(): Set = customers.filter { val (delivered, undelivered) = it.orders.partition { it.isDelivered } undelivered.size > delivered.size }.toSet()

// 对一切顾客购买过的产品取交集,回来一切顾客都购买过的产品列表 fun Shop.getSetOfProductsOrderedByEveryCustomer(): Set { val allProduct = customers.flatMap { it.orders }.flatMap { it.products }.toSet()

return customers.fold(allProduct) { orderedByAll, customer ->
    orderedByAll.intersect(customer.orders.flatMap { it.products })
}

}

归纳运用:

// 回来顾客一切送达产品中最贵的产品 fun Customer.getMostExpensiveDeliveredProduct(): Product? { return orders.filter { it.isDelivered }.flatMap { it.products }.maxBy { it.price } }

// 回来商铺中某件产品的购买次数 fun Shop.getNumberOfTimesProductWasOrdered(product: Product): Int { return customers.flatMap { it.orders }.flatMap { it.products }.count{it == product} }

Kotlin对调集供给了简直你能想到的一切操作,经过对这些操作的组合削减调集操作的复杂度,进步可读性。以下是Java和Kotln对调集操作的比照
```java
// 用Java完成
public Collection<String> doSomethingStrangeWithCollection(
        Collection<String> collection
) {
    Map<Integer, List<String>> groupsByLength = Maps.newHashMap();
    for (String s : collection) {
        List<String> strings = groupsByLength.get(s.length());
        if (strings == null) {
            strings = Lists.newArrayList();
            groupsByLength.put(s.length(), strings);
        }
        strings.add(s);
    }
    int maximumSizeOfGroup = 0;
    for (List<String> group : groupsByLength.values()) {
        if (group.size() > maximumSizeOfGroup) {
            maximumSizeOfGroup = group.size();
        }
    }
    for (List<String> group : groupsByLength.values()) {
        if (group.size() == maximumSizeOfGroup) {
            return group;
        }
    }
    return null;
}
// 用Kotlin完成
fun doSomethingStrangeWithCollection(collection: Collection<String>): Collection<String>? {
    val groupsByLength = collection.groupBy { s -> s.length }
    val maximumSizeOfGroup = groupsByLength.values.map { group -> group.size }.max()
    return groupsByLength.values.firstOrNull { group -> group.size == maximumSizeOfGroup }
}

运算符

运算符重载

仍是举 Kotlin Koans 上的运算符重载比如。假定咱们需求完成以下功用:

enum class TimeInterval { DAY, WEEK, YEAR }
data class MyDate(val year: Int, val month: Int, val dayOfMonth: Int) : Comparable<MyDate> {
    override fun compareTo(other: MyDate): Int {
        if (year != other.year) return year - other.year
        if (month != other.month) return month - other.month
        return dayOfMonth - other.dayOfMonth
    }
    override fun toString(): String {
        return "$year/$month/$dayOfMonth"
    }
}
fun main() {
    val first = MyDate(2018, 10, 30)
    val last = MyDate(2018, 11, 1)
    for (date in first..last) {
        println(date)
    }
    println()
    println(first + DAY)
    println()
    println(first + DAY * 2 + YEAR * 2)
}

输出为以下:

Kotlin精髓

只需完成以下运算符重载既可:

operator fun MyDate.rangeTo(other: MyDate): DateRange = DateRange(this, other)
operator fun MyDate.plus(timeInterval: TimeInterval): MyDate = this.addTimeIntervals(timeInterval, 1)
operator fun TimeInterval.times(num: Int): RepeatTimeInterval = RepeatTimeInterval(this, num)
operator fun MyDate.plus(repeatTimeInterval: RepeatTimeInterval): MyDate =
    this.addTimeIntervals(repeatTimeInterval.timeInterval, repeatTimeInterval.num)
class RepeatTimeInterval(val timeInterval: TimeInterval, val num: Int)
class DateRange(override val start: MyDate, override val endInclusive: MyDate) : ClosedRange<MyDate>, Iterable<MyDate> {
    override fun iterator(): Iterator<MyDate> = DateIterator(start, endInclusive)
}
class DateIterator(first: MyDate, private val last: MyDate) : Iterator<MyDate> {
    private var current = first
    override fun hasNext(): Boolean {
        return current <= last
    }
    override fun next(): MyDate {
        val result = current
        current = current.nextDay()
        return result
    }
}
fun MyDate.nextDay(): MyDate = this.addTimeIntervals(DAY, 1)
fun MyDate.addTimeIntervals(timeInterval: TimeInterval, number: Int): MyDate {
    val c = Calendar.getInstance()
    c.set(year + if (timeInterval == TimeInterval.YEAR) number else 0, month - 1, dayOfMonth)
    var timeInMillis = c.timeInMillis
    val millisecondsInADay = 24 * 60 * 60 * 1000L
    timeInMillis += number * when (timeInterval) {
        TimeInterval.DAY -> millisecondsInADay
        TimeInterval.WEEK -> 7 * millisecondsInADay
        TimeInterval.YEAR -> 0L
    }
    val result = Calendar.getInstance()
    result.timeInMillis = timeInMillis
    return MyDate(result.get(Calendar.YEAR), result.get(Calendar.MONTH) + 1, result.get(Calendar.DATE))
}

Kotlin支撑运用指定的扩展函数来完成运算符的重载,运算符对应的办法名详细拜见官方文档 Operator overloading

infix

符号为infix的办法,能够类似于二元运算符运用,举个比如

infix fun Int.plus(other: Int): Int {
    return this + other
}
fun main() {
    // 成果会输出7
    println(3 plus 4)
}

infix办法履行的优先级低于管用运算符、类型转化type case以及rangTo运算符,可是高于布尔、is、in check等其他运算符。
运用infix的扩展函数能够完成自界说的二元运算符号。

整齐Kotlin风格

在《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的运用办法,包括:
  • 运用默许参数代替办法重载
  • String模板(在Android中是否推荐仍值得商讨)
  • lambda运用it代替传入值
  • 运用下标办法拜访map
  • 懒初始化特点
  • 运用rangs规模遍历
  • if when表达式回来值
  • 等等

办法参数

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表达式

lambda表达式能够声明具有接收者,例如:

val isEven: Int.() -> Boolean = {
    this % 2 == 0
}
fun main() {
    print(2.isEven())
}

这种带接收者的lambda实践上也是一种办法界说,不过其优先级比扩展办法要低,假如一起有扩展函数(如下)具有相同名字,则会优先调用扩展办法。

fun Int.isEven(): Boolean {
    return this % 2 != 0
}

let run with apply also

这几个关键字其实都是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构建

以下是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 做些工作
}

协程Coroutine

先占个位,等我看懂了再来补充 :) 先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()
}

在Android中运用

findViewById

经过引进import kotlinx.android.synthetic.main.完成直接获取xml中ui组件。

anko

anko供给了许多东西类,帮助开发者在Android中更好地运用Kotlin。anko供给了以下有用东西:

  • 方便Intent:startActivity(intentFor<SomeOtherActivity>("id" to 5).singleTop())
  • 方便toast、dialog:toast("Hi there!")
  • 方便log:info("London is the capital of Great Britain")
  • 方便协程:bg()
  • layout DSL构建
  • 等等

ktx

android-ktx 供给了一系列Andrdoid办法的简练完成。

与Java不太相同的地方

  • 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

  • 泛型
    Java中运用extendssuper来区分泛型中生产者和消费者,俗称PEST,在Kotlin中对应的是outin。一起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加入会添加办法数以及不多的代码体积,这在大多数情况下不会产生太大的问题
  • 写法太灵敏,较难一致。由于Kotlin答应程序员选择传统的Java风味或许Kotlin风味来编写代码,这种灵敏性或许导致混合风味的代码出现,且较难一致。
  • 过多的大括号层级嵌套。这是由于lambda以及办法参数带来的,其初衷是期望咱们能够用DSL的代码风格,假如没把握DSL办法的话或许会写出比较丑陋的多层级嵌套Java风味代码,影响代码可读性。

最后

Kotlin是一门优异的言语,值得咱们测验。