中国领先的IT技术网站
|
|

在没有Kotlin的世界与Android共舞

没有 Kotlin 的生活就像在触摸板上玩魔兽争霸 3。购买鼠标很简单,但如果你的新雇主不想让你在生产中使用 Kotlin,你该怎么办?在文章接下来的部分,我想简短地描述一些 Kotlin 的特征,使你通过一些知名的工具和库,可以应用到你的 Android 里的 Java 代码中去。对于 Kotlin 和 Java 的基本认识是需要的。

作者:Piotr Ślesarew来源:Linux中国|2017-05-08 16:35

在没有Kotlin的世界与Android共舞

开始投入一件事比远离它更容易。 — Donald Rumsfeld

没有 Kotlin 的生活就像在触摸板上玩魔兽争霸 3。购买鼠标很简单,但如果你的新雇主不想让你在生产中使用 Kotlin,你该怎么办?

下面有一些选择。

  • 与你的产品负责人争取获得使用 Kotlin 的权利。
  • 使用 Kotlin 并且不告诉其他人因为你知道最好的东西是只适合你的。
  • 擦掉你的眼泪,自豪地使用 Java。

想象一下,你在和产品负责人的斗争中失败,作为一个专业的工程师,你不能在没有同意的情况下私自去使用那些时髦的技术。我知道这听起来非常恐怖,特别当你已经品尝到 Kotlin 的好处时,不过不要失去生活的信念。

在文章接下来的部分,我想简短地描述一些 Kotlin 的特征,使你通过一些知名的工具和库,可以应用到你的 Android 里的 Java 代码中去。对于 Kotlin 和 Java 的基本认识是需要的。

数据类

