型の安全性:未チェックのキャスト
-
06-07-2019 - |
質問
私の春のアプリケーションコンテキストファイルには、次のようなものがあります。
<util:map id="someMap" map-class="java.util.HashMap" key-type="java.lang.String" value-type="java.lang.String">
<entry key="some_key" value="some value" />
<entry key="some_key_2" value="some value" />
</util:map>
Javaクラスでは、実装は次のようになります。
private Map<String, String> someMap = new HashMap<String, String>();
someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");
Eclipseでは、次のような警告が表示されます。
タイプセーフ:オブジェクトからHashMapへの未チェックのキャスト
何が間違っていたのですか?問題を解決するにはどうすればよいですか
解決
まあ、まず第一に、新しいHashMap
作成呼び出しでメモリを無駄にしています。 2行目では、この作成されたハッシュマップへの参照を完全に無視し、ガベージコレクターで使用できるようにします。だから、それをしないでください:
private Map<String, String> someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");
第二に、コンパイラは、オブジェクトがgetBean
であるかどうかを確認せずにObject
にキャストしたことを訴えています。しかし、たとえあなたがやろうとしても:
if(getApplicationContext().getBean("someMap") instanceof HashMap) {
private Map<String, String> someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");
}
おそらくこの警告が表示されるでしょう。問題は、HashMap<String, String>
がHashMap<Object, Object>
を返すため、タイプが不明です。それを直接HashMap<Date, Calendar>
に変換しても、2番目のケースでは問題は発生しません(最初のケースではおそらく警告が表示されないでしょう。ただし、それをString value = map.get("thisString");
に変換しています。
HashMapは、実際にはキーとしてオブジェクトを取り、値としてオブジェクトを持っているマップです。したがって、Beanを取得したときに、返される非ジェネリック表現に任意のオブジェクトを含めることができるため、ClassCastException
を持つ可能性があるため、<=>として表現できるという保証はありません。
コードがコンパイルされ、エラーなしで<=>を実行できる場合、この警告について心配する必要はありません。ただし、マップが文字列値に対する文字列キーだけではない場合、実行時に<=>を取得します。この場合、ジェネリックはこれをブロックできないためです。
他のヒント
問題は、キャストが実行時チェックであるということです-しかし、型の消去により、実行時に他のHashMap<String,String>
とHashMap<Foo,Bar>
のFoo
とBar
の間に実際の違いはありません。
@SuppressWarnings("unchecked")
を使用して、鼻を保持します。ああ、Javaで具体化されたジェネリックのキャンペーン:)
上記のメッセージが示すように、リストはList<Object>
とList<String>
またはList<Integer>
とを区別できません。
同様の問題についてこのエラーメッセージを解決しました:
List<String> strList = (List<String>) someFunction();
String s = strList.get(0);
次のもの:
List<?> strList = (List<?>) someFunction();
String s = (String) strList.get(0);
説明:最初の型変換は、オブジェクトがリストであることを検証します(リストレベルで内部型を検証できないため)。コンパイラはListに何らかのオブジェクトが含まれていることしかわからないため、2番目の変換が必要になりました。これにより、リスト内の各オブジェクトへのアクセス時にそのタイプが検証されます。
警告はそれだけです。警告。警告は無関係な場合もあれば、そうでない場合もあります。コンパイラーが問題であると考えているが、そうではないかもしれないものに注意を向けるために使用されます。
キャストの場合、この場合は常に警告が表示されます。特定のキャストが安全であると確信している場合は、次のような注釈を追加することを検討する必要があります(構文はわかりません)。
@SuppressWarnings (value="unchecked")
getBeanがオブジェクト参照を返し、正しい型にキャストしているため、このメッセージが表示されています。 Java 1.5では警告が表示されます。これが、このように動作するコードでJava 1.5以上を使用する性質です。 Springにはタイプセーフバージョンがあります
someMap=getApplicationContext().getBean<HashMap<String, String>>("someMap");
To Doリスト。
警告を本当に取り除きたい場合、できることの1つは、ジェネリッククラスから拡張するクラスを作成することです。
たとえば、使用しようとしている場合
private Map<String, String> someMap = new HashMap<String, String>();
このような新しいクラスを作成できます
public class StringMap extends HashMap<String, String>()
{
// Override constructors
}
次に使用するとき
someMap = (StringMap) getApplicationContext().getBean("someMap");
コンパイラは(もはやジェネリックではない)型が何であるかを知っており、警告はありません。これは必ずしも完璧な解決策ではないかもしれません。一部の人はこの種のジェネリッククラスの目的に反すると主張するかもしれませんが、ジェネリッククラスの同じコードをすべて再利用しています。使用したい。
別の解決策として、同じオブジェクトをたくさんキャストし、コードに@SupressWarnings("unchecked")
を散らしたくない場合は、アノテーションを使用してメソッドを作成します。このようにして、キャストを集中化し、できればエラーの可能性を減らします。
@SuppressWarnings("unchecked")
public static List<String> getFooStrings(Map<String, List<String>> ctx) {
return (List<String>) ctx.get("foos");
}
以下のコードはタイプセーフの警告を引き起こします
Map<String, Object> myInput = (Map<String, Object>) myRequest.get();
回避策
リスト内に保持されているオブジェクトのタイプが検証されていないため、パラメーターに言及せずに新しいマップオブジェクトを作成します。
ステップ1: 新しい一時マップを作成
Map<?, ?> tempMap = (Map<?, ?>) myRequest.get();
ステップ2: メインマップのインスタンス化
Map<String, Object> myInput=new HashMap<>(myInputObj.size());
ステップ3: 一時的なマップを繰り返し、値をメインマップに設定します
for(Map.Entry<?, ?> entry :myInputObj.entrySet()){
myInput.put((String)entry.getKey(),entry.getValue());
}
未確認の警告を回避する解決策:
class MyMap extends HashMap<String, String> {};
someMap = (MyMap)getApplicationContext().getBean("someMap");