kotlin提供了很多好的特性,比如:null安全检测,属性访问,unchecked exceptions, publication有更详细的介绍。
kotlin项目开发总结有介绍如何使用,这个是有JetBrains提供的,目前已经集成到了IDEA/AS, 虽然它不是很完美(我的上篇博客也已经介绍到了),但是对比重新用Kotlin开发一个完全一样功能的类来说,可以节省不少时间。
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可以很好的使用。
kotlin提供了很多对collections和iterables的扩展,具体可以看下
我写的分析和使用collections。
Named 函数参数和默认函数参数非常简单,但有时候可以帮我们省掉很多代码。特别是当构造函数有超过4个以上的参数时,可以指定默认几个默认参数,在调用的时候可以只提供一个参数。比如
class Presenter(
val okhttp: OkHttp = productionOkHttp(),
val picasso: Picasso = productionPicassoInstance()
) {...}
在调用的时候,我们不提供参数,那么默认参数会被使用
var prensenter = Presenter()
虽然kotlin非常强大,但它毕竟不是完美的。它也会有一些可能未来会解决的缺点。
大家在使用kotlin开发项目的时候应该有注意到了,主要还是因为kotlin会自动自动生成更多的代码,比如为属性生成get/set, 对比java会存在更多的方法数量。https://youtrack.jetbrains.com/issue/KT-6246,这篇博客有做分析
我目前开发的项目使用了dagger,permissionsdispatcher,deeplinkdispatch,databinding,都需要使用kapt来做annotation proccessing,但是我已经碰到了好多次,kapt报的很奇怪的错误,有一次,我重构dagger的module时,报了一个蛋疼的问题,我以为我是dagger没用好,找了很多资料对比都没有解决,花了大概一天的时间,后面没办法只能clean project,但是奇怪的问题还在,后面我让application继承了DaggerApplication,clean下就可以。后面还是碰到好几次这样的问题,都是clean之后build就可以了,这个问题应该不只我一个人碰到,我想可能是kapt的一个bug, 我还是很相信google/JetBrains, 所以还是继续坚持使用kotlin开发项目。
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
还是像上面的问题,如果项目同时存在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来避免
Java自动转换成kotlin是个非常好的特性,但是也会带来问题。Javadoc原来的结构会被破坏掉,静态field和方法会转换成companion object中的普通声明,如果有Java代码调用这个类,就会出现点问题,除非你后面手动给这个转换后的companion object 添加上@JvmField和@JvmStatic。这是个不小的隐患,需要特别注意下。
过多的方法数量会导致编译速度变慢。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 var
和val
属性,那么kotlin会帮你自动生成更多的函数,所以带来的后果就是方法数量会越来越多,导致编译速度变慢。
假如方法数量已经接近限制,不需要使用自定义setter的属性可以用@JvmField修饰,被@JvmField修饰的属性不会自动生成setter/getter函数,如下
@JvmField
var aMap: aMap? = null
在kotlin中,”==”和”equals”都是比较引用是否相等。如何项目中只有kotlin代码,那这个肯定是个非常好的特性,但是如果项目同时包含java和kotlin(比如:你是在旧的java工程基础上,用kotlin开发新功能),那么“==”很容易产生混淆和错乱。
kotlin的class默认是final, 如果想要能被继承,那么需要使用open 修饰,它的设计原则来自于
Effective Java
这种默认的设计原则,会导致第三方/未完成的sdk的大多数会是不可继承的,从提供者角度来说:它必须保证这个类是足够完整的,从使用者角度来说:这个类必须要提供我需要的所有东西,否则,我必须重写。当然提供者可以为这个类加上open来表示这个类是可以继承的,但是这是另外一种设计思维。
Roedy Green, How to Write Unmaintainable Code有提到:
给你的所有类设置为final。 毕竟,你完成了这个项目 - 当然没有人可以通过扩展你的class来改善你的工作。 这甚至可能是一个安全漏洞 - 毕竟,为什么不是java.lang.String final? 如果您的项目中的其他编程人员抱怨,请告诉他们您的执行速度提升
kotlin允许对类的变量运算符进行重载,设想有下面的代码
var person3 = person1 + person2
“+” 这个运算符有可能在很多个地方做了重载,在后期维护代码,很难区分哪个是哪个
运算符重载是有争议的,想要了解点击下面链接
kotlin有很多优点,你也可以通过很多方式去了解和学习kotlin,相反,我们可能更想知道它的缺点,因为我们不希望它会成为未来项目中的隐患,在以后的项目开发中会不断分享关于kotlin学习心得,欢迎大家加入”kotlin交流群”一起学习提高,群号是:248445350