1. 概要

Javaソースファイルをコンパイルすると、Javaコンパイラによって「uncheckedcast」という警告メッセージが出力されることがあります。

このチュートリアルでは、警告メッセージを詳しく見ていきます。 この警告の意味、警告される理由、および問題の解決方法について説明します。

一部のJavaコンパイラは、デフォルトでチェックされていない警告を抑制します。

この「未チェックのキャスト」警告を調べる前に、「未チェック」の警告を出力するコンパイラのオプションが有効になっていることを確認しましょう。

2. 「チェックされていないキャスト」警告とはどういう意味ですか?

「チェックされていないキャスト」はコンパイル時の警告です。 簡単に言えば、タイプチェックなしでrawタイプをパラメーター化されたタイプにキャストすると、この警告が表示されます

例はそれを簡単に説明することができます。 生の型Mapを返す簡単なメソッドがあるとしましょう。

public class UncheckedCast {
    public static Map getRawMap() {
        Map rawMap = new HashMap();
        rawMap.put("date 1", LocalDate.of(2021, Month.FEBRUARY, 10));
        rawMap.put("date 2", LocalDate.of(1992, Month.AUGUST, 8));
        rawMap.put("date 3", LocalDate.of(1976, Month.NOVEMBER, 18));
        return rawMap;
    }
...
}

次に、上記のメソッドを呼び出して結果をにキャストするテストメソッドを作成しましょう。 地図

@Test
public void givenRawMap_whenCastToTypedMap_shouldHaveCompilerWarning() {
    Map<String, LocalDate> castFromRawMap = (Map<String, LocalDate>) UncheckedCast.getRawMap();
    Assert.assertEquals(3, castFromRawMap.size());
    Assert.assertEquals(castFromRawMap.get("date 2"), LocalDate.of(1992, Month.AUGUST, 8));
}

コンパイラーは、ジェネリックスをサポートしない古いバージョンのJavaとの下位互換性を維持するために、このキャストを許可する必要があります。

ただし、Javaソースをコンパイルすると、コンパイラは警告メッセージを出力します。 次に、Mavenを使用して単体テストをコンパイルして実行しましょう。

$ mvn clean test
...
[WARNING] .../src/test/java/com/baeldung/uncheckedcast/UncheckedCastUnitTest.java:[14,97] unchecked cast
  required: java.util.Map<java.lang.String,java.time.LocalDate>
  found:    java.util.Map
...
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
...
[INFO] Results:
[INFO] 
[INFO] Tests run: 16, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
...

Mavenの出力が示すように、警告を正常に再現しました。

一方、「 uncheckedcast 」コンパイラの警告が表示されても、テストは問題なく機能します。

コンパイラが理由なしに警告を出さないことはわかっています。 この警告が表示された場合、潜在的な問題があるはずです。

それを理解しましょう。

3. Javaコンパイラが警告するのはなぜですか?

uncheckedcast」という警告が表示されますが、前のセクションでテスト方法は正常に機能します。 生のタイプをキャストしていたからです地図地図 、 生もの地図のみが含まれていますエントリ。 つまり、型キャストは安全です。

潜在的な問題を分析するために、生のタイプ Map にもう1つのエントリを追加して、 getRawMap()メソッドを少し変更しましょう。

public static Map getRawMapWithMixedTypes() {
    Map rawMap = new HashMap();
    rawMap.put("date 1", LocalDate.of(2021, Month.FEBRUARY, 10));
    rawMap.put("date 2", LocalDate.of(1992, Month.AUGUST, 8));
    rawMap.put("date 3", LocalDate.of(1976, Month.NOVEMBER, 18));
    rawMap.put("date 4", new Date());
    return rawMap;
}

今回は、に新しいエントリを追加しました地図タイプ付き上記の方法で。

それでは、 getRawMapWithMixedTypes()メソッドを呼び出す新しいテストメソッドを作成しましょう。

