【译】Effective Java for Android

原文:https://medium.com/rocknnull/effective-java-for-android-cheatsheet-bf4e3433889a#.27whf6kim 

CE6A2C95-DB4C-4ECA-ABAD-B8CFC5DEF01F.png

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();
    }
    \[...\]
}

总结

列举的这些绝不是这本书所有建议的列表,也不是深入评价之后的简短解释。只是一些有用建议的小抄而已。