JAVA/TESTSGO →TS →

Java 21 / JUnit 5

Javaの挙動を、
テストで確かめる。

説明より先に、実行できる仕様を見る。Java by Tests は、Source と JUnit を並べて読むための Executable Documentation です。

01 / SOURCEStringComparison.java
package examples.string;

public final class StringComparison {
  private StringComparison() {}

  public static boolean sameReference(String left, String right) {
    return left == right;
02 / TESTStringComparisonTest.java
package examples.string;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;

class StringComparisonTest {
  @Test
OBSERVED: Source → Test ✓

Observation protocol

検証は、観測結果へ戻る。

01

SOURCE

確認したい最小の Java コードだけを置きます。

02

TEST

JUnit の assertion で、挙動をそのまま固定します。

03

OBSERVED

補足は短く。結果からコードへ戻るためだけに使います。

Verified index

まずは、55個の確かな挙動から。

55 / 55
01
Language

String: == と equals

@Test 同じ内容のStringはequalsで等しい

同じ文字列でも、参照と値の比較は別の契約です。

Java 8+→
02
Language

String: isBlank は空白だけの文字列も判定する

@Test 空白だけのStringはisBlankでtrueになる

isBlank は空文字だけでなく、空白文字だけの String も空白として扱います。

Java 11+→
03
Language

StringBuilder: append は同じインスタンスを変更する

@Test StringBuilderのappendは同じインスタンスを返す

append は新しい StringBuilder を作らず、呼び出し元を更新して同じ参照を返します。

Java 8+→
04
Collections

ArrayList: add は元の List を変更する

@Test addすると元のListに要素が追加される

mutable な List では、add の呼び出しが同じインスタンスを更新します。

Java 8+→
05
Collections

List.of: 変更できない List を作る

@Test ListOfが返すListには要素を追加できない

List.of が返すコレクションは、変更操作を受け付けません。

Java 9+→
06
Collections

Arrays.asList: 配列を共有する固定長 List

@Test ArraysAsListで変更した要素は元の配列にも反映される

Arrays.asList は配列をコピーせず、要素の置換は共有しながら追加・削除を許可しません。

Java 8+→
07
Collections

Map: computeIfAbsent で既定値を登録する

@Test 存在しないキーには既定値が登録される

キーがない場合にだけ、計算した値を登録して返します。

Java 8+→
08
Numbers

Integer: == はキャッシュ範囲でだけ同じ参照になり得る

@Test Integerの128は参照比較では等しくない

Integer の参照比較は値ではなくキャッシュされた同じオブジェクトかを見ます。

Java 8+→
09
Numbers

BigDecimal: equals と compareTo は scale の扱いが違う

@Test scaleが異なっても数値として同じBigDecimalはcompareToで等しい

1.0 と 1.00 は数値として同じでも、equals では scale の違いにより等しくありません。

Java 8+→
10
Language

record: 同じ構成要素なら equals で等しい

@Test 同じ値を持つrecordはequalsで等しくなる

record は構成要素に基づく equals と hashCode を自動的に持ちます。

Java 16+→
11
Language

equals と hashCode: HashSetで値を見つけるための契約

@Test equalsとhashCodeを実装した値はHashSetで検索できる

equals が等しいオブジェクトは、同じ hashCode を返さなければ集合検索の契約を満たせません。

Java 8+→
12
Functional

Optional: orElse で既定値を返す

@Test emptyのOptionalは既定値を返す

値がない場合だけ、指定した既定値を選びます。

Java 8+→
13
Functional

Optional: orElse と orElseGet は既定値の評価タイミングが違う

@Test orElseはOptionalに値があっても引数を評価する

orElse は値があっても引数を評価し、orElseGet は必要になるまで Supplier を呼びません。

Java 8+→
14
Functional

Optional: map と flatMap はOptionalのネストをどう扱うかが違う

@Test flatMapはOptionalを返す関数の結果を平坦化する

関数が Optional を返すとき、map は二重にし、flatMap は一段だけ平坦化します。

Java 8+→
15
Functional

Stream: filter で条件に合う値を残す

@Test filterは正の数だけを残す

filter は元の List を変更せず、条件を満たす要素だけを流します。

Java 8+→
16
Functional

Stream: 中間操作は終端操作まで評価されない

@Test Streamは終端操作までmapを評価しない

map は Stream を組み立てただけでは実行されず、toList のような終端操作で初めて評価されます。

Java 8+→
17
Functional

Stream.toList: 変更できない List を返す

@Test StreamToListが返すListには要素を追加できない

Stream.toList の結果は、内容が同じでも add を受け付けない List です。

Java 16+→
18
Error Handling

Exception: assertThrows で例外の契約を確かめる

@Test ゼロで割るとArithmeticExceptionが送出される

失敗する入力に対して、期待する例外型をテストで固定します。

Java 8+→
19
Error Handling

Exception: checked と unchecked は呼び出し側の契約が違う

@Test 不正な引数はuncheckedのIllegalArgumentExceptionを送出する

IOException は呼び出し側で扱うことを型で求め、IllegalArgumentException は実行時に送出します。

Java 8+→
20
Generics

generics: ? extends Number で数値型を受け取る

@Test IntegerのListをNumberとして合計できる

上限境界ワイルドカードで、Number の各サブタイプを読み取れます。

Java 8+→
21
Generics

generics: ? extends は読む側、? super は書く側の境界

@Test superはNumberのListへIntegerを追加できる

extends はサブタイプから読み、super はスーパタイプのコンテナへ値を書き込みます。

Java 8+→
22
Date / Time

Date / Time: LocalDate と LocalDateTime は時刻情報の有無が違う

@Test LocalDateから作るLocalDateTimeはその日の開始時刻になる

LocalDate は日付だけ、LocalDateTime は日付と時刻を持ち、変換時に失われる・補われる情報があります。

Java 8+→
23
Error Handling

try-with-resources: 複数resourceは逆順にcloseされる

@Test tryWithResourcesは処理後にcloseを必ず呼ぶ

AutoCloseable を複数宣言すると、ブロック終了時には宣言と逆順にcloseされます。

Java 7+→
24
Collections

HashMap: 可変のキーを変更すると登録済みの値を検索できない

@Test HashMapへ登録した後にキーを変更すると同じキーで検索できない

hashCodeに使う状態を変更すると、HashMapが値を置いたバケットと検索先が一致しなくなります。

Java 8+→
25
Numbers

BigDecimal: 除算の丸めモードで同じ式の結果が変わる

@Test BigDecimalの除算は指定した丸めモードで結果が変わる

割り切れない除算は、scaleとRoundingModeを明示しなければ期待する桁と方向を固定できません。

Java 8+→
26
Functional

Optional: ofNullable はnullをemptyにし、ofは例外にする

@Test ofNullableはnullをemptyのOptionalへ変換する

nullを受け入れる境界ではofNullableを使い、nullが設計違反ならofで早く失敗させます。

Java 8+→
27
Collections

HashMap: 衝突は処理できるがequalsとhashCodeの不整合は検索を壊す

@Test equalsがtrueでもhashCodeが違うキーでは登録済みの値を検索できない

同じhashCodeのキーは共存できますが、equalsがtrueなのにhashCodeが違うキーでは登録済みの値を検索できません。

Java 8+→
28
Numbers

BigDecimal: doubleから作ると意図しない桁が入る

@Test doubleから作るBigDecimalは10進数の0point1と厳密には一致しない

new BigDecimal(0.1d)は2進浮動小数点の値を正確に取り込み、10進数の0.1とは一致しません。

Java 8+→
29
Functional

Optional: フィールドや引数にするとnullとemptyが二重になる

@Test Optional型の引数にもnullを渡せるため不在表現が二重になる

Optional型のフィールドや引数にもnullを渡せるため、不在の表現がnullとemptyの二つに増えます。

Java 8+→
30
Language

String: length はUTF-16のコード単位を数える

@Test 絵文字はUTF16のlengthでは2だがcodePointCountでは1になる

絵文字のような補助文字はUTF-16で2単位になるため、lengthと文字数が一致しないことがあります。

Java 8+→
31
Language

String: 見た目が同じUnicode文字列でもequalsが異なる

@Test 見た目が同じでも合成済みと結合文字の文字列はequalsで異なる

合成済み文字と結合文字の列は、表示が同じでも別のコードポイント列として比較されます。

Java 8+→
32
Collections

HashMap: get のnullだけでは未登録とnull値を区別できない

@Test getだけではnull値のキーと未登録キーを区別できない

HashMapはnull値を保存できるため、getの戻り値がnullでもキーが存在しないとは限りません。

Java 8+→
33
Collections

List.of: null要素を許可しない

@Test ListOfはnull要素を持つListを作れない

List.ofは変更不可であるだけでなく、作成時にnull要素を検出して例外にします。

Java 9+→
34
Functional

Stream: 終端操作の後に同じStreamは使えない

@Test 終端操作を実行したStreamは再利用できない

toListやcountなどの終端操作を一度実行したStreamは、二回目の走査で例外になります。

Java 8+→
35
Date / Time

ZonedDateTime: plusDays と plusHours は夏時間で異なる

@Test 夏時間開始日のplusDaysとplusHoursは同じ時刻にならない

夏時間の切替日では、暦日を一つ進めるplusDaysと24時間を足すplusHoursが異なるローカル時刻になります。

Java 8+→
36
Language

record: 配列の構成要素は値ではなく参照で比較される

@Test 同じ要素の別配列を持つrecordはequalsで等しくならない

recordが生成するequalsでも、配列要素は内容ではなく配列オブジェクトの参照として比較されます。

Java 16+→
37
Language

sealed class: 許可した型だけを網羅的にswitchできる

@Test sealed型の全許可サブタイプをswitchで分岐できる

sealed型の許可サブタイプを全て扱うswitchは、defaultなしで分岐の網羅性を表せます。

Java 21+→
38
Concurrency

virtual thread: 通常のThreadLocalは親から引き継がれない

@Test virtualThreadには親スレッドの通常のThreadLocal値が引き継がれない

親スレッドで設定した通常のThreadLocal値は、新しく開始したvirtual threadではnullになります。

Java 21+→
39
Concurrency

CompletableFuture: join は失敗をCompletionExceptionで包む

@Test joinは非同期処理の失敗をCompletionExceptionで包む

非同期処理が失敗すると、joinは元の例外をそのままではなくCompletionExceptionのcauseとして送出します。

Java 8+→
40
Concurrency

ConcurrentHashMap: nullキーとnull値を許可しない

@Test ConcurrentHashMapはnullキーとnull値を許可しない

HashMapと異なり、ConcurrentHashMapはnullキー・null値のputを即座に例外にします。

Java 8+→
41
Generics

generics: 型引数は実行時に消去される

@Test 異なる型引数のArrayListは実行時には同じクラスである

ArrayList<String>とArrayList<Integer>はコンパイル時には別の型ですが、実行時クラスは同じです。

Java 8+→
42
Collections

TreeSet: Comparatorが0を返す値は同じ要素として扱う

@Test Comparatorが0を返す異なる値はTreeSetで同じ要素として扱われる

TreeSet は equals ではなく Comparator の比較結果で、要素の重複と検索を判断します。

Java 8+→
43
Collections

defensive copy: 入力Listの後続変更から状態を守る

@Test constructorは入力Listをコピーして後からの変更を受けない

List.copyOfで受け取ったListをコピーすると、呼び出し元による後続変更が内部状態へ漏れません。

Java 10+→
44
Collections

Collections: unmodifiable view と immutable copy は共有状態が違う

@Test unmodifiableViewは元のListの後続変更を反映する

unmodifiable view は元のListを読み続け、List.copyOfの結果は作成時点の独立した値です。

Java 10+→
45
Functional

Collectors.toMap: 重複キーにはmerge規則が必要

@Test toMapは同じキーがあるとmerge関数なしでは例外にする

toMapは同じキーを見つけると、merge関数を指定していない限り例外にします。

Java 8+→
46
Language

enum: 定数は同じインスタンスとして比較できる

@Test valueOfで得たenum定数は宣言済み定数と同じインスタンスである

enumの各定数は一意なインスタンスであり、同じ定数かの比較には==を使えます。

Java 8+→
47
Language

annotation: RUNTIME保持ならreflectionから読める

@Test RUNTIME保持のannotationはreflectionから取得できる

RetentionPolicy.RUNTIMEを指定したannotationだけが、実行時のreflection APIで取得できます。

Java 8+→
48
Language

class initialization: static fieldは宣言順に初期化される

@Test staticFieldは宣言順に初期化される

クラス初期化ではstatic fieldの初期化式がソース上の宣言順に評価されます。

Java 8+→
49
Language

overload: wideningはboxingより先に選ばれる

@Test primitiveのshortはboxingよりwideningを優先してlongを選ぶ

shortをlongとIntegerのoverloadへ渡すと、boxingではなくprimitive wideningのlongが選ばれます。

Java 8+→
50
Language

varargs: 配列を渡すと同じ配列を受け取る

@Test 配列を渡すvarargsは配列をコピーせず同じ配列を受け取る

既存配列をvarargs引数へ渡すと、メソッドはコピーではなくその配列を操作します。

Java 8+→
51
Numbers

unboxing: nullのIntegerを計算するとNullPointerExceptionになる

@Test nullのIntegerを計算するとunboxing時にNullPointerExceptionになる

Integerをintとして使う式では自動unboxingが起き、nullなら演算前にNullPointerExceptionになります。

Java 8+→
52
Language

String pool: リテラルとnew Stringは参照共有が違う

@Test 同じリテラルはpoolの同じ参照を共有する

同じStringリテラルはpoolの参照を共有し得ますが、new Stringは別インスタンスを作ります。

Java 8+→
53
Concurrency

ExecutorService: submitした例外はFutureで観測する

@Test submitしたタスクの例外はFutureのgetでExecutionExceptionとして観測する

submitしたタスクの例外は呼び出し元へ直ちに投げられず、Future.getでExecutionExceptionとして観測します。

Java 8+→
54
Concurrency

interruption: Thread.interruptedは割り込みフラグをclearする

@Test ThreadInterruptedは現在の割り込み状態を返してclearする

Thread.interruptedは現在の割り込み状態を返した後、そのフラグをclearします。

Java 8+→
55
Concurrency

synchronized: 同じスレッドは同じmonitorへ再入できる

@Test 同じスレッドは保持中のsynchronizedモニタを再入できる

synchronizedメソッドが同じオブジェクトの別のsynchronizedメソッドを呼んでも、同じスレッドなら停止しません。

Java 8+→

Static index

教材を検索

テーマ、タグ、テスト名、カテゴリ、Javaバージョンで絞り込みます。

55件の教材を表示しています。

Language · Java 8+String: == と equals@Test 同じ内容のStringはequalsで等しいLanguage · Java 11+String: isBlank は空白だけの文字列も判定する@Test 空白だけのStringはisBlankでtrueになるLanguage · Java 8+StringBuilder: append は同じインスタンスを変更する@Test StringBuilderのappendは同じインスタンスを返すCollections · Java 8+ArrayList: add は元の List を変更する@Test addすると元のListに要素が追加されるCollections · Java 9+List.of: 変更できない List を作る@Test ListOfが返すListには要素を追加できないCollections · Java 8+Arrays.asList: 配列を共有する固定長 List@Test ArraysAsListで変更した要素は元の配列にも反映されるCollections · Java 8+Map: computeIfAbsent で既定値を登録する@Test 存在しないキーには既定値が登録されるNumbers · Java 8+Integer: == はキャッシュ範囲でだけ同じ参照になり得る@Test Integerの128は参照比較では等しくないNumbers · Java 8+BigDecimal: equals と compareTo は scale の扱いが違う@Test scaleが異なっても数値として同じBigDecimalはcompareToで等しいLanguage · Java 16+record: 同じ構成要素なら equals で等しい@Test 同じ値を持つrecordはequalsで等しくなるLanguage · Java 8+equals と hashCode: HashSetで値を見つけるための契約@Test equalsとhashCodeを実装した値はHashSetで検索できるFunctional · Java 8+Optional: orElse で既定値を返す@Test emptyのOptionalは既定値を返すFunctional · Java 8+Optional: orElse と orElseGet は既定値の評価タイミングが違う@Test orElseはOptionalに値があっても引数を評価するFunctional · Java 8+Optional: map と flatMap はOptionalのネストをどう扱うかが違う@Test flatMapはOptionalを返す関数の結果を平坦化するFunctional · Java 8+Stream: filter で条件に合う値を残す@Test filterは正の数だけを残すFunctional · Java 8+Stream: 中間操作は終端操作まで評価されない@Test Streamは終端操作までmapを評価しないFunctional · Java 16+Stream.toList: 変更できない List を返す@Test StreamToListが返すListには要素を追加できないError Handling · Java 8+Exception: assertThrows で例外の契約を確かめる@Test ゼロで割るとArithmeticExceptionが送出されるError Handling · Java 8+Exception: checked と unchecked は呼び出し側の契約が違う@Test 不正な引数はuncheckedのIllegalArgumentExceptionを送出するGenerics · Java 8+generics: ? extends Number で数値型を受け取る@Test IntegerのListをNumberとして合計できるGenerics · Java 8+generics: ? extends は読む側、? super は書く側の境界@Test superはNumberのListへIntegerを追加できるDate / Time · Java 8+Date / Time: LocalDate と LocalDateTime は時刻情報の有無が違う@Test LocalDateから作るLocalDateTimeはその日の開始時刻になるError Handling · Java 7+try-with-resources: 複数resourceは逆順にcloseされる@Test tryWithResourcesは処理後にcloseを必ず呼ぶCollections · Java 8+HashMap: 可変のキーを変更すると登録済みの値を検索できない@Test HashMapへ登録した後にキーを変更すると同じキーで検索できないNumbers · Java 8+BigDecimal: 除算の丸めモードで同じ式の結果が変わる@Test BigDecimalの除算は指定した丸めモードで結果が変わるFunctional · Java 8+Optional: ofNullable はnullをemptyにし、ofは例外にする@Test ofNullableはnullをemptyのOptionalへ変換するCollections · Java 8+HashMap: 衝突は処理できるがequalsとhashCodeの不整合は検索を壊す@Test equalsがtrueでもhashCodeが違うキーでは登録済みの値を検索できないNumbers · Java 8+BigDecimal: doubleから作ると意図しない桁が入る@Test doubleから作るBigDecimalは10進数の0point1と厳密には一致しないFunctional · Java 8+Optional: フィールドや引数にするとnullとemptyが二重になる@Test Optional型の引数にもnullを渡せるため不在表現が二重になるLanguage · Java 8+String: length はUTF-16のコード単位を数える@Test 絵文字はUTF16のlengthでは2だがcodePointCountでは1になるLanguage · Java 8+String: 見た目が同じUnicode文字列でもequalsが異なる@Test 見た目が同じでも合成済みと結合文字の文字列はequalsで異なるCollections · Java 8+HashMap: get のnullだけでは未登録とnull値を区別できない@Test getだけではnull値のキーと未登録キーを区別できないCollections · Java 9+List.of: null要素を許可しない@Test ListOfはnull要素を持つListを作れないFunctional · Java 8+Stream: 終端操作の後に同じStreamは使えない@Test 終端操作を実行したStreamは再利用できないDate / Time · Java 8+ZonedDateTime: plusDays と plusHours は夏時間で異なる@Test 夏時間開始日のplusDaysとplusHoursは同じ時刻にならないLanguage · Java 16+record: 配列の構成要素は値ではなく参照で比較される@Test 同じ要素の別配列を持つrecordはequalsで等しくならないLanguage · Java 21+sealed class: 許可した型だけを網羅的にswitchできる@Test sealed型の全許可サブタイプをswitchで分岐できるConcurrency · Java 21+virtual thread: 通常のThreadLocalは親から引き継がれない@Test virtualThreadには親スレッドの通常のThreadLocal値が引き継がれないConcurrency · Java 8+CompletableFuture: join は失敗をCompletionExceptionで包む@Test joinは非同期処理の失敗をCompletionExceptionで包むConcurrency · Java 8+ConcurrentHashMap: nullキーとnull値を許可しない@Test ConcurrentHashMapはnullキーとnull値を許可しないGenerics · Java 8+generics: 型引数は実行時に消去される@Test 異なる型引数のArrayListは実行時には同じクラスであるCollections · Java 8+TreeSet: Comparatorが0を返す値は同じ要素として扱う@Test Comparatorが0を返す異なる値はTreeSetで同じ要素として扱われるCollections · Java 10+defensive copy: 入力Listの後続変更から状態を守る@Test constructorは入力Listをコピーして後からの変更を受けないCollections · Java 10+Collections: unmodifiable view と immutable copy は共有状態が違う@Test unmodifiableViewは元のListの後続変更を反映するFunctional · Java 8+Collectors.toMap: 重複キーにはmerge規則が必要@Test toMapは同じキーがあるとmerge関数なしでは例外にするLanguage · Java 8+enum: 定数は同じインスタンスとして比較できる@Test valueOfで得たenum定数は宣言済み定数と同じインスタンスであるLanguage · Java 8+annotation: RUNTIME保持ならreflectionから読める@Test RUNTIME保持のannotationはreflectionから取得できるLanguage · Java 8+class initialization: static fieldは宣言順に初期化される@Test staticFieldは宣言順に初期化されるLanguage · Java 8+overload: wideningはboxingより先に選ばれる@Test primitiveのshortはboxingよりwideningを優先してlongを選ぶLanguage · Java 8+varargs: 配列を渡すと同じ配列を受け取る@Test 配列を渡すvarargsは配列をコピーせず同じ配列を受け取るNumbers · Java 8+unboxing: nullのIntegerを計算するとNullPointerExceptionになる@Test nullのIntegerを計算するとunboxing時にNullPointerExceptionになるLanguage · Java 8+String pool: リテラルとnew Stringは参照共有が違う@Test 同じリテラルはpoolの同じ参照を共有するConcurrency · Java 8+ExecutorService: submitした例外はFutureで観測する@Test submitしたタスクの例外はFutureのgetでExecutionExceptionとして観測するConcurrency · Java 8+interruption: Thread.interruptedは割り込みフラグをclearする@Test ThreadInterruptedは現在の割り込み状態を返してclearするConcurrency · Java 8+synchronized: 同じスレッドは同じmonitorへ再入できる@Test 同じスレッドは保持中のsynchronizedモニタを再入できる