@Test(expected = ClassCastException.class)
public void givenMixTypedRawMap_whenCastToTypedMap_shouldThrowClassCastException() {
    Map<String, LocalDate> castFromRawMap = (Map<String, LocalDate>) UncheckedCast.getRawMapWithMixedTypes();
    Assert.assertEquals(4, castFromRawMap.size());
    Assert.assertTrue(castFromRawMap.get("date 4").isAfter(castFromRawMap.get("date 3")));
}

テストをコンパイルして実行すると、「uncheckedcast」という警告メッセージが再度出力されます。 また、私たちのテストは合格します。

ただし、テストには expected = ClassCastException.class 引数があるため、テストメソッドがClassCastExceptionをスローしたことを意味します。

よく見ると、 ClassCastExceptionは、生のタイプMaptoMapをキャストする行ではスローされません。 警告メッセージはこの行を指していますが。 代わりに、キーによって間違ったタイプのデータを取得すると例外が発生します castFromRawMap.get( “date 4″)。 

間違った型のデータを含む生の型コレクションをパラメーター化された型コレクションにキャストすると、間違った型のデータをロードするまで、ClassCastExceptionはスローされません。

場合によっては、例外が遅すぎることがあります。

たとえば、メソッドを呼び出すことにより、多くのエントリを含む生のタイプ Map を取得し、それをパラメータ化されたタイプのMapにキャストします。

(Map<String, LocalDate>) UncheckedCast.getRawMapWithMixedTypes()

Map のエントリごとに、LocalDateオブジェクトをリモートAPIに送信する必要があります。 ClassCastException が発生するまでは、すでに多くのAPI呼び出しが行われている可能性があります。 要件によっては、追加の復元またはデータのクリーンアッププロセスが必要になる場合があります。

間違ったタイプのエントリの状況を処理する方法を決定できるように、早期に例外を取得できれば便利です。

未チェックのキャスト」警告の背後にある潜在的な問題を理解したので、問題を解決するために何ができるかを見てみましょう。

4. 警告をどうすればよいですか?

4.1. Rawタイプの使用は避けてください

ジェネリックはJava5以降に導入されました。 Java環境がジェネリックスをサポートしている場合は、raw型の使用を避ける必要があります。 これは、生の型を使用すると、ジェネリックの安全性と表現力の利点がすべて失われるためです。

さらに、レガシーコードを検索し、それらのrawタイプの使用法をジェネリックにリファクタリングする必要があります。

ただし、古いライブラリを使用しなければならない場合もあります。 これらの古い外部ライブラリのメソッドは、生の型コレクションを返す場合があります。

これらのメソッドを呼び出してパラメーター化された型にキャストすると、「uncheckedcast」コンパイラ警告が生成されます。 ただし、外部ライブラリを制御することはできません。

次に、このケースの処理方法を見てみましょう。

4.2. 「未チェック」警告を抑制します

uncheckedcast」警告を削除できず、警告を引き起こすコードがタイプセーフであることが確実な場合は、SuppressWarnings( “unchecked”)アノテーションを使用して警告を抑制できます

@ SuppressWarning( “unchecked”)アノテーションを使用する場合は、常に可能な限り最小のスコープに配置する必要があります。

例として、 ArrayListクラスのremove()メソッドを見てみましょう。

public E remove(int index) {
    Objects.checkIndex(index, size);
    final Object[] es = elementData;
                                                              
    @SuppressWarnings("unchecked") E oldValue = (E) es[index];
    fastRemove(es, index);
                                                              
    return oldValue;
}

4.3. RawTypeコレクションを使用する前にTypesafeCheckを実行する

学習したように、 @SuppressWarning( “unchecked”)アノテーションは、キャストがタイプセーフかどうかを実際にチェックせずに、警告メッセージを抑制するだけです。

生の型をキャストすることがタイプセーフかどうかわからない場合は、データを実際に使用する前に型をチェックして、ClassCastExceptionを早期に取得できるようにする必要があります

5. 結論

この記事では、「uncheckedcast」コンパイラ警告の意味を学びました。

さらに、この警告の原因と潜在的な問題を解決する方法について説明しました。

いつものように、この記事のコードはすべてGitHub利用できます。