Google Guavaを使用したクリーンコード

おそらく、プログラマーは、ビジネスロジックの真ん中で多くの繰り返しと「低レベル」アクションの実装に満ちたコードを見たことがあるでしょう。 たとえば、レポートを印刷するメソッドの途中には、文字列を連結する次のようなコードがあります。



StringBuilder sb = new StringBuilder(); for (Iterator<String> i = debtors.iterator(); i.hasNext();) { if (sb.length() != 0) { sb.append(", "); } sb.append(i.next()); } out.println("Debtors: " + sb.toString());
      
      





このコードはより単純である可能性があることは明らかです。たとえば、Java 8では次のように記述できます。



 out.println("Debtors: " + String.join(", ", debtors));
      
      





そのため、何が起きているかがすぐにわかります。 Google Guavaは、これらの一般的なコードパターンを取り除くのに役立つJava用のオープンソースライブラリのセットです。 GuavaはJava 8よりもずっと前に登場したため、Guavaには文字列を連結する方法もあります:Joiner.on( "、").join(債務者)。



非常に基本的なユーティリティ



基本的なJavaメソッドの標準セットを実装する単純なクラスを見てみましょう。 時間を無駄にしないために、hashCode、equals、toString、compareToメソッド(最初の3つはEclipseで生成しました)の実装を特に掘り下げることはお勧めしませんが、コードの量を見てください。



 class Person implements Comparable<Person> { private String lastName; private String middleName; private String firstName; private int zipCode; // constructor, getters and setters are omitted @Override public int compareTo(Person other) { int cmp = lastName.compareTo(other.lastName); if (cmp != 0) { return cmp; } cmp = middleName.compareTo(other.middleName); if (cmp != 0) { return cmp; } cmp = firstName.compareTo(other.firstName); if (cmp != 0) { return cmp; } return Integer.compare(zipCode, other.zipCode); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (firstName == null) { if (other.firstName != null) return false; } else if (!firstName.equals(other.firstName)) return false; if (lastName == null) { if (other.lastName != null) return false; } else if (!lastName.equals(other.lastName)) return false; if (middleName == null) { if (other.middleName != null) return false; } else if (!middleName.equals(other.middleName)) return false; if (zipCode != other.zipCode) return false; return true; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((firstName == null) ? 0 : firstName.hashCode()); result = prime * result + ((lastName == null) ? 0 : lastName.hashCode()); result = prime * result + ((middleName == null) ? 0 : middleName.hashCode()); result = prime * result + zipCode; return result; } @Override public String toString() { return "Person [lastName=" + lastName + ", middleName=" + middleName + ", firstName=" + firstName + ", zipCode=" + zipCode + "]"; } }
      
      





次に、GuavaとJava 8の新しいメソッドを使用して同様のコードを見てみましょう。



 class Person implements Comparable<Person> { private String lastName; private String middleName; private String firstName; private int zipCode; // constructor, getters and setters are omitted @Override public int compareTo(Person other) { return ComparisonChain.start() .compare(lastName, other.lastName) .compare(firstName, other.firstName) .compare(middleName, other.middleName, Ordering.natural().nullsLast()) .compare(zipCode, other.zipCode) .result(); } @Override public boolean equals(Object obj) { if (obj == null || getClass() != obj.getClass()) { return false; } Person other = (Person) obj; return Objects.equals(lastName, other.lastName) && Objects.equals(middleName, other.middleName) && Objects.equals(firstName, other.firstName) && zipCode == other.zipCode; } @Override public int hashCode() { return Objects.hash(lastName, middleName, firstName, zipCode); } @Override public String toString() { return MoreObjects.toStringHelper(this) .omitNullValues() .add("lastName", lastName) .add("middleName", middleName) .add("firstName", firstName) .add("zipCode", zipCode) .toString(); } }
      
      





ご覧のとおり、コードはより簡潔で簡潔になりました。 GuavaのMoreObjectsとComparisonChainおよびJava 8のObjectsクラスを使用します。Java7またはそれ以前のバージョンを使用している場合は、GuavaのObjectsクラスを使用できます。hashCodeと、javaクラスのhashメソッドおよびequalsメソッドに類似するequalメソッドがあります。 lang.Objects。 以前は、toStringHelperもObjectsクラスにありましたが、Guava 18でのJava 8の登場により、@ DeprecatedラベルがすべてのメソッドのObjectsクラスにハングアップし、Java 8に類似しないメソッドがMoreObjectsに転送され、名前の競合がなくなりました-Guava開発し、その開発者は時代遅れのコードを取り除くことに恥ずかしがり屋ではありません。



このバージョンのクラスは元のバージョンとは少し異なることに注意してください:ミドルネームは埋められないことを提案しました。この場合、toStringの結果としてミドルネームは表示されず、compareToはミドルネームのない人はミドルネームを持つ人の後に行くと仮定します(この場合、順序は姓と名で最初に発生し、次にミドルネームでのみ発生します)。



非常に基本的なユーティリティのもう1つの例は、前提条件です。 何らかの理由で、JavaにはObjects.requireNotNullのみがあります(Java 7以降)。



前提条件について簡単に説明します。



前提条件クラスのメソッド名 スローされた例外
checkArgument(ブール値) IllegalArgumentException
checkNotNull(T) ヌルポインター傍受
checkState(ブール値) IllegalStateException
checkElementIndex(intインデックス、intサイズ) IndexOutOfBoundException
checkPositionIndex(intインデックス、intサイズ) IndexOutOfBoundException


それらが必要な理由は、Oracle Webサイトで確認できます。



新しいコレクション



ビジネスロジックの途中でこの種のコードを見ることができることがよくあります。



 Map<String, Integer> counts = new HashMap<>(); for (String word : words) { Integer count = counts.get(word); if (count == null) { counts.put(word, 1); } else { counts.put(word, count + 1); } }
      
      





または、次のようなコード:

 List<String> values = map.get(key); if (values == null) { values = new ArrayList<>(); map.put(key, values); } values.add(value);
      
      





(最後の文章では、入力はマップ、キー、および値です)。 これらの2つの例は、コレクションに変更されたデータ(この場合はそれぞれ数字とリスト)が含まれる場合のコレクションの操作を示しています。 前者の場合、マップ(マップ)は本質的にマルチセットを表します。 繰り返し要素を持つセット。2番目の場合、ディスプレイはマルチディスプレイです。 そのような抽象化はグアバにあります。 これらの抽象化を使用して例を書き換えましょう。



 Multiset<String> counts = HashMultiset.create(); for (String word : words) { counts.add(word); }
      
      





そして

 map.put(key, value);
      
      





(ここでマップはMultimap <String、String>です)。 Guavaでは、このようなマルチマッピングの動作をカスタマイズできることに注意してください-たとえば、値セットをセットとして保存したり、リストが必要な場合があります。マッピングのために、リンクリスト、ハッシュまたはツリーが必要な場合があります-Guavaで必要なすべての実装が利用可能です テーブル-同様の方法で重複コードを排除するコレクションですが、マッピング内にマッピングを保存する場合は既に存在します。 生活を楽にする新しいコレクションの例を次に示します。



マルチセット 重複する可能性のある「セット」
マルチマップ 重複する可能性のある「ディスプレイ」
バイマップ 「逆マッピング」をサポート
テーブル 順序付けられたキーペアを値に関連付けます
ClassToInstanceMap 型をこの型のインスタンスにマップします(型キャストを排除します)
レンジセット 範囲セット
レンジマップ 互いに素な範囲の非ゼロ値へのマッピングのセット




コレクションデコレータ



コレクション(既にJava Collections FrameworkにあるものとGuavaに定義されているものの両方)のデコレーターを作成するには、ForwardingList、ForwardingMap、ForwardingMiltisetなどの対応するクラスがあります。



不変コレクション



グアバには不変のコレクションもあります。 それらはクリーンなコードに直接関連していないかもしれませんが、アプリケーションの異なる部分間のデバッグと対話を大幅に簡素化します。 それらは:



ラッパーを作成する特定のコレクションのCollections.unmodifiableメソッドと比べて明確な違いがあるため、コレクションへのリンクがなくなった場合にのみコレクションが変更されないことが期待できます。 コレクションは、速度とメモリの両方を変更するためのオーバーヘッドを残します。



いくつかの簡単な例:



 public static final ImmutableSet<String> COLOR_NAMES = ImmutableSet.of( "red", "green", "blue"); class Foo { final ImmutableSet<Bar> bars; Foo(Set<Bar> bars) { this.bars = ImmutableSet.copyOf(bars); // defensive copy! } }
      
      







イテレータの実装



PeekingIterator 単純にイテレータをラップし、それにpeek()メソッドを追加して次の要素の値を取得します。 Iterators.peekingIterator(イテレーター)を呼び出して作成
AbstractIterator すべてのイテレータメソッドを実装する必要がなくなります-保護されたT computeNext()を実装するだけです
AbstractSequentialIterator 前のものと似ていますが、前のものに基づいて次の要素を計算します:保護されたT computeNext(T previous)メソッドを実装する必要があります




コレクションの機能とユーティリティ



Guavaは、Function <A、R>、Predicateなどのインターフェイスと、ユーティリティクラスFunctions、Predicates、FluentIterable、Iterables、Lists、Setsなどを提供します。 グアバはJava 8よりもずっと前に登場したため、オプションおよび機能<A、R>および述語のインターフェイスが必然的に登場したことを思い出してください。ただし、ほとんどの場合、ラムダなしで機能コードを含む機能コードがないため、これらは限られた場合にのみ有用ですケースは通常の命令よりもはるかに面倒ですが、場合によっては簡潔にすることができます。 簡単な例:



 Predicate<MyClass> nonDefault = not(equalTo(DEFAULT_VALUE)); Iterable<String> strings1 = transform(filter(iterable, nonDefault), toStringFunction()); Iterable<String> strings2 = from(iterable).filter(nonDefault).transform(toStringFunction());
      
      





関数(toStringFunction)、述語(not、equalTo)、Iterables(変換、フィルター)、FluentIterable(from)からの静的メソッドがここにインポートされます。 最初のケースでは、静的Iterableメソッドを使用して結果を作成し、2番目のケースではFluentIterableです。



入力/出力



バイトおよび文字ストリームを抽象化するために、ByteSource、ByteSink、CharSoure、CharSinkなどの抽象クラスが定義されています。 これらは通常、ファサードのリソースとファイルを使用して作成されます。 変換、読み取り、コピー、連結など、入力および出力ストリームを操作するためのメソッドのかなりのセットもあります(CharSource、ByteSource、ByteSinkクラスを参照)。 例:



 // Read the lines of a UTF-8 text file ImmutableList<String> lines = Files.asCharSource(file, Charsets.UTF_8).readLines(); // Count distinct word occurrences in a file Multiset<String> wordOccurrences = HashMultiset.create( Splitter.on(CharMatcher.WHITESPACE) .trimResults() .omitEmptyStrings() .split(Files.asCharSource(file, Charsets.UTF_8).read())); // SHA-1 a file HashCode hash = Files.asByteSource(file).hash(Hashing.sha1()); // Copy the data from a URL to a file Resources.asByteSource(url).copyTo(Files.asByteSink(file));
      
      







少しずつ



リスト 以下を含むさまざまなタイプのリストの作成 Lists.newCopyOnWriteArrayList(反復可能)、Lists.reverse(リスト)/ *表示! /、Lists.transform(fromList、関数)/ *遅延ビュー! * /
セット Map <SomeClass、Boolean>からSet <SomeClass>(view!)への変換、数学的な意味でのセットの操作(交差、結合、差)
イテラブル any、all、contains、concat、filter、find、limit、isEmpty、size、toArray、transformなどの単純なメソッド。 何らかの理由で、Java 8では、これらのメソッドの多くはコレクションにのみ適用されますが、一般的なIterableには適用されません。
バイト、整数、UnsignedIntegerなど 符号なしの数値とプリミティブ型の配列を操作します(各プリミティブ型に対応するユーティリティクラスがあります)。
ObjectArrays 実際、2種類のメソッドしかありません-配列の連結(何らかの理由で標準のJavaライブラリにない)と、特定のクラスまたは配列のクラスの一括作成(何らかの理由で、Javaライブラリにコピーするための同様のメソッドしかありません)。
ジョイナー、スプリッター Iterable、List、またはMapから、またはIterable、List、またはMapへの、またはnarezirovany文字列を結合または柔軟にするクラス
文字列、MoreObjects 言及されていないもののうち、最も一般的に使用されるメソッドは、Strings.emptyToNull(String)、Strings.isNullOrEmpty(String)、Strings.nullToEmpty(String)およびMoreObjects.firstNonNull(T、T)です。
クローザー、スローアブル try-with-resources、multi-catch(Java 6以前でのみ有効)をエミュレートし、スタックトレースを使用して例外をスローします。
com.google.common.net クラス名は、InternetDomainName、InetAddresses、HttpHeaders、MediaType、UrlEscapersを表しています。
com.google.common.htmlおよびcom.google.common.xml HtmlEscapersおよびXmlEscapers
範囲 範囲
イベントバス パブリッシャー-サブスクライバーパターンの強力な実装。 サブスクライバーはEventBusに登録され、その「応答」メソッドには注釈が付けられ、イベントがトリガーされると、EventBusはこのタイプのイベントを認識できるサブスクライバーを見つけて、イベントを通知します。
IntMath、LongMath、BigIntegerMath、DoubleMath 数字を扱うための多くの便利な機能。
クラスパス Javaでは、クラスパス上のクラスを表示するクロスプラットフォームの方法はありません。 また、グアバはパッケージまたはプロジェクトのクラスを歩く機会を提供します。
TypeToken 型の消去により、プログラムの実行中にジェネリック型を操作することできません 。 TypeTokenを使用すると、これらのタイプを操作できます。




その他の例



ハッシュ:



 HashFunction hf = Hashing.md5(); HashCode hc = hf.newHasher() .putLong(id) .putString(name, Charsets.UTF_8) .putObject(person, personFunnel) .hash();
      
      





キャッシング:

 LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder() .maximumSize(1000) .expireAfterWrite(10, TimeUnit.MINUTES) .removalListener(MY_LISTENER) .build( new CacheLoader<Key, Graph>() { public Graph load(Key key) throws AnyException { return createExpensiveGraph(key); } });
      
      





動的プロキシ:



 Foo foo = Reflection.newProxy(Foo.class, invocationHandler)
      
      





Guavaを使用せずに動的プロキシを作成するには、通常、次のコードを記述します。



 Foo foo = (Foo) Proxy.newProxyInstance( Foo.class.getClassLoader(), new Class<?>[] {Foo.class}, invocationHandler);
      
      







それがあなたが読んでいるすべてのコードです。



All Articles