2分钟了解kotlin:优点,缺点,设计原则

1. 优点

kotlin提供了很多好的特性,比如:null安全检测,属性访问,unchecked exceptions, publication有更详细的介绍。

1.1 java 自动转换成kotlin

kotlin项目开发总结有介绍如何使用,这个是有JetBrains提供的,目前已经集成到了IDEA/AS, 虽然它不是很完美(我的上篇博客也已经介绍到了),但是对比重新用Kotlin开发一个完全一样功能的类来说,可以节省不少时间。

1.2 lateinit Delegates.notNull and lazy

kotlin的null安全监测是个非常好的特性,但是也有有个问题,比如:

var aMap: AMap? = null

onCreate(){
    aMap = ...
    aMap!!.projection
    ....
}

虽然我们可以保证aMap已经在onCreate方法定义了,但是因为我们在定义aMap的时候是AMap?类型,所以在以后使用的都必须使用aMap!!来告诉编译器aMap不为null,会显得非常麻烦。幸运的是kotlin已经帮我们考虑到了应对方法

lateinit var name: String
var age: Int by Delegates.notNull<Int>()

通过上面两种方法,可以在定义的时候不提供初始化,可以延迟到需要的时候,但是就像我在kotlin项目开发总结说的,需要慎用,除非你能确保不会在后面遗漏掉重新赋值,否则会在运行时报空指针错误,这就浪费了kotlin的null安全监测这个非常好的特性。

lazy代理是个很好用的东西,就像下面这样的定义

val imm: InputMethodManager by lazy { 
    getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager 
}

lazy后面跟着的block只会在你第一次read这个imm的时候调用,以后读取imm会直接返回block保存的值。在需要追求启动速度的APP可以很好的使用。

1.3 扩展后的collections

kotlin提供了很多对collections和iterables的扩展,具体可以看下
我写的分析和使用collections。

1.4 Named 和 默认的函数参数

Named 函数参数和默认函数参数非常简单,但有时候可以帮我们省掉很多代码。特别是当构造函数有超过4个以上的参数时,可以指定默认几个默认参数,在调用的时候可以只提供一个参数。比如

class Presenter(
        val okhttp: OkHttp = productionOkHttp(),
        val picasso: Picasso = productionPicassoInstance()
) {...}

在调用的时候,我们不提供参数,那么默认参数会被使用

var prensenter = Presenter()

2. 缺点

虽然kotlin非常强大,但它毕竟不是完美的。它也会有一些可能未来会解决的缺点。

2.1 编译速度慢

大家在使用kotlin开发项目的时候应该有注意到了,主要还是因为kotlin会自动自动生成更多的代码,比如为属性生成get/set, 对比java会存在更多的方法数量。https://youtrack.jetbrains.com/issue/KT-6246,这篇博客有做分析

2.2 annotation processing的问题

我目前开发的项目使用了dagger,permissionsdispatcher,deeplinkdispatch,databinding,都需要使用kapt来做annotation proccessing,但是我已经碰到了好多次,kapt报的很奇怪的错误,有一次,我重构dagger的module时,报了一个蛋疼的问题,我以为我是dagger没用好,找了很多资料对比都没有解决,花了大概一天的时间,后面没办法只能clean project,但是奇怪的问题还在,后面我让application继承了DaggerApplication,clean下就可以。后面还是碰到好几次这样的问题,都是clean之后build就可以了,这个问题应该不只我一个人碰到,我想可能是kapt的一个bug, 我还是很相信google/JetBrains, 所以还是继续坚持使用kotlin开发项目。

2.3 没有命名空间

kotlin允许定义top-level的函数和属性,比如

//demo.kt
var a = "aa"
fun printlnA(){
    println(a)
}

这可能是一个非常好的特性,但是也会有问题,比如在项目下面有两个甚至更多的printlnA方法,那么在调用的时候(特别是在阅读代码),很难区分方法来自哪个地方,除非你F3跳转到声明处。为了避免这个问题,你可以选择把printlnA方法移到一个object中,比如

object PrintlnHelper{
    fun printlnA(){
        println(a)
    }
}

让我们来看下kotlin和java的调用方式

//kotlin
PrintlnHelper.printlnA()
//java
PrintlnHelper.INSTANCE.printlnA()

为了避免如上在Java中调用的怪怪的。。,可以使用@JvmStatic注解该方法

object PrintlnHelper{
    @JvmStatic
    fun printlnA(){
        println(a)
    }
}

通过上面的分析,对于有代码洁癖或者同时用Java和kotlin开发的项目,也是不够完美的,这个缺点是可以避免的,但是你需要花费一点时间去熟悉Kotlin

