TranslateProject/translated/tech/20161221 Living Android without Kotlin.md

9.7 KiB
Raw Blame History

没有Kotlin(Android)生活

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

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

下面有一些选择。

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

想象一下你在和产品负责人的斗争中失败作为一个专业的工程师在没有任何人的允许下你将不会去依靠和使用时髦的技术。我知道这听起来非常恐怖特别当你已经品尝到Kotlin的好处但是又没有失去你的希望。

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

数据类

你真的已经喜欢上Kotlin的数据类不是吗对于你来说得到equals(), hashCode(), toString()copy()这些类是很容易的。具体来说,data关键字还会按照声明顺序生成对应于属性的componentN()函数。 它们用于构造声明。

data class Person(val name: String)
val (riddle) = Person("Peter")
println(riddle)

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

val (riddle): String = Person("Peter").component1()
println(riddle) // prints Peter
view raw

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

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

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

import lombok.Data;

@Data class Person {
    final String name;
}

@ Data注解生成equalshashCodetoString。 此外它为所有字段创建getters为所有非最终字段创建setters并为所有必填字段finalals创建构造函数。 值得注意的是Lombok仅用于编译因此库代码不会添加到您的最终的.apk。

Lambda表达式

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

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

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

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

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

defaultConfig {
    jackOptions {
        enabled true
    }
}

compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}

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

dependencies {
    classpath 'me.tatarka:gradle-retrolambda:3.4.0'
}

apply plugin: 'me.tatarka.retrolambda'

compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}

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

数据操作

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

fun foo(persons: MutableList<Person>) {
    persons.filter { it.age >= 21 }
           .filter { it.name.startsWith("P") }
           .map { it.name }
           .sorted()
           .forEach(::println)
}

data class Person(val name: String, val age: Int)

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

void foo(List<Person> persons) {
    persons.stream()
           .filter(it -> it.getAge() >= 21)
           .filter(it -> it.getName().startsWith("P"))
           .map(Person::getName)
           .sorted()
           .forEach(System.out::println);
}

class Person {
    final private String name;
    final private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    String getName() { return name; }
    int getAge() { return age; }
}

这太好了以至于不是真的,所以在哪里捉住? 悲伤的是流从API 24 \可用。谷歌做的不错,但为什么广泛的应用程序有minSdkVersion = 24

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

import lombok.Data;
import com.annimon.stream.Stream;

void foo(List<Person> persons) {
    Stream.of(persons)
          .filter(it -> it.getAge() >= 21)
          .filter(it -> it.getName().startsWith("P"))
          .map(Person::getName)
          .sorted()
          .forEach(System.out::println);
}

@Data class Person {
    final String name;
    final int age;
}

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

扩展功能

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

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

import lombok.Data;
import lombok.experimental.ExtensionMethod;

@ExtensionMethod(Streams.class)
public class Foo {
    void foo(List<Person> persons) {
        persons.toStream()
               .filter(it -> it.getAge() >= 21)
               .filter(it -> it.getName().startsWith("P"))
               .map(Person::getName)
               .sorted()
               .forEach(System.out::println);
    }
}

@Data class Person {
    final String name;
    final int age;
}

class Streams {
    static <T> Stream<T> toStream(List<T> list) {
        return Stream.of(list);
    }
}

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


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


作者简介:

Coder and professional dreamer @ Grid Dynamics


via: https://hackernoon.com/living-android-without-kotlin-db7391a2b170#.q95i5232f

作者:Piotr Ślesarew 译者:DockerChen 校对:校对者ID

本文由 LCTT 原创编译,Linux中国 荣誉推出