我想你肯定已经喜欢上 Kotlin 的数据类。对于你来说,得到 equals()、 hashCode()、 toString() 和 copy() 这些是很容易的。具体来说,data 关键字还可以按照声明顺序生成对应于属性的 componentN() 函数。 它们用于解构声明。

  1. data class Person(val name: String) 
  2. val (riddle) = Person("Peter"
  3. println(riddle) 

你知道什么会被打印出来吗?确实,它不会是从 Person 类的 toString() 返回的值。这是解构声明的作用,它赋值从 name 到 riddle。使用园括号 (riddle) 编译器知道它必须使用解构声明机制。

  1. val (riddle): String = Person("Peter").component1() 
  2. println(riddle) // prints Peter) 

这个代码没编译。它就是展示了构造声明怎么工作的。

正如你可以看到 data 关键字是一个超级有用的语言特性,所以你能做什么把它带到你的 Java 世界? 使用注释处理器并修改抽象语法树(Abstract Syntax Tree)。 如果你想更深入,请阅读文章末尾列出的文章(Project Lombok— Trick Explained)。

使用项目 Lombok 你可以实现 data关键字所提供的几乎相同的功能。 不幸的是,没有办法进行解构声明。

  1. import lombok.Data; 
  2. @Data class Person { 
  3.     final String name

@Data 注解生成 equals()、hashCode() 和 toString()。 此外,它为所有字段创建 getter,为所有非最终字段创建setter,并为所有必填字段(final)创建构造函数。 值得注意的是,Lombok 仅用于编译,因此库代码不会添加到您的最终的 .apk。

Lambda 表达式

Android 工程师有一个非常艰难的生活,因为 Android 中缺乏 Java 8 的特性,而且其中之一是 lambda 表达式。 Lambda 是很棒的,因为它们为你减少了成吨的样板。 你可以在回调和流中使用它们。 在 Kotlin 中,lambda 表达式是内置的,它们看起来比它们在 Java 中看起来好多了。 此外,lambda 的字节码可以直接插入到调用方法的字节码中,因此方法计数不会增加。 它可以使用内联函数。

  1. button.setOnClickListener { println("Hello World") } 

最近 Google 宣布在 Android 中支持 Java 8 的特性,由于 Jack 编译器,你可以在你的代码中使用 lambda。还要提及的是,它们在 API 23 或者更低的级别都可用。

  1. button.setOnClickListener(view -> System.out.println("Hello World!")); 

怎样使用它们?就只用添加下面几行到你的 build.gradle 文件中。

  1. defaultConfig { 
  2.     jackOptions { 
  3.         enabled true 
  4.     } 
  5. compileOptions { 
  6.     sourceCompatibility JavaVersion.VERSION_1_8 
  7.     targetCompatibility JavaVersion.VERSION_1_8 

如果你不喜欢用 Jack 编译器,或者你由于一些原因不能使用它,这里有一个不同的解决方案提供给你。Retrolambda 项目允许你在 Java 7,6 或者 5 上运行带有 lambda 表达式的 Java 8 代码,下面是设置过程。

  1. dependencies { 
  2.     classpath 'me.tatarka:gradle-retrolambda:3.4.0' 
  3. apply plugin: 'me.tatarka.retrolambda' 
  4. compileOptions { 
  5.     sourceCompatibility JavaVersion.VERSION_1_8 
  6.     targetCompatibility JavaVersion.VERSION_1_8 

正如我前面提到的,在 Kotlin 下的 lambda 内联函数不增加方法计数,但是如何在 Jack 或者 Retrolambda 下使用它们呢? 显然,它们不是没成本的,隐藏的成本如下。

该表展示了使用不同版本的 Retrolambda 和 Jack 编译器生成的方法数量。该比较结果来自 Jake Wharton 的“探索 Java 的隐藏成本” 技术讨论之中。

数据操作

Kotlin 引入了高阶函数作为流的替代。 当您必须将一组数据转换为另一组数据或过滤集合时,它们非常有用。

  1. fun foo(persons: MutableList<Person>) { 
  2.     persons.filter { it.age >= 21 } 
  3.            .filter { it.name.startsWith("P") } 
  4.            .map { it.name } 
  5.            .sorted() 
  6.            .forEach(::println) 
  7. data class Person(val name: String, val age: Int

流也由 Google 通过 Jack 编译器提供。 不幸的是,Jack 不使用 Lombok,因为它在编译代码时跳过生成中间的 .class 文件,而 Lombok 却依赖于这些文件。

  1. void foo(List<Person> persons) { 
  2.     persons.stream() 
  3.            .filter(it -> it.getAge() >= 21) 
  4.            .filter(it -> it.getName().startsWith("P")) 
  5.            .map(Person::getName) 
  6.            .sorted() 
  7.            .forEach(System.out::println); 
  8. class Person { 
  9.     final private String name
  10.     final private int age; 
  11.     public Person(String nameint age) { 
  12.         this.name = name
  13.         this.age = age; 
  14.     } 
  15.     String getName() { return name; } 
  16.     int getAge() { return age; } 

这简直太好了,所以 catch 在哪里? 令人悲伤的是,流从 API 24 才可用。谷歌做了好事,但哪个应用程序有用 minSdkVersion = 24?

幸运的是,Android 平台有一个很好的提供许多很棒的库的开源社区。Lightweight-Stream-API 就是其中的一个,它包含了 Java 7 及以下版本的基于迭代器的流实现。

  1. import lombok.Data; 
  2. import com.annimon.stream.Stream; 
  3. void foo(List<Person> persons) { 
  4.     Stream.of(persons) 
  5.           .filter(it -> it.getAge() >= 21) 
  6.           .filter(it -> it.getName().startsWith("P")) 
  7.           .map(Person::getName) 
  8.           .sorted() 
  9.           .forEach(System.out::println); 
  10. @Data class Person { 
  11.     final String name
  12.     final int age; 

上面的例子结合了 Lombok、Retrolambda 和 Lightweight-Stream-API,它看起来几乎和 Kotlin 一样棒。使用静态工厂方法允许您将任何 Iterable 转换为流,并对其应用 lambda,就像 Java 8 流一样。 将静态调用 Stream.of(persons) 包装为 Iterable 类型的扩展函数是完美的,但是 Java 不支持它。

扩展函数

扩展机制提供了向类添加功能而无需继承它的能力。 这个众所周知的概念非常适合 Android 世界,这就是 Kotlin 在该社区很受欢迎的原因。

有没有技术或魔术将扩展功能添加到你的 Java 工具箱? 因 Lombok,你可以使用它们作为一个实验功能。 根据 Lombok 文档的说明,他们想把它从实验状态移出,基本上没有什么变化的话很快。 让我们重构最后一个例子,并将 Stream.of(persons) 包装成扩展函数。

  1. import lombok.Data; 
  2. import lombok.experimental.ExtensionMethod; 
  3. @ExtensionMethod(Streams.class) 
  4. public class Foo { 
  5.     void foo(List<Person> persons) { 
  6.         persons.toStream() 
  7.                .filter(it -> it.getAge() >= 21) 
  8.                .filter(it -> it.getName().startsWith("P")) 
  9.                .map(Person::getName) 
  10.                .sorted() 
  11.                .forEach(System.out::println); 
  12.     } 
  13. @Data class Person { 
  14.     final String name
  15.     final int age; 
  16. class Streams { 
  17.     static <T> Stream<T> toStream(List<T> list) { 
  18.         return Stream.of(list); 
  19.     } 

所有的方法是 public、static 的,并且至少有一个参数的类型不是原始的,因而是扩展方法。 @ExtensionMethod 注解允许你指定一个包含你的扩展函数的类。 你也可以传递数组,而不是使用一个 .class 对象。

我完全知道我的一些想法是非常有争议的,特别是 Lombok,我也知道,有很多的库,可以使你的生活更轻松。请不要犹豫在评论里分享你的经验。干杯!

【编辑推荐】

  1. Android的滑动分析以及各种实现
  2. Android图片放大缩小动画,竟如此简单
  3. Android带你解析ScrollView–仿QQ空间标题栏渐变
  4. 深入理解Android的渲染机制
  5. Android分辨率适配小试牛刀
【责任编辑:枯木 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

热门职位+更多

读 书 +更多

PHP和MySQL Web开发(原书第3版)

本书将介绍如何创建可交互的Web站点,包括从最简单的订单表单到复杂的安全电子商务站点。而且,读者还将了解如何使用开放源代码技术来实现...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊
× 大数据高端培训 仅限10人