前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Kotlin —— 这次入门就不用放弃了

Kotlin —— 这次入门就不用放弃了

作者头像
用户2802329
发布2018-08-07 14:58:02
1.6K0
发布2018-08-07 14:58:02
举报
文章被收录于专栏:Android先生Android先生Android先生
作者:FEELS_CHAOTIC址:http://www.jianshu.com/p/f364e3f9cc36

声明:本文是FEELS_CHAOTIC原创,已获其授权发布,未经原作者允许请勿转载

写在文前

本文将展示在Android中会遇到的实际问题,并且使用Kotlin怎么去解决它们。一些Android开发者在处理异步、数据库或者处理Activity中非常冗长的listener时发现了很多的问题。通过一个个真实的场景,我们一边解决问题一边学习Kotlin的特性。

快速上手

如果不知道如何在Kotlin中写一个相当简单的Java表达式。这里有一个简单的诀窍,就是在AndroidStudio的Java文件中编写一段代码,然后将其粘贴到kt文件中,它会自动转换为Kotlin。

Kotlin优势

  1. 它更加易表现:这是它最重要的优点之一。你可以编写少得多的代码。
  2. 它更加安全:Kotlin是空安全的,也就是说在我们编译时期就处理了各种null的情况,避免了执行时异常。你可以节约很多调试空指针异常的时间,解决掉null引发的bug。
  3. 它可以扩展函数:这意味着,就算我们没有权限去访问这个类中的代码,我们也可以扩展这个类的更多的特性。
  4. 它是函数式的:Kotlin是基于面向对象的语言。但是就如其他很多现代的语言那样,它使用了很多函数式编程的概念,比如,使用lambda表达式来更方便地解决问题。其中一个很棒的特性就是Collections的处理方式。我稍后会进行介绍。
  5. 它是高度互操作性的:你可以继续使用所有用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中,实现一个单例,我们需要:

  1. 保留一个单例对象的静态实例
  2. 提供一个类方法让外界访问唯一的实例
  3. 构造方法采用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中,委托的实现依靠于关键字 byby表示将抽象主题的实例(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。

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的优势才会慢慢展现出来,这需要一个较为漫长的过渡期。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-11-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Android先生 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
    • 1. 没有命名空间
      • 2. 没有静态修饰符
        • 3. 编译方法数量
          • 编译速度对比
          相关产品与服务
          数据库
          云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档