迟到的Kotlin体验

Google在今年的I/O大会上宣布Kotlin正式成为Android的官方语言,这么好玩的新东西当然不能错过啦,陆陆续续看资料写Demo过了一个多月,希望这篇体验还不太晚。

什么是Kotlin

Statically typed programming language for modern multiplatform applications
100% interoperable with Java™ and Android™

Kotlin是一门基于 JVM 的静态语言,由JetBrains 在2011年推出并持续维护,目前已发布1.0正式版,官网突出的一个优点是完全兼容Java和Android,除此以外,Kotlin还有下面几个核心目标。

  • 简约:帮你减少实现同一个功能的代码量。
  • 易懂:让你的代码更容易阅读,同时易于理解。
  • 安全:移除了你可能会犯错误的功能。
  • 通用:基于 JVM 和 Javascript,你可以在很多地方运行。
  • 互操作性:这就意味着 Kotlin 和 Java 可以相互调用,同时 Jetbrains 的目标是让他们 100% 兼容。

在2017年的I/O大会上,Google宣布Kotlin正式成为Android的开发语言。

Kotlin vs Java

Java的问题:

  • 空引用,这个被发明者吐槽价值十亿的错误(来源)一直困扰着众多Java程序员,这是使用Java语言无论如何都没办法避免的问题
  • 泛型的通配符,难以理解的super和extends,对编译器而言异常检查也很困难
  • 高阶方法,Java8才引入了函数式编程和Lambda,在此之前类似的实现都要写繁琐的接口来代替
  • 检查型异常,检查型异常的抛出有时会暴露实现细节,或者导致开发人员会被异常淹没,写出难以理解的代码

以上问题部分在新版本的Java中被解决,但无奈Android还只支持Java6,API19以上才支持Java7,对Java8的支持更是遥遥无期,所以这时候,Kotlin对Android开发人员来说就异常吸引了。

Kotlin的优点:

  • 空安全(Null safety)
  • Lambda 表达式
  • 数据类 (Data classes)
  • 函数字面量和内联函数(Function literals & inline functions)
  • 函数扩展 (Extension functions)
  • 智能转换(Smart casts)
  • 字符串模板(String templates)
  • 主构造函数(Primary constructors)
  • 类委托(Class delegation)
  • 类型推断(Type inference)
  • 单例(Singletons)
  • 声明点变量(Declaration-site variance)
  • 区间表达式(Range expressions)

Kotlin还为Android开发准备了两个库,一个是Kotlin Android Extensions,目前只实现了对findView的简化。另一个是开源的Anko,为Layout、SQLite等操作作了简化。

基本语法对比

下面是一些个人感兴趣的语法对比,更详细的内容和说明请参考官方文档汉化文档。都说Kotlin是Android界的Swift,那我就不客气把Swift也一起拿来对比了。

Print
Hello,World!

// Java
System.out.println("Hello,World!");
// Kotlin
println("Hello,World!")
// Swift
print("Hello,World!")

** 变量**
Kotlin使用var声明可变变量,val声明不可变变量,由于有类型推断,如果变量在初始化时就赋值,可以省略类型声明。

// Java
int x;
x = 1;
final int y = 2;
// Kotlin
var x: Int
x = 1
val y = 2
// Swift
var x:Int
x = 1
let y = 2

方法
Kotlin方法支持默认参数,使用vararg表示可变长度参数,直接使用“=返回值”可以省略返回值类型声明

// Java
public int sizeOf(float param0, String param1, int... param2) {
    return param2.length;
}
// Kotlin
fun sizeOf(param0: Float, param1: String = "hhh", vararg param2: Int)
        = param2.size
// Swift
public func sizeOf(param0:Float, param1:String = "hhh", param2:Int...) -> Int{
    return param2.count
}


没有对比就没有伤害,Kotlin中类的定义可以异常简洁,类的成员变量和构造函数可以一起定义,同时因为有默认参数的存在,Java需要写多种构造函数,在Kotlin中并不必要。
Kotlin还有个很方便的功能,只需要在$后面加上参数就可以拼接字符串,涉及方法调用和计算的话需要加上大括号。
但Kotlin也有个让我不爽的地方是,静态方法和变量需要定义在companion object中,感觉这种写法白白增加了一段缩进。

