タイプセーフ:未チェックのキャスト


266

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

<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への未チェックのキャスト

何を間違えたのですか?どうすれば問題を解決できますか?


実際に未チェックのキャストの警告を排除パラメータ化HashMapの、へのキャストをチェックするために、私はルーチンを思い付いた:リンクは、私は、これが「正しい」解決策だと思いますが、その価値はかどうか、それは議論の余地があるかもしれません。:)
skiphoppy


回答:


249

さて、まず第一に、あなたは新しい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");
}

おそらくこの警告が表示されます。問題は、getBeanが返されるObjectため、タイプが何であるかは不明です。それを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ジェネリックはこれが発生しないようにすることができないため、実行時にaを取得します。


12
これは少し前のことですが、キャストの前にSet <CustomClass>の型チェックに関する答えを探していましたが、パラメーター化されたジェネリックではインスタンス化できません。例:if(event.getTarget instanceof Set <CustomClass>)ジェネリック型チェックは、?キャストの警告は削除されません。例:if(event.getTarget instanceof Set <?>)
garlicman

315

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

@SuppressWarnings("unchecked")鼻を使ってください。ああ、そしてJavaで具体化されたジェネリックのキャンペーン:)


14
型指定されていないNSMutableWhateverではなく、Javaの具体化されたジェネリックスを使用します。これは、曜日を問わず、10年後のように感じます。少なくともJavaは試みています。
Dan Rosenstark、2011

12
丁度。型チェックを主張する場合、これはHashMap <?、?>でのみ実行でき、ジェネリック型を型チェックしないことと同じであるため、警告は削除されません。それは世界の終わりではありませんが、あなたが警告を抑制したり、それと一緒に暮らしたりするのを捕まえられてうっとうしいです。
ガーリックマン

5
@JonSkeet具体化されたジェネリックとは何ですか?
SasQ 2016

89

上記のメッセージが示すように、リストはa List<Object>とa 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番目の変換が必要になります。これは、アクセスされるときにリスト内の各オブジェクトのタイプを確認します。


3
あなたは私の友達です。リストをキャストする代わりに、リストを反復して各要素をキャストするだけで、警告は表示されません。
juan Isaza、2015年

2
これで警告は削除されましたが、それでも自信がありません:P
mumair

1
はい、コンパイラは目隠しをしているように感じますが、ランタイムはそうではありません:Dだから、これと@SuppressWarnings( "unchecked")の間に違いはありません
channae

1
すごい!@SupressWarningを使用する主な違いは、アノテーションを使用するとIDEおよびコード分析ツールからの警告が排除されることですが、-Werrorフラグのコンパイルを使用している場合は、まだエラーが発生します。このアプローチを使用すると、両方の警告が修正されます。
Edu Costa

30

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

キャストの場合、この場合は常に警告が表示されます。特定のキャストが安全であることを確信している場合は、次のような注釈を追加することを検討してください(構文はわかりません)。

@SuppressWarnings (value="unchecked")

14
-1:警告を受け入れてはなりません。または、これらの種類の警告を抑制または修正します。あなたは多くの警告をしなければならない瞬間が来るでしょう、そしてあなたは一度関連するものを見ないでしょう。
ezdazuzena 2013年

10
パラメータ化されたジェネリック、つまりMapをキャストするときにクラスキャストの警告を実際に回避することはできないため、これが元の質問に対する最良の答えです。
muttonUp 2015年

9

getBeanがオブジェクト参照を返し、それを正しい型にキャストしているため、このメッセージが表示されます。Java 1.5は警告を出します。これは、このように機能するコードでJava 1.5以上を使用する性質です。Springにはタイプセーフバージョンがあります

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

そのtodoリストに。


6

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

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

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

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

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

次に、あなたが使うとき

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

コンパイラーは(もはやジェネリックではない)タイプが何であるかを知っており、警告は出されません。これは必ずしも完全な解決策とは限らない場合があり、この種のジェネリッククラスの目的が無効になると主張する人もいますが、ジェネリッククラスの同じコードをすべて再利用しており、コンパイル時にどの型を宣言するだけです。あなたが使いたいです。


3

未チェックの警告を回避するソリューション:

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

解決策ではなくハックのように見えます。
Malwinder Singh

1
-シリアライズ可能なクラスMyMapは、longタイプの静的なfinal serialVersionUIDフィールドを宣言しません:{
Ulterior

1

別の解決策として、同じオブジェクトを大量にキャストしているときにコードを散らかしたくない場合は@SupressWarnings("unchecked")、アノテーション付きのメソッドを作成します。このようにしてキャストを一元化し、エラーの可能性を減らします。

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

1

以下のコードはタイプセーフ警告を引き起こします

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()); 
    }

0

何を間違えたのですか?どうすれば問題を解決できますか?

ここに :

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>オブジェクトであるとます。

だが <T> T getBean(String name, Class<T> requiredType) throws BeansException;、ジェネリックコレクションなどのBeanジェネリッククラスでは、パラメータとして複数のクラス、つまりコレクションタイプとそのジェネリックタイプを指定する必要があるため、これでは不十分です。

この種のシナリオでは、一般的に、直接使用しない方が良い方法です。 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;
    }
}

豆の注入:

@Autowired
@Qualifier("someMap")
Map<String, String> someMap;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.