web-dev-qa-db-ja.com

未チェックのキャストとは何ですか?どのようにチェックしますか?

チェックされていないキャストの意味(あるタイプから別のタイプへのキャスト)は理解できると思いますが、キャストを「チェック」するとはどういう意味ですか?キャストをチェックして、Eclipseでこの警告を回避するにはどうすればよいですか?

31
SIr Codealot

チェックされていないキャストは、ジェネリック型から非修飾型またはその逆に(暗黙的または明示的に)キャストしていることを意味します。例えば。この線

_Set<String> set = new HashSet();
_

このような警告が表示されます。

通常、このような警告には正当な理由があるため、警告を抑制する代わりにコードを改善する必要があります。効果的なJava第2版からの引用:

可能なすべてのチェックされていない警告を削除します。すべての警告を削除すると、コードがタイプセーフであることが保証されます。これは非常に良いことです。これは、実行時にClassCastExceptionを取得しないことを意味し、プログラムが意図したとおりに動作しているという確信を高めます。

警告を排除できず、警告を引き起こしたコードがタイプセーフであることを証明できる場合は、(そしてその場合にのみ)@SuppressWarnings("unchecked")アノテーションで警告を抑制します。最初にコードがタイプセーフであることを証明せずに警告を抑制した場合、誤った安心感しか得られません。コードは警告を出さずにコンパイルできますが、実行時にClassCastExceptionをスローする可能性があります。ただし、安全であることがわかっているチェックされていない警告を(抑制するのではなく)無視した場合、新しい問題が発生して実際の問題を示していることに気付くことはありません。新しい警告は、あなたが黙っていなかったすべての誤警報の中で失われます。

もちろん、上記のコードのように警告を取り除くのは必ずしも簡単ではありません。ただし、コードを確認しないと、コードを安全にする方法はわかりません。

34
Péter Török

ピーターが書いたことをさらに詳しく説明するには:

非ジェネリック型からジェネリック型へのキャストは、コンパイル時にジェネリックパラメーターが消去されるため、実行時に問題なく機能し、正当なキャストが残されます。ただし、型パラメーターに関する誤った仮定が原因で、予期しないClassCastExceptionでコードが後で失敗する場合があります。例えば:

List l1 = new ArrayList();
l1.add(33);
ArrayList<String> l2 = (ArrayList<String>) l1;
String s = l2.get(0);

3行目のチェックされていない警告は、予期しないClassCastExceptionが後で発生する可能性があるという意味で、コンパイラがタイプセーフを保証できなくなったことを示しています。そして、これは暗黙のキャストを実行する4行目で発生します。

43
Eyal Schneider

チェックされたキャストとは対照的に、チェックされていないキャストは、実行時に型の安全性をチェックしません。

これは、第3版の_Consider typesafe heterogenous containers_セクションに基づく例です。 Joshua Blochによる "Effective Java"の例ですが、コンテナクラスは意図的に壊れています-間違ったタイプを格納して返します:

_public class Test {

    private static class BrokenGenericContainer{
        private final Map<Class<?>, Object> map= new HashMap<>();

        public <T> void store(Class<T> key, T value){
            map.put(key, "broken!"); // should've been [value] here instead of "broken!"
        }

        public <T> T retrieve(Class<T> key){
//          return key.cast(map.get(key)); // a checked cast 
            return (T)map.get(key);        // an unchecked cast
        }

    }

    public static void main(String[] args) {
        BrokenGenericContainer c= new BrokenGenericContainer();
        c.store(Integer.class, 42);
        List<Integer> ints = new ArrayList<>();
        ints.add(c.retrieve(Integer.class));
        Integer i = ints.get(0);
    }

}
_


retrieve()未チェックのキャストを使用する場合-_(T)map.get(key)_-このプログラムを実行すると、ClassCastExceptionが発生しますInteger i = ints.get(0)行。 retrieve()メソッドは、実際のタイプが実行時にチェックされなかったため完了します。

_Exception in thread "main" 
Java.lang.ClassCastException: Java.lang.String cannot be cast to Java.lang.Integer
    at Test.main(Test.Java:27)
_


しかし、retrieve()チェックされたキャストを使用する場合-key.cast(map.get(key))-このプログラムを実行すると、 key.cast(map.get(key))行でClassCastExceptionが発生しました。チェックされたキャストが型が間違っていることを検出し、例外をスローするためです。 retrieve()メソッドは完了しません:

_Exception in thread "main" Java.lang.ClassCastException: 
                                          Cannot cast Java.lang.String to Java.lang.Integer
    at Java.lang.Class.cast(Class.Java:3369)
    at Test$BrokenGenericContainer.retrieve(Test.Java:16)
    at Test.main(Test.Java:26)
_

少し違うように見えるかもしれませんが、チェックされていないキャストの場合、Stringは_List<Integer>_に正常に移行しました。実際のアプリケーションでは、これの結果は...まあ、深刻かもしれません。チェックされたキャストの場合、型の不一致はできるだけ早く発見されました。


プログラマがメソッドが実際に安全であると本当に確信している場合、チェックされていないキャストの警告を回避するために、@SuppressWarnings("unchecked")を使用できます。より良い代替策は、可能であればジェネリックとチェックされたキャストを使用することです。

ジョシュア・ブロックが言ったように、

...チェックされていない警告は重要です。それらを無視しないでください。


完全を期すために、 this 回答はEclipseの詳細を扱っています。

0
jihor