原文地址:effective-java-for-android

原文作者:Netcyrax

Effective Java is considered by many, one of the most important books for writing Java code that is maintainable in the long run and efficient at the same time. Android is using Java and that means that all of the advice given in the book must be applicable, right? Not exactly. Some people consider that most of the advice given by the book are not applicable in the Android development world. In my opinion, this is not the case. I believe that there are parts of the book that are not applicable, either because not all Java features are optimized to be used with Android (e.g. enums, serialization, etc) or due to the limitations that a mobile device has (e.g. Dalvik/ART behaves differently than a desktop JVM) . Nevertheless, most of the paradigms in the book can be used with little to no modification and can contribute to a healthier, cleaner and more maintainable code base.

《Effective Java》 这本书被许多人认为是编写长期可维护的且高效的 Java 代码最重要的指导书籍之一,Android 开发使用的语言是 Java,这意味着本书中提供的所有建议都适用吗?并不完全是,有些人认为本书提供的大部分建议在 Android 开发世界中都不适用。在我看来,情况并非如此。 我认为这本书的一些部分是不适用的,因为并不是所有的 Java 功能都被优化为与 Android(例如枚举,序列化等)一起使用,或者由于移动设备的限制(例如 Dalvik / ART 行为与桌面 JVM 不同)。 尽管如此,这本书中的大部分范例都可以很少使用,也可以不进行任何修改,并且可以帮助建立更健康、更干净、更易于维护的代码库。

This post is an attempt to concentrate what I consider the most important items from the book when developing an Android app. For those who read the book, this post might act as a reminder of the items/principles that are mentioned. For those who didn’t read it (yet), it can be used to give them a taste of what the book offers.

这篇文章是我在开发 Android 应用程序时,从这本书里面获取的最重要的部分。 对于那些阅读这本书的人来说,这篇文章可能会提醒您提及的项目/原则。 对于那些还没有阅读它的人,本文可以从另外的角度来带你品位这本书。

并非每一行都需要翻译,相信很多人都看得懂,或者结合代码一下就知道是什么意思。关键是作者将这些提炼出来了,各位可以根据自己的理解消化吸收。

Force non-instantiability (强制不可实例化)

If you do not want an object to be created using the new keyword, enforce it using a private constructor. Especially useful for utility classes that contain only static functions.

如果你不希望一个对象可以通过 new 关键字来创建,那么用就使用 privte 构造函数, 尤其适用于那些只包含 static 方法的工具类:

1
2
3
4
5
6
class MovieUtils {
private MovieUtils() {}
static String titleAndYear(Movie movie) {
[...]
}
}

Static Factories (静态工厂方法)

Instead of using the new keyword and the constructor use a static factory method (and a private constructor). These factory methods are named, are not required to return a new instance of the object each time and can return a different subtype depending on what’s needed.

1
2
3
4
5
6
class Movie {
[...]
public static Movie create(String title) {
return new Movie(title);
}
}

[Update] A useful tip by our reader @stsdema28: Using static factories can make testing difficult. If that’s the case, consider using non-static factories that you can mock during testing (or a factory interface that a fake can implement).

使用静态工厂方法会使得测试变得困难,在这种情况下,可以考虑使用非 static 的工厂方法,这样在测试的时候可以模拟创建,

Builders

When you have an object that requires more than ~3 constructor parameters, use a builder to construct the object. It might be a little more verbose (啰嗦的,冗长的) to write but it scales well and it’s very readable. If you are creating a value class, consider AutoValue.

如果你的对象有超过三个构造参数,考虑使用 builder ,虽然写起来比较长,但是易扩展易读。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Movie {
static Builder newBuilder() {
return new Builder();
}
static class Builder {
String title;
Builder withTitle(String title) {
this.title = title;
return this;
}
Movie build() {
return new Movie(title);
}
}
private Movie(String title) {
[...]
}
}
// Use like this:
Movie matrix = Movie.newBuilder().withTitle("The Matrix").build();

Avoid mutability(可变性)

