为什么使用 Kotlin

Kotlin

1 JetBrains 亲儿子,Google 干儿子

Kotlin 由大名鼎鼎的 JetBrains 开发维护,并且得到了 Google 的官方认可,可谓是 JetBrains 的亲儿子,Google 的干儿子,据说 Google 还在最新的 Android P 上,在虚拟机层对 Kotlin 进行了专门的性能优化。

2 Kotlin 和 Java 完全兼容

Kotlin 和 Java 一样都是基于 JVM 的语言,所以他们俩其实就是亲兄弟,自然也不存在什么兼容性问题,Java 的 API 在 Kotlin 里会被自动转换成 Kotlin 语法,反过来 Kotlin 的 API 在 Java 里也会自动转换成 Kotlin 语法,所以你完全不需要担心什么兼容性问题。

关于两个语言是如何相互转换的,我猜测都是通过字节码进行解析然后转换的,仅仅是猜测而已。

3 跟 setter 和 getter 说再见

据说我们这辈子撸过最多的代码是 setter 和 getter?因为你得为数据封装类的每一个字段写 setter 和 getter,机智一点的会使用 IDE 生成代码,而 Kotlin 则一句话就搞定了。

比如我们要写一个 Person 的数据封装类,用 Java 的话我们会这样写:

public class Person {

    private String name;
    private String addr;
    private int age;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }

    public String getAddr() {
        return addr;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getAge() {
        return age;
    }

}

这段 Java 代码看起来是不是又臭又长,我们换 Kotlin 写个 Person 类看看:

data class Person(var name: String, var addr: String, var age: Int)

什么!这就完了吗?没错这就完了,你需要的 setter 和 getter 方法 Kotlin 都已经为你自动生成了!

4 参数很多看着懵逼?

如果一个方法有很多参数,在调用的时候难免会出现看不出每个参数是什么意思的情况,尤其是传递数字的时候,比如我们有下面一个方法:

public Rectange createRectange(int x, int y, int width, int height) {}

我们在调用的时候这样写:

createRectangle(20, 30, 40, 50);

什么鬼!对 API 不熟悉的人根本没办法一眼看出这个方法调用传递的参数都是什么意思!我们换 Kotlin 写个看看:

fun createRectangle(x: Int, y: Int, width: Int, height: Int) {}

Kotlin 允许我们在调用方法的时候显式指定参数的名称,如下所示:

createRectangle(x = 20, y = 30, width = 40, height = 50)

怎么样!是不是看起来清晰多了,一眼就看出在坐标为 (20, 30) 的位置创建一个宽 40,高 50 的矩形。

5 跟方法重载说再见

如果一个方法有 N 个参数,并且有些参数是可选的,你是不是要为它写好几个重载版本,当让你也可以用 Builder 解决该问题,不管哪一种方式我都要多写很多代码啊!在 Kotlin 中你可以给每一个参数指定默认值,具有默认值的参数可以在调用的时候选填,是不是很爽!

我们来看下面这个 Java 方法:

public void foo() {
    foo(0, 0, 0)
}

public void foo(int a) {
    foo(a, 0, 0)
}

public void foo(int a, int b) {
    foo(a, b, 0)
}

public void foo(int a, int b, int c) {
    this.a = a;
    this.b = b;
    this.c = c;
}

再来看看 Kotlin 的写法:

fun foo(a: Int = 0, b: Int = 0, c: Int = 0) {}

搞定了!我们来看看这方法怎么调用:

foo()
foo(a = 1)
foo(b = 2)
foo(c = 3)
foo(a = 1, b = 2)
foo(a = 1, b = 2, c = 3)

6 用 as 代替 () 进行类型转换

如果你是 Android 开发者,相信你一定对 findViewById() 方法不陌生吧,在获取每一个 View 的时候都需要进行类型转换,如下所示:

Button button = (Button) findViewById(R.id.button);
ImageView imageView = (ImageView) findViewById(R.id.image_view);

这样的类型转换方式本身没什么问题,但是 Kotlin 有更优雅的类型转换关键字 as

Button button = findViewById(R.id.button) as Button;
ImageView imageView = findViewById(R.id.image_view) as Button;

