Android 版《Effective Java》
翻譯原文 Effective Java for Android
《Effective Java》是公認的Java最重要的書籍之一,該書介紹了如何編寫長期可維護的代碼,同時又兼顧效率。Android應(yīng)用是使用Java進行開發(fā),那是否意味著這本書中的建議都可以應(yīng)用到Android開發(fā)中?并未如此。有些人認為書中的大部分建議無法應(yīng)用到Android開發(fā)世界中。因為Android沒有優(yōu)化Java的所有特性(比如說:枚舉,本地化等)或是手機設(shè)備的限制(比如 Dalvik/ART 與桌面JVM的差異),書中的一部分內(nèi)容無法應(yīng)用到Android開發(fā)中。盡管如此,書中的大部分范式還是可以經(jīng)過少許的修改或者直接使用,用以構(gòu)建更加健康的、清晰的、可維護的代碼基礎(chǔ)。
這篇博客將嘗試關(guān)注書中那些我認為對于Android開發(fā)比較重要的條目。
Force non-instantiability
如果你不希望對象被new創(chuàng)建,可以使用私有的構(gòu)造函數(shù)。對于那些只有靜態(tài)方法的工具類特別有用。
class MovieUtils {
private MovieUtils() {}
static String titleAndYear(Movie movie) {
[...]
}
}
Static Factories
不使用new以及構(gòu)造函數(shù),而是使用靜態(tài)工廠方法(以及私有構(gòu)造函數(shù))。這些工廠方法被賦予名字,不要求每次都返回一個新的實例,并且可以根據(jù)要求來返回不同的子類型。
class Movie {
[...]
public static Movie create(String title) {
return new Movie(title);
}
}
Builder
當(dāng)你有一個對象的構(gòu)造函數(shù)包含3個以上的參數(shù)時,使用Builder來構(gòu)建對象。這樣做有些繁瑣,但是更容易擴展并且可讀性更好。如果你要創(chuàng)建一個數(shù)據(jù)類(value class), 那么考慮下使用Auto Value。
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
不可變,是說對象在其整個生命周內(nèi)保持一致。對象中所有的必要數(shù)據(jù)都在創(chuàng)建時提供,這種方式有簡潔性,線程安全,可分享(shareability)等優(yōu)點。
class Movie {
[...]
Movie sequel() {
return Movie.create(this.title + " 2");
}
}
// Use like this:
Movie toyStory = Movie.create("Toy Story");
Movie toyStory2 = toyStory.sequel();
把所有的類都設(shè)計成不可變是不可能的。盡管如此,還是要把類盡可能的設(shè)計成不可變(比如,private final域以及final 類)。手機上創(chuàng)建類會有更多開銷,所以要適量。
Static member classes
如果你定義的內(nèi)部類不依賴于外部類,不要忘記把它定義成靜態(tài)的。否則的話,內(nèi)部類的每個實例都會包含一個外部類的引用。
class Movie {
[...]
static class MovieAward {
[...]
}
}
Generics(almost)everywhere
我們應(yīng)該感謝Java提供的類型安全(請參見JavaScript)。盡可能的避免使用原始類型或類。泛型提供了在編譯時確保類型安全的機制。
// 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);
不要忘記可是在方法的參數(shù)以及返回中使用泛型
// DON'T
List sort(List input) {
[...]
}
// DO
<T> List<T> sort(List<T> input) {
[...]
}
再復(fù)雜一點的情況,你可以使用通配符來擴展類型范圍。
// 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
當(dāng)不得不返回沒有數(shù)值的list或者collection時,不應(yīng)該返回null。返回一個空集合,使其指向一個比較簡單接口(不需要編寫文檔或者使用非空返回注解功能),這樣避免了引發(fā) NullPointException。盡量返回同一個集合,而不是創(chuàng)建一個新的。
// 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"));
[...]
}
No + String
需要鏈接少量的String時,可以使用+。 當(dāng)需要鏈接大量的字符串變量時,不要使用,因為性能很差。這個時候使用StringBuilder來代替。
String latestMovieOneLiner(List<Movie> movies) {
StringBuilder sb = new StringBuilder();
for (Movie movie : movies) {
sb.append(movie);
}
return sb.toString();
}
Recoverable exceptions
我個人不喜歡在檢查到錯誤是拋出異常,但是你要這樣做的話,要保證異常是可檢測的,并且異常的捕獲者可以修復(fù)這個錯誤。
String latestMovieOneLiner(List<Movie> movies) {
StringBuilder sb = new StringBuilder();
for (Movie movie : movies) {
sb.append(movie);
}
return sb.toString();
}
總結(jié)
這份列表并不是書中建議的全部列表,也不是簡短的深度解讀。這是一份備忘錄。