Immutable(不可变) is an object that stays the same for its entire lifetime. All the necessary data of the object are provided during its creation. There are various advantages to this approach like simplicity, thread-safety and shareability.

不可变性指的是一个对象在其生命周期中保持不变,所有的必要的数据都是在其创建的时候就生成好了,这种方法有多种优点,如简单,线程安全性和可共享性。

1
2
3
4
5
6
7
8
9
class Movie {
[...]
Movie sequel() {
return Movie.create(this.title + " 2");
}
}
// Use like this:
Movie toyStory = Movie.create("Toy Story");
Movie toyStory2 = toyStory.sequel();

It might be difficult to make every class immutable. If that’s the case, make your class as immutable as possible (eg, fields private final and the class final). Object creation might be more expensive on mobile, therefore don’t over do it.

使每一个 class 都不可改变可能是困难的。 如果是这样,请使您的 class 尽可能不变(例如,field 使用 private final 修饰, class 使用 final 修饰 )

Static member classes (静态内部类)

If you define an inner class that does not depend on the outer class, don’t forget to define it as static. Failing to do so will result in each instance of the inner class to have references to the outer class.

如果你定义的内部类并不依赖于外部类,别忘了把这个内部类置为 static,否则的话,内部类的每一个实例都会持有外部类的引用。

1
2
3
4
5
6
class Movie {
[...]
static class MovieAward {
[...]
}
}

Generics (almost) everywhere

Java provides type-safety and we should be grateful for it (see JS). Try to avoid using raw types or the Object type when possible. Generics provide, most of the times the mechanism to make your code type safe on compile time.

1
2
3
4
5
6
7
8
9
10
11
// DON'T
List movies = Lists.newArrayList();
movies.add("Hello!");
[...]
String movie = (String) movies.get(0);
// DO
List<String> movies = Lists.newArrayList();
movies.add("Hello!");
[...]
String movie = movies.get(0);

Don’t forget that you can use generics and in your methods for parameters and returned values.

1
2
3
4
5
6
7
8
9
// DON'T
List sort(List input) {
[...]
}
// DO
<T> List<T> sort(List<T> input) {
[...]
}

To be a little more flexible you can use bounded wildcards to extend the range of types that you accept.

1
2
3
4
5
6
7
8
9
10
11
12
13
// Read stuff from collection - use "extends"
void readList(List<? extends Movie> movieList) {
for (Movie movie : movieList) {
System.out.print(movie.getTitle());
[...]
}
}
// Write stuff to collection - use "super"
void writeList(List<? super Movie> movieList) {
movieList.add(Movie.create("Se7en"));
[...]
}

Return empty

When having to return a list/collection with no result avoid null. Returning an empty collection leads to a simpler interface (no need to document and annotate the null-returning function) and avoids accidental NPE. Prefer to return the same empty collection rather than creating a new one.

1
2
3
4
5
6
List<Movie> latestMovies() {
if (db.query().isEmpty()) {
return Collections.emptyList();
}
[...]
}

No + String

Having to concatenate a few Strings, + operator might do. Never use it for a lot of String concatenations; the performance is really bad. Prefer a StringBuilder instead.

少用 + 来连接字符串,除非很少的字符串相连。 比较多的字符串用 + 的话,性能会很差,推荐用 StringBuilder 替代。

1
2
3
4
5
6
7
String latestMovieOneLiner(List<Movie> movies) {
StringBuilder sb = new StringBuilder();
for (Movie movie : movies) {
sb.append(movie);
}
return sb.toString();
}

Recoverable (能回收的) exceptions

I am not in favor of throwing exceptions for indicating(标识) errors, but if you do so, make sure the exception is checked and the catcher of the exception is able to recover from the error.

1
2
3
4
5
6
List<Movie> latestMovies() throws MoviesNotFoundException {
if (db.query().isEmpty()) {
throw new MoviesNotFoundException();
}
[...]
}

Conclusion (结尾)

This list is by no mean a complete list of the advice that is given in the book nor the short text explanations full-depth justifications. This was rather a cheat sheet for these useful tips :)