【译】Effective Java for Android
原文:https://medium.com/rocknnull/effective-java-for-android-cheatsheet-bf4e3433889a#.27whf6kim
Effective Java被很多人看作是写出可维护和高效率Java代码的重要著作之一。Android使用Java开发是不是意味着里面的所有建议都必须使用呢?不全是。 一些 人 认为这本书的绝大多数建议在Android开发的世界里都是不适用的。在我看来并不是这样。我相信这本书的部分建议并不适用,不管是因为不是所有Java特性都已优化在安卓上使用(比如 enums, serialization, 等等),还是因为移动设备的局限性(比如 Dalvik/ART和桌面JVM表现出不同的行为)。但不管怎么说,这本书的大多数范例都可以稍加修改或者不加修改的使用,从而建立一个更加健康,干净,可维护的代码库。
这篇文章总结一些我认为该书对于安卓开发最重要的知识点。对于读过这本书的人来说,就当回忆一下,没读过的也可以从中品尝到该书的内容是什么样的。
强制不可实例化
如果你不希望使用new关键字来创建一个对象,强制它使用私有构造器。这对那些只有静态函数的工具类来说尤其有用。
class MovieUtils {
private MovieUtils() {}
static String titleAndYear(Movie movie) {
\[...\]
}
}
静态工厂
不使用new和构造器,使用一个静态工厂方法(以及一个private的构造器)。这些工厂方法是可以命名的,无需每次都返回对象实例,而且更具需要可以返回不同的派生类型。
class Movie {
\[...\]
public static Movie create(String title) {
return new Movie(title);
}
}
Builders
当你的对象需要3个以上的构造参数时,使用builder来构造这个对象。写起来可能有点啰嗦但是扩展性和可读性都更好。如果你是创建一个值类型,考虑AutoValue。
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();
尽量使用不可变对象
不可变对象永远不会发生改变,其字段的值只在构造函数运行时设置一次,其后就不会再改变。这样做有几个好处,比如简单,线程安全以及可共享。
class Movie {
\[...\]
Movie sequel() {
return Movie.create(this.title + " 2");
}
}
// Use like this:
Movie toyStory = Movie.create("Toy Story");
Movie toyStory2 = toyStory.sequel();
很难做到每个类都是不可变的。对于那些不能做到不可变的类,我们也可以让它尽量接近于不可变(比如,用private final 声明变量,final声明类)。移动设备上的对象开销很昂贵,所以别过度使用。
不可变对象的解释参见: https://my.oschina.net/zzw922cn/blog/487610 - 译注
静态成员类
如果你要定义一个不依赖外部类的内部类,别忘了把它定义为static。不然每个内部类的实例都会有一个外部类的引用。
class Movie {
\[...\]
static class MovieAward {
\[...\]
}
}
泛型(几乎)无处不在
Java是类型安全的,我们应该对此心存感激(看看JS吧)。避免使用 raw types或者Object type。泛型提供了让代码在编译时类型安全的机制。
// 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
List sort(List input) {
\[...\]
}
// DO
<T> List<T> sort(List<T> input) {
\[...\]
}
为了更加灵活,你还可以使用 bounded wildcards(限界通配符)限制可以接收的类型范围。
// 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
当必须返回一个空的list/collection 的时候避免使用null。返回一个空的集合可以让接口看起来更简单(无需文档和@NotNull 注解)和防止偶然的空指针异常。建议返回同一个空集合而不是创建一个新的。
List<Movie> latestMovies() {
if (db.query().isEmpty()) {
return Collections.emptyList();
}
\[...\]
}
不要使用 + String
要连接几个字符串,+号也许可行。但是在太多的字符串链接上使用;这样性能真的很差,建议使用StringBuilder。
String latestMovieOneLiner(List<Movie> movies) {
StringBuilder sb = new StringBuilder();
for (Movie movie : movies) {
sb.append(movie);
}
return sb.toString();
}
可恢复异常
我并不喜欢抛出异常来提示错误,但是如果你这样做的话,确保异常是受检查和可恢复的。
List<Movie> latestMovies() throws MoviesNotFoundException {
if (db.query().isEmpty()) {
throw new MoviesNotFoundException();
}
\[...\]
}
总结
列举的这些绝不是这本书所有建议的列表,也不是深入评价之后的简短解释。只是一些有用建议的小抄而已。