mirror of
https://github.com/LCTT/TranslateProject.git
synced 2025-01-13 22:30:37 +08:00
commit
adce202c7d
@ -1,243 +0,0 @@
|
||||
Translating by DockerChen
|
||||
Living (Android) without Kotlin
|
||||
============================================================
|
||||
|
||||
![](https://cdn-images-1.medium.com/max/2000/1*Fd349rzh3XWwSbCP2IV7zA.jpeg)
|
||||
|
||||
> It is easier to get into something than to get out of it. — Donald Rumsfeld
|
||||
|
||||
Living without Kotlin is like playing Warcraft III on the touchpad. Buying a mouse is simple but what can you do if your new employer does not want to let you use Kotlin in production?
|
||||
|
||||
There are a few options.
|
||||
|
||||
* Fight with your product owner to obtain the rights to use Kotlin.
|
||||
* Use Kotlin and do not tell anyone about it because you know best what is best for you.
|
||||
* Wipe away your tears and use Java in all its glory.
|
||||
|
||||
Imagine that you lost the battle with your product owner and as a professional engineer, you will not lie and use hipster technology without anyone’s permission. I know that it sounds pretty scary especially when you have already tasted Kotlin but do not lose your hope.
|
||||
|
||||
In further parts of this article, I want to shortly describe some Kotlin features that you can apply to your Android Java code through some well-known tools and libraries. A rudimentary understanding of Kotlin and Java is required.
|
||||
|
||||
### Data classes
|
||||
|
||||
You really do love Kotlin’s data classes, don't you? It is so easy to get `equals()`, `hashCode()`, `toString()` and `copy()` generated for you. To be specific `data` keyword also generates `componentN()` functions corresponding to the properties in their order of declaration. They are used in destructuring declarations.
|
||||
|
||||
```
|
||||
data class Person(val name: String)
|
||||
val (riddle) = Person("Peter")
|
||||
println(riddle)
|
||||
```
|
||||
|
||||
Do you know what will be printed? For sure it will not be the value returned from the `toString()` of the `Person` class. Here is where the destructuring declaration comes to play and assigns a value from `name` to `riddle`. Using parenthesis for `(riddle)` compiler knows that it has to use destructuring declaration mechanism.
|
||||
|
||||
|
||||
```
|
||||
val (riddle): String = Person("Peter").component1()
|
||||
println(riddle) // prints Peter
|
||||
view raw
|
||||
```
|
||||
|
||||
>The code does not compile. It is just to show how does the destructuring declaration work.
|
||||
|
||||
As you can see `data` keyword is a super useful language feature so what can you do to bring it to your Java world? Use annotation processor and modify the Abstract Syntax Tree. If you want to go deeper please read the article listed at the end(Project Lombok — Trick Explained).
|
||||
|
||||
Using project Lombok you can achieve almost the same functionality that `data` keyword provides. Unfortunately, there is no way to have destructuring declarations.
|
||||
|
||||
```
|
||||
import lombok.Data;
|
||||
|
||||
@Data class Person {
|
||||
final String name;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
`@Data` annotation generates `equals()`, `hashCode()` and `toString()`. Additionally, it creates getters for all fields, setters for all non-final fields and constructor with all required fields(finals). It is worth to be aware of that Lombok is used just for compilation so the library code will not be added to your final `.apk`.
|
||||
|
||||
### Lambda expressions
|
||||
|
||||
Android engineers have a pretty tough life because of lack of Java 8 features and one of them are lambda expressions. Lambdas are great as they cut down tons of boilerplate for you. You can use them in your callbacks and streams. In Kotlin lambda expressions are built-in and they look way much better than they look in Java. Moreover, the bytecode of the lambda can be inserted directly into the bytecode of the calling method, so the method count does not increase. It can be done using inline functions.
|
||||
|
||||
```
|
||||
button.setOnClickListener { println("Hello World") }
|
||||
```
|
||||
|
||||
Lately Google announced support for Java 8 features in Android and thanks to Jack compiler you can use lambdas in your code. It is also good to mention that they are available on API level 23 and lower.
|
||||
|
||||
```
|
||||
button.setOnClickListener(view -> System.out.println("Hello World!"));
|
||||
```
|
||||
|
||||
How to enable them? Just add those several lines to your `build.gradle` file.
|
||||
|
||||
```
|
||||
defaultConfig {
|
||||
jackOptions {
|
||||
enabled true
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
```
|
||||
|
||||
If you are not a fan of Jack compiler or for some reasons you cannot use it, there is a different solution for you. Project Retrolambda lets you run Java 8 code with lambda expressions and method references on Java 7, 6 or 5 and here is the setup.
|
||||
|
||||
```
|
||||
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
|
||||
}
|
||||
```
|
||||
|
||||
As I mentioned before inline functions in lambdas in Kotlin do not increase method count but what about using them with Jack or Retrolambda? Obviously, they do not come for free and the hidden costs are listed below.
|
||||
|
||||
|
||||
![](https://cdn-images-1.medium.com/max/800/1*H7h2MB2auMslMkdaDtqAfg.png)
|
||||
|
||||
### Data manipulations
|
||||
|
||||
Kotlin introduces higher-order functions as a replacement for streams. They are extremely useful when you have to transform one set of data to another or filter the collection.
|
||||
|
||||
```
|
||||
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 are also provided by Google using Jack compiler. Unfortunately, Jack does not work with Lombok because it skips generating intermediate `.class` files when compiling the code and Lombok depends on these files.
|
||||
|
||||
```
|
||||
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; }
|
||||
}
|
||||
```
|
||||
|
||||
That is too good to be true so where is the catch? Sadly, streams are available from API 24\. Good job Google but in what universe apps have `minSdkVersion = 24`?
|
||||
|
||||
Fortunately Android platform has an awesome open source community which produces a lot of great libraries. Lightweight-Stream-API is one of them and it contains streams implementation based on iterators for Java 7 and below.
|
||||
|
||||
```
|
||||
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;
|
||||
}
|
||||
```
|
||||
|
||||
The sample above combines Lombok, Retrolambda, and Lightweight-Stream-API and it looks almost as good as Kotlin, doesn’t it. Using static factory method allows you to transform any Iterable into a stream and apply lambdas on it as on Java 8 streams. It would be perfect to wrap the static invocation `Stream.of(persons)` into extension function of Iterable type but Java does not support it.
|
||||
|
||||
### Extension functions
|
||||
|
||||
Extension mechanism provides an ability to add functionality to a class without having to inherit from it. This well-known concept fits great in the Android world and that is why Kotlin is so popular in the community.
|
||||
|
||||
Is there any technique or magic trick that brings extension functions to your Java toolbox? Thanks to Lombok you can use them as an experimental feature. According to what Lombok documentation says they want to move it out of experimental status with no or minor changes soon. Let’s refactor last sample and wrap `Stream.of(persons)` into extension function.
|
||||
|
||||
```
|
||||
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);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
All methods that are `public`, `static`, and have at least one argument whose type is not primitive, are considered extension methods. `@ExtensionMethod` annotation allows you to specify a class that contains your extension functions. Instead of using one `.class` object you can also pass an array.
|
||||
|
||||
* * *
|
||||
|
||||
I am fully aware that some of my thoughts are pretty controversial especially Lombok ones and I also know that there are a lot of other libraries that can make your life easier. Please do not hesitate to share your experience in comments. Cheers!
|
||||
|
||||
![](https://cdn-images-1.medium.com/max/800/1*peB9mmElOn6xwR3eH0HXXA.png)
|
||||
|
||||
|
||||
|
||||
---------------------------------
|
||||
|
||||
作者简介:
|
||||
|
||||
![](https://cdn-images-1.medium.com/fit/c/60/60/1*l7_L6VCKzkOm0gq4Kplnkw.jpeg)
|
||||
|
||||
Coder and professional dreamer @ Grid Dynamics
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://hackernoon.com/living-android-without-kotlin-db7391a2b170#.q95i5232f
|
||||
|
||||
作者:[Piotr Ślesarew][a]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://hackernoon.com/@piotr.slesarew?source=post_header_lockup
|
||||
[1]:http://jakewharton.com/exploring-java-hidden-costs/
|
||||
[2]:https://medium.com/u/8ddd94878165
|
||||
[3]:https://projectlombok.org/index.html
|
||||
[4]:https://github.com/aNNiMON/Lightweight-Stream-API
|
||||
[5]:https://github.com/orfjackal/retrolambda
|
||||
[6]:http://notatube.blogspot.com/2010/11/project-lombok-trick-explained.html
|
||||
[7]:http://notatube.blogspot.com/2010/11/project-lombok-trick-explained.html
|
||||
[8]:https://twitter.com/SliskiCode
|
243
translated/tech/20161221 Living Android without Kotlin.md
Normal file
243
translated/tech/20161221 Living Android without Kotlin.md
Normal file
@ -0,0 +1,243 @@
|
||||
没有Kotlin(Android)生活
|
||||
============================================================
|
||||
|
||||
![](https://cdn-images-1.medium.com/max/2000/1*Fd349rzh3XWwSbCP2IV7zA.jpeg)
|
||||
|
||||
> 开始投入一件事比远离它更容易。 — 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()` 返回的值。这是构造声明的作用,它赋值从`name`到`riddle`。使用园括号`(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`注解生成`equals()`,`hashCode()`和`toString()`。 此外,它为所有字段创建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 7,6或者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下使用它们? 显然,它们不是免费的,隐藏的成本如下。
|
||||
|
||||
|
||||
|
||||
![](https://cdn-images-1.medium.com/max/800/1*H7h2MB2auMslMkdaDtqAfg.png)
|
||||
|
||||
### 数据操作
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
```
|
||||
|
||||
上面的例子结合了Lombok,Retrolambda和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);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
所有的方法是`public`,`static`,并且至少有一个参数的类型不是原始的,被认为是扩展方法。 `@ ExtensionMethod`注解允许你指定一个包含你的扩展函数的类。 而不是使用一个`.class`对象,你也可以传递数组。
|
||||
|
||||
* * *
|
||||
|
||||
我完全知道我的一些想法是非常有争议的,特别是Lombok,我也知道,有很多的库,可以使你的生活更轻松。请不要犹豫在评论里分享你的经验。干杯!
|
||||
|
||||
|
||||
![](https://cdn-images-1.medium.com/max/800/1*peB9mmElOn6xwR3eH0HXXA.png)
|
||||
|
||||
|
||||
|
||||
---------------------------------
|
||||
|
||||
作者简介:
|
||||
|
||||
![](https://cdn-images-1.medium.com/fit/c/60/60/1*l7_L6VCKzkOm0gq4Kplnkw.jpeg)
|
||||
|
||||
Coder and professional dreamer @ Grid Dynamics
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://hackernoon.com/living-android-without-kotlin-db7391a2b170#.q95i5232f
|
||||
|
||||
作者:[Piotr Ślesarew][a]
|
||||
译者:[DockerChen](https://github.com/DockerChen)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://hackernoon.com/@piotr.slesarew?source=post_header_lockup
|
||||
[1]:http://jakewharton.com/exploring-java-hidden-costs/
|
||||
[2]:https://medium.com/u/8ddd94878165
|
||||
[3]:https://projectlombok.org/index.html
|
||||
[4]:https://github.com/aNNiMON/Lightweight-Stream-API
|
||||
[5]:https://github.com/orfjackal/retrolambda
|
||||
[6]:http://notatube.blogspot.com/2010/11/project-lombok-trick-explained.html
|
||||
[7]:http://notatube.blogspot.com/2010/11/project-lombok-trick-explained.html
|
||||
[8]:https://twitter.com/SliskiCode
|
Loading…
Reference in New Issue
Block a user