天天看點

【Java技術專題】「Guava開發指南」手把手教你如何進行使用

作者:IT技術控
【Java技術專題】「Guava開發指南」手把手教你如何進行使用

Guava中的Preconditions(前置條件判斷)

Preconditions(前置條件):讓方法調用的前置條件判斷更簡單。

Guava在Preconditions 類中提供了若幹前置條件判斷的實用方法,我們強烈建議在 Eclipse 中靜态導入這些方法。每個方法都有三個變種:

  1. 當方法沒有額外參數時,抛出的異常中不包含錯誤消息,這會使得調用方很難确定發生了什麼錯誤。是以,我們應該在抛出異常時,為其提供一個有意義的錯誤消息。
  2. 當方法有一個 Object 對象作為額外參數時,我們可以使用該對象的 toString() 方法作為錯誤消息。這樣可以保證錯誤消息能夠包含額外參數的資訊,友善調用方定位問題。
  3. 當方法有一個 String 對象作為額外參數,并且有一組任意數量的附加 Object 對象時,我們可以采用類似 printf 的方式來處理異常消息。由于考慮到 GWT 的相容性和效率,我們隻支援 %s 訓示符。是以,我們可以在錯誤消息中使用 %s 來表示 String 參數,并使用 String.format() 方法将附加參數格式化為字元串,然後将其插入到錯誤消息中。

綜上所述,無論是否有額外參數,我們都應該為抛出的異常提供有意義的錯誤消息,以便調用方能夠更好地了解和解決問題。在處理異常消息時,我們應該根據具體情況選擇合适的方式,以保證代碼的可讀性和可維護性。

實際代碼案例

java複制代碼checkArgument(i >= 0, "Argument was %s but expected nonnegative", i);
checkArgument(i < j, "Expected i < j, but %s > %s", i, j);
           

判斷邏輯參數場景

方法聲明(不包括額外參數) 描述 檢查失敗時抛出的異常
checkArgument(boolean) 檢查 boolean 是否為 true,用來檢查傳遞給方法的參數。 IllegalArgumentException
checkNotNull(T) 檢查 value 是否為 null,該方法直接傳回 value,是以可以内嵌使用 checkNotNull? NullPointerException
checkState(boolean) 用來檢查對象的某些狀态。 IllegalStateException
checkElementIndex(int index, int size) 檢查 index 作為索引值對某個清單、字元串或數組是否有效。index>=0 && index<size IndexOutOfBoundsException
checkElementIndex(int index, int size) 檢查 index 作為索引值對某個清單、字元串或數組是否有效。index>=0 && index<size IndexOutOfBoundsException
checkPositionIndex(int index, int size) 檢查 index 作為位置值對某個清單、字元串或數組是否有效。index>=0 && index<=size IndexOutOfBoundsException
checkPositionIndexes(int start, int end, int size) 檢查[start, end]表示的位置範圍對某個清單、字元串或數組是否有效 IndexOutOfBoundsException
提示:索引值常用來查找清單、字元串或數組中的元素,如 List.get(int), String.charAt(int) 位置值和位置範圍常用來截取清單、字元串或數組,如 List.subList(int,int), String.substring(int) 相比 Apache Commons 提供的類似方法,我們把 Guava 中的 Preconditions 作為首選。

相比其他API的優勢和好處

在靜态導入後,Guava 方法非常清楚明晰。checkNotNull 清楚地描述做了什麼,會抛出什麼異常;

  • checkNotNull 直接傳回檢查的參數,讓你可以在構造函數中保持字段的單行指派風格:this.field = checkNotNull(field)
  • 簡單的、參數可變的 printf 風格異常資訊。鑒于這個優點,JDK7 已經引入Objects.requireNonNull 的情況下,我們仍然建議你使用checkNotNull。

在編碼時,如果某個值有多重的前置條件,我們建議你把它們放到不同的行,這樣有助于在調試時定位。此外,把每個前置條件放到不同的行,也可以幫助你編寫清晰和有用的錯誤消息。

Guava中的Preconditions(常見Object方法)

提示:可闡述程式設計學習方法

equals

當一個對象中的字段可以為 null 時,實作 Object.equals 方法會很痛苦,因為不得不分别對它們進行 null 檢查。使用 Objects.equal 幫助你執行 null 敏感的 equals 判斷,進而避免抛出 NullPointerException。例如:

java複制代碼Objects.equal("a", "a"); // returns true
Objects.equal(null, "a"); // returns false
Objects.equal("a", null); // returns false
Objects.equal(null, null); // returns true
           
注意:JDK7 引入的 Objects 類提供了一樣的方法 Objects.equals。

hashCode

用對象的所有字段作散列[hash]運算應當更簡單。Guava 的 Objects.hashCode(Object...)會對傳入的字段序列計算出合理的、順序敏感的散列值。你可以使用 Objects.hashCode(field1, field2, …, fieldn)來代替手動計算散列值。

注意:JDK7 引入的 Objects 類提供了一樣的方法 Objects.hash(Object...)

toString

toString方法在調試時是無價之寶,但是編寫 toString 方法有時候卻很痛苦。使用 Objects.toStringHelper 可以輕松編寫有用的 toString 方法。例如:

java複制代碼// Returns "ClassName{x=1}"
Objects.toStringHelper(this).add("x", 1).toString();
// Returns "MyObject{x=1}"
Objects.toStringHelper("MyObject").add("x", 1).toString();
           

compare/compareTo

實作一個比較器[Comparator],或者直接實作 Comparable 接口有時也傷不起。考慮一下這種情況:

