web-dev-qa-db-ja.com

型安全:未チェックキャスト

私の春のアプリケーションコンテキストファイルには、次のようなものがあります。

<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では、次のような警告が表示されます。

型安全性:ObjectからHashMapへの未チェックキャスト

私は何をしましたか?どうすれば問題を解決できますか?

229
DragonBorn

まず最初に、新しいHashMap作成呼び出しでメモリを無駄にしています。 2行目では、この作成されたハッシュマップへの参照を完全に無視し、ガベージコレクターで使用できるようにします。だから、それをしないでください:

private Map<String, String> someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");

第二に、コンパイラは、オブジェクトがHashMapかどうかをチェックせずに、オブジェクトをHashMapにキャストすると文句を言います。しかし、たとえあなたがやろうとしても:

if(getApplicationContext().getBean("someMap") instanceof HashMap) {
    private Map<String, String> someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");
}

おそらくこの警告が表示されるでしょう。問題は、getBeanObjectを返すため、タイプが不明です。 HashMapに直接変換しても、2番目のケースでは問題は発生しません(最初のケースでは警告が表示されない可能性があります。Javaコンパイラの警告がどれほど危険かはわかりません) Java 5)。ただし、それをHashMap<String, String>に変換しています。

HashMapは、実際にはオブジェクトをキーとして取り、値としてオブジェクトを持っているマップです(必要に応じてHashMap<Object, Object>)。したがって、Beanを取得したときに、HashMap<String, String>として表現できることを保証するものではありません。返される非ジェネリック表現には任意のオブジェクトを含めることができるため、HashMap<Date, Calendar>を使用できるからです。

コードがコンパイルされ、エラーなしでString value = map.get("thisString");を実行できる場合、この警告について心配する必要はありません。ただし、マップが完全に文字列値の文字列キーではない場合、実行時にClassCastExceptionを取得します。この場合、ジェネリックはこれをブロックできないためです。

236
MetroidFan2002

問題はキャストが実行時チェックであるということです - しかし型消去のため、実行時には実際には他のFooBarHashMap<String,String>HashMap<Foo,Bar>の間に違いはありません。

@SuppressWarnings("unchecked")を使い、鼻を握ります。ああ、そしてJavaで具体化された総称のためのキャンペーン:)

281
Jon Skeet

上記のメッセージが示すように、リストは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);

説明:最初の型変換では、内部に保持されている型を考慮せずに、オブジェクトがリストであることを検証します(リストレベルで内部型を検証することはできないため)。 2番目の変換が必要になるのは、Listが何らかの種類のオブジェクトを含んでいることだけをコンパイラが認識しているためです。これは、アクセスされたときにリスト内の各オブジェクトのタイプを検証します。

75
Larry Landry

警告はそれだけです。警告。時には警告は無関係ですが、時にはそうではありません。これらは、コンパイラが問題となる可能性があるがそうではないと考えていることに注意を向けさせるために使用されます。

キャストの場合は、この場合常に警告を発します。特定のキャストが安全であると絶対的に確信している場合は、次のように注釈を追加することを検討する必要があります(構文はわかりません)。

@SuppressWarnings (value="unchecked")
27
David M. Karr

GetBeanがオブジェクト参照を返し、それを正しい型にキャストしているため、このメッセージが表示されます。 Java 1.5はあなたに警告を与えます。これは、このように動作するコードでJava 1.5以上を使用することの本質です。 Springには型保証バージョンがあります

someMap=getApplicationContext().getBean<HashMap<String, String>>("someMap");

toDoリストに。

9
David Nehme

本当に警告を取り除きたいのなら、できることの1つは、ジェネリッククラスから拡張するクラスを作成することです。

たとえば、使用しようとしている場合

private Map<String, String> someMap = new HashMap<String, String>();

このような新しいクラスを作ることができます

public class StringMap extends HashMap<String, String>()
{
    // Override constructors
}

それからあなたが使うとき

someMap = (StringMap) getApplicationContext().getBean("someMap");

コンパイラは(もはや一般的ではない)型が何であるかを知っています、そして警告はありません。これは必ずしも完璧な解決策ではないかもしれません、この種の一般クラスの目的を破ると主張する人もいるかもしれませんが、あなたはまだ一般クラスからの同じコードの全てを再利用しています。あなたが使いたいのです。

5
Rabbit

未確認の警告を回避するための解決策:

class MyMap extends HashMap<String, String> {};
someMap = (MyMap)getApplicationContext().getBean("someMap");
1
ochakov

同じオブジェクトをたくさんキャストしていて、コードに@SupressWarnings("unchecked")を付けたくない場合は、アノテーションを付けてメソッドを作成するという方法もあります。このようにしてキャストを集中化し、うまくいけばエラーの可能性を減らすことができます。

@SuppressWarnings("unchecked")
public static List<String> getFooStrings(Map<String, List<String>> ctx) {
    return (List<String>) ctx.get("foos");
}
1
Jeremy

以下のコードは型安全警告を引き起こします

Map<String, Object> myInput = (Map<String, Object>) myRequest.get();

回避策

リスト内に保持されているオブジェクトの種類は検証されないため、パラメータを指定せずに新しいMapオブジェクトを作成します。

ステップ1:新しい一時的な地図を作成する

Map<?, ?> tempMap = (Map<?, ?>) myRequest.get();

ステップ2:メインマップをインスタンス化する

Map<String, Object> myInput=new HashMap<>(myInputObj.size());

ステップ3:一時Mapを繰り返してメインMapに値を設定します

 for(Map.Entry<?, ?> entry :myInputObj.entrySet()){
        myInput.put((String)entry.getKey(),entry.getValue()); 
    }
1
Andy

私は何を間違えましたか?問題を解決するにはどうすればよいですか?

ここに :

Map<String,String> someMap = (Map<String,String>)getApplicationContext().getBean("someMap");

Objectを返すため、通常は使用したくないレガシーメソッドを使用します。

Object getBean(String name) throws BeansException;

BeanファクトリからBeanを取得(シングルトンの場合)/作成(プロトタイプの場合)するのに適したメソッドは次のとおりです。

<T> T getBean(String name, Class<T> requiredType) throws BeansException;

次のような使用:

Map<String,String> someMap = app.getBean(Map.class,"someMap");

コンパイルされますが、すべてのMapオブジェクトが必ずしもMap<String, String>オブジェクトであるとは限らないため、未チェックの変換警告が表示されます。

ただし、汎用コレクションなどのBean汎用クラスでは、<T> T getBean(String name, Class<T> requiredType) throws BeansException;では十分ではありません。これは、パラメーターとして複数のクラスを指定する必要があるためです:コレクション型とその汎用型。

この種のシナリオおよび一般的に、より良いアプローチは、直接BeanFactoryメソッドを使用するのではなく、フレームワークにBeanを注入させることです。

Bean宣言:

@Configuration
public class MyConfiguration{

    @Bean
    public Map<String, String> someMap() {
        Map<String, String> someMap = new HashMap();
        someMap.put("some_key", "some value");
        someMap.put("some_key_2", "some value");
        return someMap;
    }
}

Beanインジェクション:

@Autowired
@Qualifier("someMap")
Map<String, String> someMap;
0
davidxxx