// Java
public class User extends Person{

    public static final String TAG = "USER";

    String name;
    int birth;

    User() {
        this("", 0);
    }

    User(String name, int birth) {
        this.name = name;
        this.birth = birth;
    }

    public String getInfo() {
        int age = 2017 - birth;
        return String.format("%s's age is %d", name, age);
    }
}
// Kotlin
class User(val name: String = "", val birth: Int = 0) : Person() {
    companion object {
        val TAG = "USER"
    }

    fun getInfo(): String = "$name's age is ${2017 - birth}"
}
// Swift
class User:Person{
    
    static let TAG = "USER"
    
    var name:String
    var birth:Int
    
    init(name:String = "", birth:Int = 0) {
        self.name = name
        self.birth = birth
    }
    
    public func getInfo() -> String{
        return "\(name)'s age is \(2017-birth)"
    }
}

** 空值处理 **
Kotlin所有变量默认不能为空,可以加上?表示允许空的类型,Kotlin要求开发人员必须要标记可空值且必须要对可能的空值作处理,这就从机制上限制了Null Pointer的问题。实际写代码时,因为有很多语法糖,对空值的处理很方便,但和Java兼容的部分比较麻烦,基本全部变量都要视做可空变量处理。(还是Android的代码用的爽,正经的加上@Nullable和@NonNull注解)

// Java
String name = null;
if(name !=null){
    int length = name.length();
}
// Kotlin
var name:String? = null
val length = name?.length
val length = name!!.length // throw NullPointerException
// Swift
var name:String? = nil
let length = name?.characters.count
let length = name!.characters.count // ERROR?

** For循环**
Kotlin增加了用..表示区间的写法,用在for和when中方便了不少

// Java
for (int i = 1; i < 11 ; i++) { }
for (int i = 1; i < 11 ; i+=2) { }
for (String item : collection) { }
for (Map.Entry<String, String> entry: map.entrySet()) { }
// Kotlin
for (i in 1..10) { }
for (i in 1..10 step 2) { }
for (item in collection) { }
for ((index, item) in collection.withIndex()) { }
for ((key, value) in map) { }
// Swift
for i in 1...10 { }
for i in stride(from: 1, to:10, by: 2) { }
for item in collection { }
for (index, item) in collection.enumerated() { }
for (key, value) in map { }

** Switch**
Kotlin的When集合了Java的switch和if...else...的功能,同时简化了判断值在区间内的写法

// Java
final int x = 10;
final String xResult;
switch (x){
  case 0:
  case 11:
    xResult = "0 or 11";
    break;
  case 1:
  case 2:
    //...
  case 10:
    xResult = "from 1 to 10";
    break;
  default:
    if(x < 12 && x > 14) {
      xResult = "not from 12 to 14";
      break;
    }
    xResult = "otherwise";
}

final int y = 10;
final String yResult;
if(isNegative(y)){
  yResult = "is Negative";
} else if(isZero(y)){
  yResult = "is Zero";
}else if(isOdd(y)){
  yResult = "is Odd";
}else {
  yResult = "otherwise";
}
// Kotlin
val x = 10
val xResult: String = when (x) {
    0, 11 -> "0 or 11"
    in 1..10-> "from 1 to 10"
    !in 12..14 -> "not from 12 to 14"
    else -> "otherwise"
}

val y = 10
val yResult = when {
  isNegative(y) -> "is Negative"
  isZero(y) -> "is Zero"
  isOdd(y) -> "is odd"
  else -> "otherwise"
}
// Swift
let x = 10
let xResult: String
switch x {
case 1,11:    xResult = "1 or 11"
case 2...10:xResult = "from 2 to 10"
default :xResult = "other"
}

let y = 10
let yResult: String
if isNegative(value: y){
  yResult = "is Negative"
} else if isZero(value: y){
  yResult = "is Zero"
}else if isOdd(value: y){
  yResult = "is Odd"
}else {
  yResult = "otherwise"
}