java複制代碼class Person implements Comparable<Person> {
private String lastName;
private String firstName;
private int zipCode;
public int compareTo(Person other) {
		int cmp = lastName.compareTo(other.lastName);
		if (cmp != 0) {
			return cmp;
		}
		cmp = firstName.compareTo(other.firstName);
		if (cmp != 0) {
			return cmp;
		}
		return Integer.compare(zipCode, other.zipCode);
	}
}
           

這部分代碼太瑣碎了,是以很容易搞亂,也很難調試。我們應該能把這種代碼變得更優雅,為此,Guava 提供了ComparisonChain。

ComparisonChain

ComparisonChain 執行一種懶比較:它執行比較操作直至發現非零的結果,在那之後的比較輸入将被忽略。

java複制代碼public int compareTo(Foo that) {
	return ComparisonChain.start()
		.compare(this.aString, that.aString)
		.compare(this.anInt, that.anInt)
		.compare(this.anEnum, that.anEnum, Ordering.natural().nullsLast())
		.result();
}
           

這種Fluent接口風格的可讀性更高,發生錯誤編碼的幾率更小,并且能避免做不必要的工作。更多 Guava 排序器工具可以在下一節裡找到。

Guava中的排序器[Ordering]

Guava 強大的”流暢風格比較器” 是 Guava 流暢風格比較器[Comparator]的實作,它可以用來為建構複雜的比較器,以完成集 合排序的功能。

從實作上說,Ordering 執行個體就是一個特殊的 Comparator 執行個體。Ordering 把很多基于 Comparator 的靜态方法(如 Collections.max)包裝為自己的執行個體方法(非靜态方法),并且提供了鍊式調用方法,來定制和增強現有的比較器。

建立排序器:常見的排序器可以由下面的靜态方法建立

方法聲明(不包括額外參數) 描述
natural() 對可排序類型做自然排序,如數字按大小,日期按先後排序。
usingToString() 按對象的字元串形式做字典排序[lexicographical ordering]。
natural() 對可排序類型做自然排序,如數字按大小,日期按先後排序。
from(Comparator) 把給定的 Comparator 轉化為排序器。

實作自定義的排序器時,除了用上面的 from 方法,也可以跳過實作 Comparator,而直接繼承 Ordering:

java複制代碼Ordering<String> byLengthOrdering = new Ordering<String>() {
	public int compare(String left, String right) {
		return Ints.compare(left.length(), right.length());
	}
};
           

鍊式調用方法:通過鍊式調用,可以由給定的排序器衍生出其它排序器

方法聲明(不包括額外參數) 描述
reverse() 擷取語義相反的排序器。
nullsFirst() 使用目前排序器,但額外把 null 值排到最前面。
nullsLast() 使用目前排序器,但額外把 null 值排到最後面。
compound(Comparator) 合成另一個比較器,以處理目前排序器中的相等情況。
lexicographical() 基于處理類型 T 的排序器,傳回該類型的可疊代對象 Iterable的排序器。
onResultOf(Function) 對集合中元素調用 Function,再按傳回值用目前排序器排序。

例如,你需要下面這個類的排序器。

java複制代碼class Foo {
	@Nullable String sortedBy;
	int notSortedBy;
}
           

考慮到排序器應該能處理 sortedBy 為 null 的情況,我們可以使用下面的鍊式調用來合成排序器:

java複制代碼Ordering<Foo> ordering = Ordering.natural().nullsFirst().onResultOf(new Function<Foo, String>() {
	public String apply(Foo foo) {
		return foo.sortedBy;
	}
});
           

當閱讀鍊式調用産生的排序器時,應該從後往前讀。上面的例子中,排序器首先調用 apply 方法擷取 sortedBy值,并把 sortedBy 為 null 的元素都放到最前面,然後把剩下的元素按 sortedBy 進行自然排序。之是以要從後往前讀,是因為每次鍊式調用都是用後面的方法包裝了前面的排序器。

注:用 compound 方法包裝排序器時,就不應遵循從後往前讀的原則。為了避免了解上的混亂,請不要把 compound 寫在一長串鍊式調用的中間,你可以另起一行,在鍊中最先或最後調用 compound。超過一定長度的鍊式調用,也可能會帶來閱讀和了解上的難度。我們建議按下面的代碼這樣,在一個鍊中最多使用三個方法。此外,你也可以把 Function 分離成中間對象,讓鍊式調用更簡潔緊湊。
java複制代碼Ordering<Foo> ordering = Ordering.natural().nullsFirst().onResultOf(sortKeyFunction)
           

運用排序器:Guava的排序器實作有若幹操縱集合或元素值的方法。

方法聲明(不包括額外參數) 描述
greatestOf(Iterable iterable, int k) 擷取可疊代對象中最大的k個元素,相反最小:leastOf。
isOrdered(Iterable) 判斷可疊代對象是否已按排序器排序:允許有排序值相等的元素,isStrictlyOrdered。
sortedCopy(Iterable) 判斷可疊代對象是否已嚴格按排序器排序:不允許排序值相等的元素,immutableSortedCopy。
min(E, E) 傳回兩個參數中最小的那個。如果相等,則傳回第一個參數,max(E, E)。
min(E, E, E, E...) 傳回多個參數中最小的那個。如果有超過一個參數都最小,則傳回第一個最小的參數,max(E, E, E, E...)
min(Iterable) 傳回疊代器中最小的元素。如果可疊代對象中沒有元素,則抛出 NoSuchElementException,max(Iterable), min(Iterator), max(Iterator)。

各位大佬覺得寫的不錯的話,記得關注+點贊支援支援!

繼續閱讀