两种方式比较起来,个人更喜欢 Kotlin 的方式,原因如下:

  • as 代替 () 更加具有可读性
  • 敲代码的时候打 as 比打 () 更快ヾ(。`Д´。)

7 让方法重写或实现更明显

我们来看下面这段代码:

public class A extends B {
  
    public void foo() {
      
    }
  
}

如果我告诉你 A 的 foo() 方法是重写 B 的 foo() 方法,你会怎么想?反正我是想骂人了,标记重写方法的 @Override 被狗吃了吗?这样的写法虽然编译器不会报错,但是却不利于代码的阅读,很容易让人误以为 foo() 方法是 A 自己新定义的。在 Kotlin 中则不允许你这样写,它要求你必须显式声明某一个方法是重写父类或接口的:

class A: B() {

    override fun foo() {
    
    }
  
}

有了 override 这个关键字,我们一看就知道这是一个重写的方法。

8 支持类的方法扩展

Kotlin 支持对一类进行方法扩展,注意这个扩展并不是通过继承,也就是它可以扩展一个 final 类。例如我们可以扩展 String 类,添加一个 log(tag: String) 方法,让它可以在日志系统里面输出自己的内容:

fun String.log(tag: String) {
    Log.d(tag, this)
}

然后我们就可以这么用:

val myString: String = 'test'
myString.log(TAG)

上面的 String 扩展很无聊,我们来一个逼格高一些的,例如我们可以扩展 SharedPreferences 类的 edit() 方法,让它支持更便捷的便捷方式:

fun SharedPreferences.edit(action: SharedPreferences.Editor.() -> Unit) {
    val editor = edit()
    action(editor)
    editor.apply()
}

然后我们就可以这么用:

val sp = getSharedPreferences("name",  Context.MODE_PRIVATE)
sp.edit({
    putInt("key1", 0)
    putLong("key2, 1)
    putString("key3", "value")
})

没错,你不需要调用 apply() 或者 commit() 方法,因为我们的扩展方法里面做好了,你要做的就是存储数据。

9 妈妈再也不用担心我空指针了

空指针异常是我们经常遇到的问题,为什么会出现空指针?要嘛是忘记进行判空处理,要嘛就是忘记赋值。当然在开发过程中可以使用 @Nullable 注解来对可能为空的变量或方法返回值进行注解,但是这样当你没有进行判空的时候,也仅仅是收到 IDE 的一个警告信息而已,照样能够正常编译。

在 Kotlin 的语法里,你必须显示声明成员变量、方法参数、方法返回值是否可以为空,当你在使用一个可能为空的成员变量时没有进行判空处理,编译器直接就会报错,而不是形同虚设的警告。下面是一段简单的代码,通过 ? 关键字声明成员变量 name 可能为空,然后我们在使用的时候必须判空:

var name: String? = null

if (name != null) {
    // do something
}

10 一句话实现单例模式

当例模式是我们经常用到的一个设计模式,其目的就是为了让全局只有一个对象实例,在 Java 里面我们会这么写:

public class Singleton {
    public static final Singleton INSTANCE = new Singleton();
    private Singleton() {}
}

在 Kotlin 里面则更加简单,只需要通关键字 object 就可以生命一个类为单例:

object Singleton {}

11 让你的代码仅在模块内可见

有过 SDK 开发经验的人应该会遇到这样一个问题,在开发过程中我们经常会将类按照功能进行分包,例如 A 属于com.xxx.pkga,B 属于 com.xxx.pkgb,但是我们又希望 A 的 foo() 方法可以被 B 访问,但是不被 SDK 之外的其他人访问,这种情况在 Java 里面是无解的,要嘛你声明 foo() 方法为 public,要嘛你把 A 和 B 放在一个包下,并声明 foo() 方法为 default,但是这两种做法都违背了我们的意愿。在 Kotlin 里面,专门提供了 internal 关键字,用于声明一个类或方法只有模块内部可见,所谓的模块在 Android Studio 里面就是一个 Gradle Module,而我们的 SDK 通常都是一个 Module,这就完美解决了上面描述的问题了。

12 比 // TODO 更狠的 TODO()

你是不是经常写了个 // TODO 注释然后放在那边几百年不动它?好吧这不能怪你,因为它只是一段注释而已,不会主动提示你“快来把我解决掉!”,而 Kotlin 给我提供了一个更给力的方式 TODO() 方法,用它在代码中标记那些你日后会去处理的逻辑,并且当你跑到这段没有完成逻辑处理的代码时,TODO() 会这样告诉你“来啊!干掉我啊!不然我就让你崩溃!”

fun foo() {
    TODO("来啊!干掉我啊!不然我就让你崩溃!")
}

其内部实现原理也很简单,就是给你抛异常,但是起码我不用自己写啦,而且它默认就是引入的:

/**
 * Always throws [NotImplementedError] stating that operation is not implemented.
 */
@kotlin.internal.InlineOnly
public inline fun TODO(): Nothing = throw NotImplementedError()

13 更加灵活的字符串定义

用 Java 拼接字符串是不是很恶心?比如我们要拼接一串 SQL 查询语句,用 Java 会这么写:

String table = ...
String id = ...
String email = ...
String age = ...
String sql = "SELECT * FROM " + table + " WHERE id=" + id + " AND name=" + name + " AND email=" + email + " AND age=" + age;  

使用一堆的 + 拼接起这个查询语句,它的可读性真是不敢恭维,而且写起来也不方便,我们用 Kotlin 的字符串模板试试:

val table = ...
val id = ...
val email = ...
val age = ...
val sql = "SELECT * FROM $table WHERE id=$id AND name=$name AND email=$email AND age=$age"

在字符串中的 $table$id$name$email$age 会被自动替换为同名变量的值,再也不需要使用一堆 + 去拼接字符串啦,如果你想让语句看起来更好看些,可以这么写:

val table = ...
val id = ...
val email = ...
val age = ...
val sql = """
          SELECT * FROM $table 
          WHERE id=$id 
          AND name=$name 
          AND email=$email 
          AND age=$age
          """

在 Kotlin 中字符串除了用 "字符串" 表示之外,还可以用 """字符串""" 表示,这种方式让你在定义字符串的时候随意换行并且保留格式。

持续更新中...

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

推荐阅读更多精彩内容