更多对比:
Kotlin to Java
Kotlin to Swift

Kotlin中各种酷炫的语法

** 函数扩展**
Kotlin 最强大的特性之一,通过 被扩展类型.函数 的写法,就能将函数变成被扩展类型的一部分。

// 先定义好Context函数扩展
fun Context.toast(message: String, duration: Int = Toast.LENGTH_SHORT) {
    Toast.makeText(this, message, duration).show()
}
// 然后在所有Context的子类中都可以直接调用toast方法
toast("Show it again?")

下面这个例子是Anko库中使用函数扩展对intent的一个简化。

// Java
Intent intent = new Intent(this, SomeOtherActivity.class);
intent.putExtra("id", 5);
intent.setFlag(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);

// Kotlin + Anko
startActivity(intentFor<SomeOtherActivity>("id" to 5).singleTop())

** 委托**
Kotlin 把委托模式视为很重要的语言特性,Kotlin标准库中已经实现了多种常见的委托,开发者也可以很容易定义自己的委托,使用时只要加一个小小的by。

//懒加载
val app: SQLiteOpenHelper by lazy {
    MyDatabaseHelper(applicationContext)
}

//Not Null,如果还没赋值前调用get方法,会抛出错误
var instance: Application by Delegates.notNull()

// 从Map中取值
val map: HashMap<String, Int> = ...
val width: Int by map
val height: Int by map

PS:Kotlin版的Butter Knife 就是用了委托来实现,使用起来的比Java的注解简洁方便许多,还省去了调用bind方法的步骤。

val titleTV: TextView by bindView(R.id.titleTV)
val sourceTV: TextView by bindView(R.id.sourceTV)
val webView: WebView by bindView(R.id.webView)

** 异步线程**
引入Anko库可以很方便的切换子线程和主线程,这个线程切换更多的是Kotlin函数式编程的体现,可以接受函数作为参数和把函数作为返回值会令代码更简洁和有更多的可能性。

doAsync {
    // 子线程
    for (i in 1..10) {
        val aTitle = titles[i]
        uiThread {
            // 主线程
            titleTV.text = aTitle
        }
        Thread.sleep(1000)
    }
}

总结

个人看来Kotlin最吸引人的一点是完全兼容Java,换句话说现存的所有Java框架和库可以无需修改直接拿到Kotlin上使用,在开发的过程中还可以先在部分模块中使用Kotlin编写,慢慢过渡到Kotlin开发。Intellj还提供一个Java-to-Kotlin的功能,但批量转换成Kotlin很容易报错,感觉还是留着Java文件共存或者手写一份比较靠谱。
其次各种简洁、易懂的语法也是一大亮点,Java中几十行的长代码,用Kotlin写可能只需要几行,简洁大方,逼格满满,还能保持很好的可读性,缺点就是会提高一些上手的门槛。
Kotlin会编译成JVM字节码,理论上Kotlin执行效率和Java代码的执行效率是一致的。有时候Kotlin可能会显得高一些,比如Kotlin提供了方法的inline设置,可以设置某些高频方法进行inline操作,减少了运行时的进栈出栈和保存状态的开销。

用Kotlin写了个新闻客户端,用了Dagger2、Retrofit、Glide、LifeCycle,兼容起来基本没什么问题,看着就舒爽。
地址:KotlinTest

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,042评论 25 707
  • 前言 人生苦多,快来 Kotlin ,快速学习Kotlin! 什么是Kotlin? Kotlin 是种静态类型编程...
    任半生嚣狂阅读 26,119评论 9 118
  • Google在今年的IO大会上宣布,将Android开发的官方语言更换为Kotlin,作为跟着Google玩儿An...
    蓝灰_q阅读 76,710评论 31 490
  • 2017年7月3日 星期一 晴 二年级五班代路瑶爸爸 亲子日记第七篇 今天是女儿上辅导班的第一天,下午回...
    人弋三寿阅读 156评论 0 0
  • 文 | M.W. 罗永浩最大的标签是“一个理想主义的工匠”,因此他创立的锤子科技一开始就被寄予了过高的期望。 这里...
    村口的聋猫阅读 825评论 0 3