写在文前
本文将展示在Android中会遇到的实际问题,并且使用Kotlin怎么去解决它们。一些Android开发者在处理异步、数据库或者处理Activity中非常冗长的listener时发现了很多的问题。通过一个个真实的场景,我们一边解决问题一边学习Kotlin的特性。
快速上手
如果不知道如何在Kotlin中写一个相当简单的Java表达式。这里有一个简单的诀窍,就是在AndroidStudio的Java文件中编写一段代码,然后将其粘贴到kt文件中,它会自动转换为Kotlin。
Kotlin优势
它更加易表现:这是它最重要的优点之一。你可以编写少得多的代码。
它更加安全:Kotlin是空安全的,也就是说在我们编译时期就处理了各种null的情况,避免了执行时异常。你可以节约很多调试空指针异常的时间,解决掉null引发的bug。
它可以扩展函数:这意味着,就算我们没有权限去访问这个类中的代码,我们也可以扩展这个类的更多的特性。
它是函数式的:Kotlin是基于面向对象的语言。但是就如其他很多现代的语言那样,它使用了很多函数式编程的概念,比如,使用lambda表达式来更方便地解决问题。其中一个很棒的特性就是Collections的处理方式。我稍后会进行介绍。
它是高度互操作性的:你可以继续使用所有用Java写的代码和库,甚至可以在一个项目中使用Kotlin和Java两种语言混合编程。一行Java一行Kotlin,别提有多风骚了。
详细实例
1. 易表现和简洁性
通过Kotlin,可以更容易地避免模版代码,因为大部分的典型情况都在语言中默认覆盖实现了。
举个例子,在Java中,如果我们要典型的数据类,我们需要去编写(至少生成)这些代码:
public class User{
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 "User{" +
"id=" + id +
", name='" + name + '\'' +
", url='" + url + '\'' +
", mbid='" + mbid + '\'' +
'}';
}
}
我们在不使用第三方框架的基础上,需要大量的set get方法和复写基础方法。
而使用Kotlin,我们只需要通过data
关键字:
data class User(
var id: Long,
var name: String,
var url: String,
var mbid: String)
这个数据类,它会自动生成所有属性和它们的访问器, 并自动生成相应的 equals、hashcode、toString 方法。
空口无凭,我们验证一下:
首先建立一个kt文件,新建一个简单的User类:
data class User(var name: String)
这时候在命令行使用kotlinc编译,得到一个class文件,反编译成Java文件,可以看到:
public final class User {
@NotNull
private String name;
@NotNull
public final String getName() {
return this.name;
}
public final void setName(@NotNull String var1) {
Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
this.name = var1;
}
public User(@NotNull String name) {
Intrinsics.checkParameterIsNotNull(name, "name");
super();
this.name = name;
}
// 解构声明
@NotNull
public final String component1() {
return this.name;
}
@NotNull
public final User copy(@NotNull String name) {
Intrinsics.checkParameterIsNotNull(name, "name");
return new User(name);
}
// $FF: synthetic method
// $FF: bridge method
@NotNull
public static User copy$default(User var0, String var1, int var2, Object var3) {
if((var2 & 1) != 0) {
var1 = var0.name;
}
return var0.copy(var1);
}
public String toString() {
return "User(name=" + this.name + ")";
}
public int hashCode() {
return this.name != null?this.name.hashCode():0;
}
public boolean equals(Object var1) {
if(this != var1) {
if(var1 instanceof User) {
User var2 = (User)var1;
if(Intrinsics.areEqual(this.name, var2.name)) {
return true;
}
}
return false;
} else {
return true;
}
}
}
事实说明在kotlin中 data 修饰符 = java中 private + getter + setter + toString + equals + hashCode
2. 空安全
当我们使用Java开发的时候,如果我们不想遇到NullPointerException,我们就需要在每次使用它之前,不停地去判断它是否为null。
而Kotlin是空安全的,我们通过一个安全调用操作符?
来明确地指定一个对象是否能为空。
我们可以像这样去写:
// 这里不能通过编译. User对象不能是null
var notNullUser: User= null
// User可以是 null
var user: User? = null
// 无法编译, user可能是null,我们需要进行处理
user.print()
// 只要在user != null时才会打印
user?.print()
// 使用Elvis操作符来给定一个在是null的情况下的替代值
val name = user?.name ?: "empty"
/**
如果user为可空类型,又一定要调用它的成员函数和变量,可以用!!操作符
两种可能,要么正确返回name,要么抛出空指针异常
当user为null,你不想返回null,而是抛出一个空指针异常,你就可以使用它。
*/
var name = user!!.name
3. 扩展方法
我们可以给任何类添加函数(View,Context等)。比起Java的继承机制,更加简洁和优雅。举个例子,我们可以给fragment增加一个显示toast的函数:
fun Fragment.toast(message: CharSequence, duration: Int = Toast.LENGTH_SHORT) {
Toast.makeText(getActivity(), message, duration).show()
}
我们现在可以这么做:
fragment.toast("Hello world!")
此处duration
已经赋了默认值,所以这个参数可传可不传。
包括扩展属性,可以直接 类名.属性名:类型
注意:Kotlin 的方法扩展并不是真正修改了对应的类文件,而是在编译器和 IDE 方面做了处理。使我们看起来像是扩展了方法。
4. 函数式支持
- Collections迭代
Kotlin使用lambda表达式来更方便地解决问题。体现最好的就是Collections的处理方式。
list.map(
println(it) //it表示迭代的对象
)
查看源码,我们可以看到实际上map
就是一个扩展方法,给所有可以迭代的集合提供该方法,map
方法接收的参数是一个lambda表达式,类型为T,返回值为R类型(意味着任意类型),那这里T类型实际上就是list的元素类型。
甚至于可以
list.map(::println)
::
表示方法或类的引用。为什么可以直接传方法引用呢?
我们看看println方法源码,可以看到println接收一个Any类也就是任意类型,而且返回值为空(Kotlin中空类型为Unit类,此处源码省略了返回值类型声明),所以完全符合map方法的要求。
注:类似于RxJava对数组的处理,Kotlin也提供了
flatMap
方法,具体可以自己了解。
- 事件
在Java中,每次我们去声明一个点击事件,都不得不去实现一个内部类,而在Kotlin中,可以直接声明我们要做什么。
view.setOnClickListener { toast("Hello world!") }
//注:此处的toast方法是Kotlin默认已经提供的扩展方法
5. 互操作性
Kotlin调用Java和Java调用Kotlin与之前的Java 类之间调用方式没有太大差别,不详细介绍。
就举个Java调用Kotlin的小例子:
//Kotlin
class Overloads {
fun overloaded(a: Int, b: Int = 0, c: Int = 1){
println("$a, $b, $c")
}
}
//Java
public class AccessToOverloads {
public static void main(String... args) {
Overloads overloads = new Overloads();
overloads.overloaded(1, 2, 3);
}
}
可以看到非常简单,这里要多介绍一个Kotlin注解@JvmOverloads
。仍然定义了一个overloaded
方法,加上注解后,Kotlin会自动重载成n个方法(n表示参数个数)
//Kotlin
class Overloads {
@JvmOverloads
fun overloaded(a: Int, b: Int = 0, c: Int = 1){
println("$a, $b, $c")
}
}
/**
在Java可以调用3个overloaded方法,分别是:
overloaded(a,b,c)
overloaded(a,b)
overloaded(a)
*/
public class AccessToOverloads {
public static void main(String... args) {
Overloads overloads = new Overloads();
overloads.overloaded(1, 2, 3);
overloads.overloaded(1);
overloads.overloaded(1,3);
}
}
6. 其他
- 单例
首先说说单例的实现方式,在之后的实战中,将会经常接触到object
这个关键字。
先看Java,在Java中,实现一个单例,我们需要:
保留一个单例对象的静态实例
提供一个类方法让外界访问唯一的实例
构造方法采用private修饰符
而在Kotlin中,一个修饰符就解决了。
object PlainOldSingleton {
}
怎么做到的?我们看看反编译的结果:
可以看到写法和Java是完全一样的,又有一个新问题,在类加载的时候就初始化了实例,这种方式很糟糕,我们最好选择懒加载。那么在Kotlin中懒加载的2种实现方式如下:
class LazyNotThreadSafe {
//方式一
companion object{
val instance by lazy(LazyThreadSafetyMode.NONE) {
LazyNotThreadSafe()
}
//方式二,实际是Java的直译
private var instance2: LazyNotThreadSafe? = null
fun get() : LazyNotThreadSafe {
if(instance2 == null){
instance2 = LazyNotThreadSafe()
}
return instance2!!
}
}
}
如果想要实现线程安全,可以加上@Synchronized
注解,这和Java中给类加上Synchronized
修饰符是一样的。同样@Volatile
注解和Java的Volatile修饰符作用也是一样的。
或者使用静态内部类的单例方法:
class LazyThreadSafeStaticInnerObject private constructor(){
companion object{
fun getInstance() = Holder.instance
}
private object Holder{
val instance = LazyThreadSafeStaticInnerObject()
}
}
- 委托
Kotlin中,委托的实现依靠于关键字 by
,
by
表示将抽象主题的实例(by后边的实例)保存在代理类实例的内部。
比如下面这个例子中:BaseImpl类继承于Base接口,并可以Base接口的所有的 public 方法委托给一个指定的对象。
interface Base {
fun display()
}
class BaseImpl : Base {
override fun display() {
print("baseimpl display")
}
}
class ProxyClass(base: Base) : Base by base
//程序入口
fun main(args: Array<String>) {
var base = BaseImpl()
var proxy = ProxyClass(base)
proxy.display()
}
- 泛型
在Java中,一般使用Gson库来解析Json。调用方法的时候,我们需要传入想要转成的类的Class。我们都知道Java的泛型实际上是伪泛型,对泛型支持的底层实现采用的是类型擦除的方式(只有在编译期才有)。
所以当使用Gson.fromJson(String json , Class<T> classOf)
方法时,虽然传入了类型参数,当实际上这个T仍然是个Object。
而在Kotlin中,可以使用reified,告别Class。
reified的意思是具体化。作为Kotlin的一个方法泛型关键字,它代表你可以在方法体内访问泛型指定的JVM类对象。
inline fun <reified T: Any> Gson.fromJson(json: String): T{
//封装了`Gson.fromJson(String json , Class<T> classOf)`方法
return fromJson(json, T::class.java)
}
这里需要指定T类型为Any,即Object类。
接着可以不需要传入Class,直接调用
fun main(args: Array<String>) {
val json = "{state:0,result:'success',name:'test'}"
var result : ReifiedBean = Gson().fromJsonNew(json)
println(result.name+","+result.result)
}
这要归功于inline,inline 意味着编译的时候真正要编译到调用点。那么哪个方法调用了它,参数的类型都是确定的。也就不需要传入Class了
** 7. 摆脱不必要的依赖**
Kotlin替换了许多第三方库,如ButterKnife、Google Autovalue、Retrolambda、Lombok和一些RxJava代码。
但是也是可以100%兼容RxJava的,举个读取本地文本逐个字打印的例子。
好了,言归正传。
普通的获取View方法,需要一个个去findViewById
而使用Kotlin后
可能有人注意到了,还是需要findViewById啊!!骗子!说好的优雅呢?完全没觉得更加简洁啊!!别急,Kotlin常用的获取控件方式不是这样的,容我介绍个Kotlin库——Anko。
3. Kotlin库——Anko
简介
Anko是Kotlin官方开发的一个让开发Android应用更快速更简单的Kotlin库
1. 再也不用findViewById
做过Android开发的人都知道,布局文件写的多了,findViewById也是一个很大的工作量,而且还要先声明变量,在findViewById然后再强转成我们的控件,使用方式一般如下
TextView username;
username=(TextView)findViewById(R.id.user);
username.setText("我是一个TextView");
有时候写的是不是想吐,可能有些人说现在不是有一些注解的库,如butterknife,当我们使用注解时可以不用findViewById了,使用方式如下
@BindView(R.id.user)
TextView username;
username.setText("我是一个TextView");
确实是这样,使用注解后确实给我们少了一些工作量,不过这依然没有最简单化,最简单的就是我们可以直接给id为user的控件直接赋值,或许你会感觉这有点不可思议。不过Kotlin确实做到了。我们可以直接这样写
user.text="我是一个TextView"
user就是我们布局文件声明的id,.text
就相当于setText()
,在Kotlin语言中,我们看不到了像Java中的set/get方法了。
当我们想这样使用的时候(不用findViewById,直接使用xml控件id)
我们需要在gradle加入apply plugin: ‘kotlin-android-extensions’
,需要加入下面一句代码
import kotlinx.android.synthetic.main.activity_login.*
注:activity_login就是我们的布局
import org.jetbrains.anko.toast
import org.jetbrains.anko.onClick
class Main2Activity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
my_textView.text = "kotline test"
my_textView.textColor = Color.BLUE
my_button.text = "Click"
my_button.onClick { toast("aa") }
}
}
为什么Anko不需要.setText
可以直接.text
呢?其实这是通过扩展函数实现的,我们看下内部的实现细节:
public var TextView.text: CharSequence
get() = getText()
set(v) = setText(v)
2. Anko Layout
通常我们使用xml文件写我们的布局,但是存在有一些缺点:如不是类型安全,不是空安全,解析xml文件消耗更多的CPU和电量等等。
而Anko Layout可以使用DSL动态创建我们的UI,并且它比我们使用Java动态创建布局方便很多。主要是更简洁,它拥有类似xml创建布局的层级关系,能让我们更容易阅读。
verticalLayout {
val textView = textView("textview")
val name = editText()
val button=button()
button.onClick {
toast("${name.text}")
}
}
我们在OnCreate方法中可以去掉setContentView,然后加入上面代码就可以显示如下图的效果,即一个垂直的线性布局中,放了一个TextView,一个EditText,和一个Button。并且Button中有一个点击事件,当点击时将EditText的内容以toast显示。
在上面创建UI过程中,我们直接把创建UI的代码写在onCreate方法中了,当然,还有一种写法。我们创建一个内部类实行AnkoComponent接口,并重写createView方法,该方法返回一个View,也就是我们创建的布局。修改如下
inner class UI : AnkoComponent<LoginActivity> {
override fun createView(ui: AnkoContext<LoginActivity>): View {
return with(ui){
verticalLayout {
val textView=textView("我是一个TextView"){
textSize = sp(17).toFloat()//自定义字体大小
textColor=context.resources.getColor(R.color.red)//自定义颜色
}.lparams{
margin=dip(10)//它表示将10dp转换为像素
height= dip(40)
width= matchParent
}
val name = editText("EditText")
button("Button") {
onClick { view ->
toast("Hello, ${name.text}!")
}
}
}
}
}
}
然后在onCreate方法中加一句代码,即可创建我们的布局页面了。如下
UI().setContentView(this@LoginActivity)
其中,dip(10)
,表示将10dp转换为像素的意思,是Anko的扩展函数,说到扩展函数,我发现Kotlin源码里大量地使用扩展函数,这也是Kotlin语言的优势之一。确实很强大,例如dip扩展(摘取View扩展)
inline fun View.dip(value: Int): Int = context.dip(value)
fun Context.dip(value: Int): Int = (value * resources.displayMetrics.density).toInt()
就如我们之前说的toast、text也是拓展函数一样
inline fun AnkoContext<*>.toast(message: CharSequence) = ctx.toast(message)
fun Context.toast(message: CharSequence) = Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
但是为了界面和逻辑分离,界面还是建议使用xml,所以这里就不对Anko Layout多做介绍了。
3. 其他方面
比如网络请求AsyncTask
doAsync {
//后台执行代码
uiThread {
//UI线程
toast("线程${Thread.currentThread().name}")
}
}
其他内容可以直接访问Anko
Kotlin的缺点
尽管 Kotlin 非常棒,但是它并不完美。我列举了一些我不喜欢的部分。
1. 没有命名空间
Kotlin 允许你在文件中定义顶级的函数和属性,但是这会带来困扰——所有从 Kotlin 引用的顶级声明无法区分。这让我们有时候在读代码时很难快速确定用的是哪一个函数。
例如,你定义这样一个顶级函数:
fun foo() {...}
你可以通过 foo() 调用。
如果你在不同的包里面也存在同样的方法,在调用时就不能明显区分出是调用的哪个方法。你可以通过在前面添加包名的方式去调用,但是如果 Java 约定的包名很深,似乎不太友好。
一种近似的解决方案是使用单例的 object 类。
object FooActions { fun foo() {...}}
这样你在 Kotlin 中可以通过 FooActions.foo() 调用,但是在 Java 中你必须要这样 FooActions.INSTANCE.foo()
这样调用,这看起来很麻烦。
你也可以使用 @JvmStatic 去注解该方法,从而省掉INSTANCE
。
其实没有命名空间并不是什么大不了的事,但是如果 Kotlin 能够提供的话,能省不少事。
2. 没有静态修饰符
Kotlin为静态函数和属性提供了一个和 Java 不一样的处理方式。并不是说有多烂,只是觉得让代码变得不干净而且没有必要。
例如,在 Android 的 View 类中定义的静态属性 View.VISIBLE 和静态函数 View.inflate
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) {...}
}
}
注:companion object为伴生对象
尽管 Kotlin 的版本并没有那么恐怖,但是它的复杂程度超过了我对这门语言的预期。如果去掉注解,你在 Java 中就不得不使用这样可怕的语法去调用:
// With annotations:
View.VISIBLE;
//Without annotations:
View.Companion.getVISIBLE();
3. 编译方法数量
Kotlin 肯定会减少项目中的代码行数,但是它也会提高代码在编译以后的方法数。主要原因就是 Kotlin 属性的实现方式。
和 Java 不一样,Kotlin 没有提供单独定义域的方式。你必须使用 val 或者 var 来声明变量。这样有一个好处,就是省去了像 Java 一样定义 getters 和 setters 方法。
但是这需要一定的成本。每一个public的 val 变量都会生成一个「支持域」和一个能被 Java 调用的 getter 方法。每一个public的 var 变量都会生成 getter 和 setter 方法。
// kt 文件:
// 默认就是public,无需额外添加public修饰符
val strValPublic: String = "strValPublic"
var strVarPublic: String = "strVarPublic"
// 以下是反编译结果:
public final class VarAndValKt {
@NotNull
private static final String strValPublic = "strValPublic";
@NotNull
private static String strVarPublic = "strVarPublic";
@NotNull
public static final String getStrValPublic() {
return strValPublic;
}
@NotNull
public static final String getStrVarPublic() {
return strVarPublic;
}
public static final void setStrVarPublic(@NotNull String var0) {
Intrinsics.checkParameterIsNotNull(var0, "<set-?>");
strVarPublic = var0;
}
}
拓展:Intrinsics.checkParameterIsNotNull 方法其实很简单,原理:
public static void checkParameterIsNotNull(Object value, String paramName) {
if (value == null) {
throwParameterIsNullException(paramName);
}
}
其实所有空安全的秘密都在这个类里面了
庆幸的是,私有属性的 getters 和 setters 会生成域而不是生成方法。
// kt文件:
private val strValPrivate: String = "strValPrivate"
private var strVarPrivate: String = "strVarPrivate"
// 以下是反编译结果:
public final class VarAndValKt {
private static final String strValPrivate = "strValPrivate";
private static String strVarPrivate = "strVarPrivate";
}
所以如果你把项目中Java代码转成Kotlin,而且之前的 Java 代码中定义了大量的公开域(这在定义常量的时候很常见),你会惊奇的发现最终编译生成的方法数量大幅上升。
如果你的 Android 应用快接近方法数限制了,我建议你为不需要自定义 getter 方法的常量加上 @JvmField 注解。这样会阻止 getters 方法的生成,从而减少你的方法数。
// kt 文件:
@JvmField
val strValPublic: String = "strValPublic"
@JvmField
var strVarPublic: String = "strVarPublic"
// 以下是反编译结果:
// 注意看,get set方法消失,取而代之的是private修饰符变成了public
public final class VarAndValKt {
@JvmField
@NotNull
public static final String strValPublic = "strValPublic";
@JvmField
@NotNull
public static String strVarPublic = "strVarPublic";
}
4. 没有CE机制
Kotlin官网对CE的解释:
翻译一下:
Kotlin 没有受检的异常。这其中有很多原因,但我们会提供一个简单的例子。
以下是 JDK 中 StringBuilder 类实现的一个示例接口
Appendable append(CharSequence csq) throws IOException;
这个签名是什么意思? 它是说,每次我追加一个字符串到一些东西(一个 StringBuilder、某种日志、一个控制台等)上时我就必须捕获那些 IOException。 为什么?因为它可能正在执行 IO 操作(Writer 也实现了 Appendable)…… 所以它导致这种代码随处可见的出现
我们看到Java的CE机制被诟病了很久,但是如果你经过理性的分析,就会发现,Java 的有些设计看起来“繁复多余”,实际上却是经过深思熟虑的决定。Java 的设计者知道有些地方可以省略,却故意把它做成多余的。我们不能盲目地以为简短就是好,多写几个字就是丑陋不优雅,其实不是那样的。
Kotlin有异常机制,但不要求你在函数的类型里面声明可能出现的异常类型,也不使用静态类型系统对异常的处理进行检查和验证。那当我每调用一个函数(不管是标准库函数,第三方库函数,还是队友写的函数,甚至我自己写的函数),我都会疑惑这个函数是否会抛出异常。由于函数类型上不需要标记它可能抛出的异常,为了确保一个函数不会抛出异常,你就需要检查这个函数的源代码,以及它调用的那些函数的源代码,甚至整个调用树!
在这种疑虑的情况下,你就不得不做最坏的打算,你就得把代码写成:
try
{
foo()
}
catch (e:Exception)
{
printf(e)
}
因为不知道 foo 函数里面会有什么异常出现,所以你的 catch 语句里面也不知道该做什么。大部分人只能在里面放一条 log,记录异常的发生。这是一种非常糟糕的写法,不但繁复,而且可能掩盖运行时错误。
那么 Java 呢?因为 Java 有 CE,所以当你看到一个函数没有声明异常,就可以放心的省掉 try-catch。所以这个问题,自然而然就被避免了,你不需要在很多地方疑惑是否需要写 try-catch。Java 编译器的静态类型检查会告诉你,在什么地方必须写 try-catch,或者加上 throws 声明。
结尾
在学习过程中,我发现,如果有着扎实的Java基础,这东西掌握起来是很快的,所以到底学不学Kotlin,其实是不用着急的。一个新的语言想要快速的普及,那么可能只有在运行效率上有所提升,才是最大的优势,而Kotlin并不具备这样的属性。
我们可以看下Java和Kotlin的编译速度对比。
编译速度对比
我不会试图比较一行代码的编译速度;相反,比较的是将代码从Java转换为Kotlin是否会影响其总体构建的时间。
在转换之前,App Lock的Java代码有5,491个方法和12,371行代码。 改写后,这些数字下降到4,987方法和8,564行Kotlin代码。 在重写期间没有发生大的架构更改,因此在重写之前和之后测试编译时间应该很好地了解Java和Kotlin之间的构建时间的差异。我写了一个shell来重复执行gradle。所有测试连续进行10次。
-
clean + 不用Gradle daemon Build
这是两种语言中构建时间最差的情况:从冷启动运行一个clean的构建。 对于这个测试,我禁用了Gradle daemon。
这里是十个构建所花费的时间:
对于没有Gradle daemon 并且clean构建,Java编译比Kotlin快17%,但是大部分人不会这么编译他们的代码。
- clean +Gradle daemon Build
可以看到,Kotlin第一次运行所花费的时间与上一个方案的时间相同,但后续运行的性能逐步提高。
对于clean + Gralde daemon 编译,Java编译比Kotlin快13%。
所以Kotlin编译在完整代码情况下比Java慢一点。 但是你通常只会对几个文件进行更改后编译,所以,我们来看看Kotlin在增量编译是否可以赶上Java。
- 增量编译
所以虽然Java在clean构建比Kotlin 快10-15%,但这些情况很少。 对于大多数开发人员来说,更常见的情况是部分构建,随着Gradle daemon运行和增量编译的开启,Kotlin编译速度快或略快于Java。
所以,还是那句话,一个新的语言想要快速的普及,在运行效率上有所提升,才是最大的优势,Kotlin肯定值得学习的,但并没有传的那么夸张。有精力就去学习,有自己的学习计划也可以放一放,延后再学。
我想只有用得多了,Kotlin的优势才会慢慢展现出来,这需要一个较为漫长的过渡期。
作者:FeelsChaotic
链接://www.greatytc.com/p/f364e3f9cc36
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。