小さなライブラリのコントラクトは、APIの表面積よりも正しさを左右することが 頻繁にあります。以下のルールは、よくあるJava統合の失敗を防ぎます。
オブジェクトのコントラクト #
equalsは、反射的、対称的、推移的、一貫性があり、nullに対してfalseで
なければなりません。2つのオブジェクトが等しい場合、それらは同じ
hashCodeを持たなければなりません。したがって、値をハッシュキーとして
使用する前に、両方をオーバーライドしてください。オブジェクトがマップの
キーになっている間は、等価性の判定に使用するフィールドを変更しないでください。
等価性によって対称性が破られる場合は、継承よりもコンポジションを優先します。
Object.cloneはフィールドをコピーしますが、コンストラクター、継承、
可変フィールドとの相性がよくありません。コピーコンストラクターまたは
名前付きのcopyOfファクトリを優先してください。配列は、cloneが予測可能な
浅い配列のコントラクトを持つため、限定的な例外です。
テキスト、正規表現、文字列 #
再利用するPatternはコンパイルしてください。Matcher.matches()は入力全体が
一致することを要求しますが、find()は次に一致する領域を検索します。
Pattern.quote(value)は正規表現に配置するリテラルテキストをエスケープし、
Matcher.quoteReplacement(value)は置換文字列をエスケープします。これらは
互換的に使用できません。
JDK 9以降、HotSpotのString実装はコンパクト文字列を使用しています。
byte[]とエンコーディングマーカーの組み合わせにより、Latin-1テキストを
コンパクトに表現し、それ以外はUTF-16で表現できます。これは実装詳細です。
JavaのStringは引き続きUTF-16コード単位の列であり、I/O境界ではCharset変換を
明示的に行う必要があります。
カウンターと列挙型コレクション #
AtomicLongは、正確で線形化可能な単一の値を提供します。LongAdderは競合時に
更新を複数のセルへ分散するため、更新のスケーラビリティは向上しますが、
更新と同時に実行されるsum()はアトミックなスナップショットではありません。
メトリクスにはLongAdderを使用し、観測した各合計値が正しさの判定を左右する
場合にはAtomicLongを使用してください。
Enum<E extends Enum<E>>は、1つの列挙型に対して、型安全な順序付けと操作を
Enumに提供します。EnumSetとEnumMapは、その閉じたキー空間を活用し、
列挙子の宣言順序を保持します。列挙型を対象とする場合は、HashSetや
HashMapよりもこれらを優先してください。
コレクションとスレッド #
Thread.Stateは、BLOCKED、WAITING、TIMED_WAITINGなど、JVMレベルの状態を
報告します。これは観測用であり、同期プロトコルではありません。スレッドの
協調には、join、future、ラッチ、または構造化された所有関係を使用してください。
Mapがnullキーまたは値を許可するかどうかは、実装固有です。
HashMapは1つのnullキーを許可しますが、ConcurrentHashMapはnullキーと
null値を拒否します。また、TreeMapは自然順序付けの場合にnullキーを拒否する
ことがあります。Mapインターフェースのオプション操作が成功すると仮定せず、
選択した実装のコントラクトを確認してください。
Arrays.asList(array)は、指定されたオブジェクト配列を基盤とする固定サイズの
リストを返します。setはその配列を更新しますが、addとremoveは失敗します。
int[]を渡すと、可変長引数がプリミティブ配列をボックス化しないため、1要素の
List<int[]>が作成されます。ボックス化された値のリストが必要な場合は、
Arrays.stream(ints).boxed().toList()を使用してください。
コメント
GitHub で閲覧・コメントする(投稿にはログインが必要です)