2.4 没有静态修饰语

还是像上面的问题,如果项目同时存在kotlin和Java,而且Java需要调用kotlin代码,看代码分析问题

//Java
public class View {
    public static final int VISIBLE = 0x00000000;
    public static final int INVISIBLE = 0x00000004;
    public static View inflate(Context context, int resource) {...}
}

使用kotlin使用同样的功能,如下:

class View {
    companion object {
        @JvmField 
        val VISIBLE: Int = 0x00000000
        @JvmField 
        val INVISIBLE: Int = 0x00000004
        @JvmStatic
        fun inflate(context: Context, resource: Int) {...}
    }
}

下面是有@JvmField和没有的Java调用方式

// With annotations:
View.VISIBLE;
//Without annotations:
View.Companion.getVISIBLE();

其实这个问题也不是很严重的,但是对比Java还是多了个@JvmField, 但是kotlin新手或者容易忘记,使用@JvmField,这个缺点可以通过熟悉kotlin来避免

2.5 Java自动转换成kotlin带来的问题

Java自动转换成kotlin是个非常好的特性,但是也会带来问题。Javadoc原来的结构会被破坏掉,静态field和方法会转换成companion object中的普通声明,如果有Java代码调用这个类,就会出现点问题,除非你后面手动给这个转换后的companion object 添加上@JvmField和@JvmStatic。这是个不小的隐患,需要特别注意下。

2.6 会增加方法数量

过多的方法数量会导致编译速度变慢。kotlin通过闭包,内联函数等可以显著减少代码的总行数,但它可能也会增加编译后的方法数量。对于Android项目来说这肯定是一个不小的缺点。有很多原因会导致方法数量增加,但是最大的来源是kotlin实现属性。

kotlin不像Java可以直接访问field, 而是通过创建property的方式来访问。这是一个很好的特性,你可以自定义实现property的set/get,对比Java的set/get方法是个很大的进步。

但是这个是有代价的,对于val属性,kotlin自动生成backing field和getter函数来供java调用。public var属性会自动生成setter/getter函数。幸运的是private var属性已经有默认的setter/getter,需要不需要额外生成。所以这个时候你想想如果你定义了很多个public varval属性,那么kotlin会帮你自动生成更多的函数,所以带来的后果就是方法数量会越来越多,导致编译速度变慢。

假如方法数量已经接近限制,不需要使用自定义setter的属性可以用@JvmField修饰,被@JvmField修饰的属性不会自动生成setter/getter函数,如下

@JvmField
var aMap: aMap? = null

2.7 “==”

在kotlin中,”==”和”equals”都是比较引用是否相等。如何项目中只有kotlin代码,那这个肯定是个非常好的特性,但是如果项目同时包含java和kotlin(比如:你是在旧的java工程基础上,用kotlin开发新功能),那么“==”很容易产生混淆和错乱。

3. 设计原则

3.1

kotlin的class默认是final, 如果想要能被继承,那么需要使用open 修饰,它的设计原则来自于

Effective Java

这种默认的设计原则,会导致第三方/未完成的sdk的大多数会是不可继承的,从提供者角度来说:它必须保证这个类是足够完整的,从使用者角度来说:这个类必须要提供我需要的所有东西,否则,我必须重写。当然提供者可以为这个类加上open来表示这个类是可以继承的,但是这是另外一种设计思维。

Roedy Green, How to Write Unmaintainable Code有提到:
给你的所有类设置为final。 毕竟,你完成了这个项目 - 当然没有人可以通过扩展你的class来改善你的工作。 这甚至可能是一个安全漏洞 - 毕竟,为什么不是java.lang.String final? 如果您的项目中的其他编程人员抱怨,请告诉他们您的执行速度提升

3.2 错误的使用运算符重载

kotlin允许对类的变量运算符进行重载,设想有下面的代码

var person3 = person1 + person2

“+” 这个运算符有可能在很多个地方做了重载,在后期维护代码,很难区分哪个是哪个2分钟了解kotlin:优点,缺点,设计原则_第1张图片

运算符重载是有争议的,想要了解点击下面链接

  • Operator Overloading Considered Harmful
  • Operator Overloading Ad Absurdum
  • Why Everyone Hates Operator Overloading

4.总结

kotlin有很多优点,你也可以通过很多方式去了解和学习kotlin,相反,我们可能更想知道它的缺点,因为我们不希望它会成为未来项目中的隐患,在以后的项目开发中会不断分享关于kotlin学习心得,欢迎大家加入”kotlin交流群”一起学习提高,群号是:248445350

你可能感兴趣的:(kotlin,Android)