java - मैं अनचेक कास्ट चेतावनियों को कैसे संबोधित करूं?




generics warnings (16)

Esko Luontola द्वारा उपरोक्त उत्तर में ऑब्जेक्ट्स अनचेक यूटिलिटी फ़ंक्शन प्रोग्राम अव्यवस्था से बचने का एक शानदार तरीका है।

यदि आप एक संपूर्ण विधि पर SuppressWarnings नहीं चाहते हैं, तो जावा आपको इसे स्थानीय पर रखने के लिए मजबूर करता है। यदि आपको किसी सदस्य पर कलाकारों की आवश्यकता है तो यह इस तरह के कोड का कारण बन सकता है:

@SuppressWarnings("unchecked")
Vector<String> watchedSymbolsClone = (Vector<String>) watchedSymbols.clone();
this.watchedSymbols = watchedSymbolsClone;

उपयोगिता का उपयोग करना बहुत साफ है, और यह अभी भी स्पष्ट है कि आप क्या कर रहे हैं:

this.watchedSymbols = Objects.uncheckedCast(watchedSymbols.clone());

नोट: मुझे यह जोड़ना महत्वपूर्ण लगता है कि कभी-कभी चेतावनी का मतलब है कि आप कुछ गलत कर रहे हैं जैसे:

ArrayList<Integer> intList = new ArrayList<Integer>();
intList.add(1);
Object intListObject = intList; 

 // this line gives an unchecked warning - but no runtime error
ArrayList<String> stringList  = (ArrayList<String>) intListObject;
System.out.println(stringList.get(0)); // cast exception will be given here

संकलक आपको क्या बता रहा है कि यह कास्ट रनटाइम पर जांच नहीं की जाएगी, इसलिए जब तक आप जेनेरिक कंटेनर में डेटा तक पहुंचने का प्रयास नहीं करते हैं तब तक कोई रनटाइम त्रुटि नहीं उठाई जाएगी।

ग्रहण मुझे निम्नलिखित रूप की चेतावनी दे रहा है:

सुरक्षा टाइप करें: ऑब्जेक्ट से हैश मैप में अनचेक कास्ट

यह एक एपीआई से एक कॉल से है कि मेरा कोई नियंत्रण नहीं है जिस पर ऑब्जेक्ट लौटाता है:

HashMap<String, String> getItems(javax.servlet.http.HttpSession session) {
  HashMap<String, String> theHash = (HashMap<String, String>)session.getAttribute("attributeKey");
  return theHash;
}

यदि संभव हो तो मैं ग्रहण चेतावनियों से बचना चाहता हूं, क्योंकि सैद्धांतिक रूप से वे कम से कम संभावित कोड समस्या का संकेत देते हैं। हालांकि, मुझे अभी तक इसे खत्म करने का एक अच्छा तरीका नहीं मिला है। मैं स्वयं एक विधि में शामिल एकल लाइन निकाल सकता हूं और उस विधि में @SuppressWarnings("unchecked") जोड़ सकता @SuppressWarnings("unchecked") , इस प्रकार कोड के ब्लॉक होने के प्रभाव को सीमित कर सकता हूं जहां मैं चेतावनियों को अनदेखा करता हूं। कोई बेहतर विकल्प? मैं इन चेतावनियों को ग्रहण में बंद नहीं करना चाहता हूं।

कोड में आने से पहले, यह आसान था, लेकिन अभी भी चेतावनी उत्तेजित:

HashMap getItems(javax.servlet.http.HttpSession session) {
  HashMap theHash = (HashMap)session.getAttribute("attributeKey");
  return theHash;
}

जब आपने हैश का उपयोग करने की कोशिश की तो समस्या कहीं और थी, आपको चेतावनियां मिलेंगी:

HashMap items = getItems(session);
items.put("this", "that");

Type safety: The method put(Object, Object) belongs to the raw type HashMap.  References to generic type HashMap<K,V> should be parameterized.

HTTP सत्र की दुनिया में आप वास्तव में कास्ट से बच नहीं सकते हैं, क्योंकि एपीआई इस तरह लिखा गया है (केवल Object लेता है और लौटाता है)।

थोड़ी सी काम के साथ आप आसानी से अनचेक कलाकारों से बच सकते हैं, 'हालांकि। इसका मतलब है कि यह एक पारंपरिक घटना में बदल जाएगा जो त्रुटि की स्थिति में ClassCastException देता है)। एक अनचेक अपवाद किसी भी समय बाद में किसी भी बिंदु पर CCE में बदल सकता है, जो कि कलाकार के बिंदु के बजाय (यही वजह है कि यह एक अलग चेतावनी है)।

एक समर्पित कक्षा के साथ हैश मैप को बदलें:

import java.util.AbstractMap;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Attributes extends AbstractMap<String, String> {
    final Map<String, String> content = new HashMap<String, String>();

    @Override
    public Set<Map.Entry<String, String>> entrySet() {
        return content.entrySet();
    }

    @Override
    public Set<String> keySet() {
        return content.keySet();
    }

    @Override
    public Collection<String> values() {
        return content.values();
    }

    @Override
    public String put(final String key, final String value) {
        return content.put(key, value);
    }
}

फिर Map<String,String> बजाय उस वर्ग पर डालें और सब कुछ ठीक उसी स्थान पर चेक किया जाएगा जहां आप अपना कोड लिखते हैं। बाद में कोई अप्रत्याशित ClassCastExceptions नहीं।


आप निम्न की तरह उपयोगिता वर्ग बना सकते हैं, और अनचेक चेतावनी को दबाने के लिए इसका उपयोग कर सकते हैं।

public class Objects {

    /**
     * Helps to avoid using {@code @SuppressWarnings({"unchecked"})} when casting to a generic type.
     */
    @SuppressWarnings({"unchecked"})
    public static <T> T uncheckedCast(Object obj) {
        return (T) obj;
    }
}

आप इसे निम्नानुसार उपयोग कर सकते हैं:

import static Objects.uncheckedCast;
...

HashMap<String, String> getItems(javax.servlet.http.HttpSession session) {
      return uncheckedCast(session.getAttribute("attributeKey"));
}

इसके बारे में कुछ और चर्चा यहां है: http://cleveralias.blogs.com/thought_spearmints/2006/01/suppresswarning.html


इस विशेष मामले में, मैं मैप्स को सीधे एचटीपीशन में संग्रहीत नहीं करता, बल्कि इसके बजाय मेरी अपनी कक्षा का एक उदाहरण, जिसमें बदले में एक मानचित्र (कक्षा का कार्यान्वयन विवरण) होता है। फिर आप सुनिश्चित कर सकते हैं कि मानचित्र में तत्व सही प्रकार के हैं।

लेकिन अगर आप यह जांचना चाहते हैं कि मानचित्र की सामग्री सही प्रकार के हैं, तो आप इस तरह के कोड का उपयोग कर सकते हैं:

public static void main(String[] args) {
    Map<String, Integer> map = new HashMap<String, Integer>();
    map.put("a", 1);
    map.put("b", 2);
    Object obj = map;

    Map<String, Integer> ok = safeCastMap(obj, String.class, Integer.class);
    Map<String, String> error = safeCastMap(obj, String.class, String.class);
}

@SuppressWarnings({"unchecked"})
public static <K, V> Map<K, V> safeCastMap(Object map, Class<K> keyType, Class<V> valueType) {
    checkMap(map);
    checkMapContents(keyType, valueType, (Map<?, ?>) map);
    return (Map<K, V>) map;
}

private static void checkMap(Object map) {
    checkType(Map.class, map);
}

private static <K, V> void checkMapContents(Class<K> keyType, Class<V> valueType, Map<?, ?> map) {
    for (Map.Entry<?, ?> entry : map.entrySet()) {
        checkType(keyType, entry.getKey());
        checkType(valueType, entry.getValue());
    }
}

private static <K> void checkType(Class<K> expectedType, Object obj) {
    if (!expectedType.isInstance(obj)) {
        throw new IllegalArgumentException("Expected " + expectedType + " but was " + obj.getClass() + ": " + obj);
    }
}

इसे ले लो, यह एक नया हैश मैप बनाने से बहुत तेज है, अगर यह पहले से ही एक है, लेकिन फिर भी सुरक्षित है, क्योंकि प्रत्येक तत्व को इसके प्रकार के विरुद्ध चेक किया गया है ...

@SuppressWarnings("unchecked")
public static <K, V> HashMap<K, V> toHashMap(Object input, Class<K> key, Class<V> value) {
       assert input instanceof Map : input;

       for (Map.Entry<?, ?> e : ((HashMap<?, ?>) input).entrySet()) {
           assert key.isAssignableFrom(e.getKey().getClass()) : "Map contains invalid keys";
           assert value.isAssignableFrom(e.getValue().getClass()) : "Map contains invalid values";
       }

       if (input instanceof HashMap)
           return (HashMap<K, V>) input;
       return new HashMap<K, V>((Map<K, V>) input);
    }

एंड्रॉइड स्टूडियो में यदि आप निरीक्षण को अक्षम करना चाहते हैं तो आप इसका उपयोग कर सकते हैं:

//noinspection unchecked
Map<String, String> myMap = (Map<String, String>) deserializeMap();

ग्रहण प्राथमिकता में, जावा-> कंपाइलर-> त्रुटियों / चेतावनियों-> जेनेरिक प्रकारों पर जाएं और Ignore unavoidable generic type problems को Ignore unavoidable generic type problems चेक-बॉक्स की जांच करें।

यह प्रश्न के इरादे को पूरा करता है, यानी

मैं ग्रहण चेतावनियों से बचना चाहता हूं ...

अगर आत्मा नहीं है।


चेतावनी दमन एक समाधान नहीं है। आपको एक बयान में दो स्तर कास्टिंग नहीं करना चाहिए।

HashMap<String, String> getItems(javax.servlet.http.HttpSession session) {

    // first, cast the returned Object to generic HashMap<?,?>
    HashMap<?, ?> theHash = (HashMap<?, ?>)session.getAttribute("attributeKey");

    // next, cast every entry of the HashMap to the required type <String, String>
    HashMap<String, String> returingHash = new HashMap<>();
    for (Entry<?, ?> entry : theHash.entrySet()) {
        returingHash.put((String) entry.getKey(), (String) entry.getValue());
    }
    return returingHash;
}

जाहिर है, जाहिर है, अनचेक कास्ट नहीं करना है।

यदि यह बिल्कुल जरूरी है, तो कम से कम @SuppressWarnings एनोटेशन के दायरे को सीमित करने का प्रयास करें। अपने Javadocs मुताबिक, यह स्थानीय चर पर जा सकता है; इस तरह, यह पूरी विधि को भी प्रभावित नहीं करता है।

उदाहरण:

@SuppressWarnings("unchecked")
Map<String, String> myMap = (Map<String, String>) deserializeMap();

यह निर्धारित करने का कोई तरीका नहीं है कि Map वास्तव में सामान्य पैरामीटर <String, String> होना चाहिए या नहीं। आपको पहले से पता होना चाहिए कि पैरामीटर क्या होना चाहिए (या जब आप ClassCastException प्राप्त करते हैं तो आपको पता चल जाएगा)। यही कारण है कि कोड एक चेतावनी उत्पन्न करता है, क्योंकि संकलक संभवतः यह नहीं जानता कि सुरक्षित है या नहीं।


दुर्भाग्यवश, यहां कोई शानदार विकल्प नहीं है। याद रखें, इन सभी का लक्ष्य टाइप सुरक्षा को संरक्षित करना है। " जावा जेनरिक " गैर-जेनेरिक विरासत पुस्तकालयों से निपटने के लिए एक समाधान प्रदान करता है, और विशेष रूप से अनुभाग खंड 8.2 में "खाली लूप तकनीक" कहा जाता है। असल में, असुरक्षित कलाकार बनाएं, और चेतावनी दबाएं। फिर इस तरह के नक्शे के माध्यम से लूप:

@SuppressWarnings("unchecked")
Map<String, Number> map = getMap();
for (String s : map.keySet());
for (Number n : map.values());

यदि एक अप्रत्याशित प्रकार का सामना करना पड़ता है, तो आपको रनटाइम क्लासकास्ट अपवाद मिलेगा, लेकिन कम से कम यह समस्या के स्रोत के करीब होगा।


मैंने प्रश्न को गलत समझा होगा (एक उदाहरण और आसपास की रेखाएं कुछ अच्छी होंगी), लेकिन आप हमेशा एक उपयुक्त इंटरफेस (और जावा 5 +) का उपयोग क्यों नहीं करते? मुझे कोई कारण नहीं दिखता कि आप किसी Map<KeyType,ValueType> बजाय Map<KeyType,ValueType> क्यों डालना चाहते हैं। असल में, Map बजाय एक चर के प्रकार को HashMap Map पर सेट करने के किसी भी कारण की कल्पना नहीं कर सकता।

और स्रोत एक Object क्यों है? क्या यह एक विरासत संग्रह का पैरामीटर प्रकार है? यदि ऐसा है, तो जेनेरिक का उपयोग करें और इच्छित प्रकार निर्दिष्ट करें।


यदि आप अपना कोड पोस्ट करते हैं तो एक त्वरित अनुमान निश्चित रूप से कह सकता है लेकिन आपने कुछ के साथ कुछ किया होगा

HashMap<String, Object> test = new HashMap();

जो आपको करने की आवश्यकता होने पर चेतावनी उत्पन्न करेगा

HashMap<String, Object> test = new HashMap<String, Object>();

यह देखने लायक हो सकता है

जावा प्रोग्रामिंग भाषा में जेनरिक

यदि आपके अपरिचित से क्या करने की आवश्यकता है।


यह सामान कठिन है, लेकिन यहां मेरे वर्तमान विचार हैं:

यदि आपका एपीआई ऑब्जेक्ट लौटाता है, तो ऐसा कुछ भी नहीं है जो आप कर सकते हैं - इससे कोई फर्क नहीं पड़ता कि आप वस्तु को अंधा कर देंगे। आपने जावा को क्लासकास्ट एक्सेप्शन फेंकने दिया है, या आप प्रत्येक तत्व को स्वयं जांच सकते हैं और एस्सारशन या अवैध अर्ग्यूमेंट एक्सेप्शन या कुछ ऐसे फेंक सकते हैं, लेकिन इन रनटाइम चेक सभी समकक्ष हैं। आपको संकलन समय को अनचेक किया गया है, चाहे आप रनटाइम पर क्या करते हैं।

मैं सिर्फ अंधेरे कास्ट करना पसंद करता हूं और जेवीएम को मेरे रनटाइम चेक को करने देता हूं क्योंकि हम "एपीआई" को वापस लेना चाहते हैं, और आम तौर पर यह मानने के लिए तैयार हैं कि एपीआई काम करता है। यदि आपको उनकी ज़रूरत है, तो कलाकारों के ऊपर हर जगह जेनेरिक का प्रयोग करें। आप वास्तव में कुछ भी खरीद नहीं रहे हैं क्योंकि आपके पास अभी भी एक अंधेरा कलाकार है, लेकिन कम से कम आप वहां से जेनेरिक का उपयोग कर सकते हैं ताकि JVM आपके कोड के अन्य टुकड़ों में अंधेरे जानवरों से बचने में आपकी सहायता कर सके।

इस विशेष मामले में, संभवतः आप SetAttribute को कॉल देख सकते हैं और देख सकते हैं कि प्रकार जा रहा है, इसलिए रास्ते पर उसी प्रकार के प्रकार को अंधा-कास्टिंग करना अनैतिक नहीं है। SetAttribute का संदर्भ देने वाली एक टिप्पणी जोड़ें और इसके साथ किया जाए।


यहां एक संक्षिप्त उदाहरण दिया गया है जो अन्य उत्तरों में उल्लिखित दो रणनीतियों को नियोजित करके "अनचेक कास्ट" चेतावनी से बचाता है।

  1. रनटाइम पर पैरामीटर के रूप में ब्याज के प्रकार की कक्षा को पास करें ( Class<T> inputElementClazz )। फिर आप इसका उपयोग कर सकते हैं: inputElementClazz.cast(anyObject);

  2. संग्रह के प्रकार कास्टिंग के लिए, वाइल्डकार्ड का उपयोग करें? एक सामान्य प्रकार टी के बजाय यह स्वीकार करने के लिए कि आप वास्तव में नहीं जानते कि विरासत कोड ( Collection<?> unknownTypeCollection ) से किस तरह की वस्तुओं की अपेक्षा की जा सकती है। आखिरकार, "अनचेक कास्ट" चेतावनी हमें यह बताना चाहता है: हम यह सुनिश्चित नहीं कर सकते कि हमें Collection<T> , इसलिए Collection<T> करने के लिए ईमानदार बात Collection<?> । यदि पूरी तरह से जरूरी है, तो ज्ञात प्रकार का संग्रह अभी भी बनाया जा सकता है ( Collection<T> knownTypeCollection ज्ञात टाइप टाइप)।

नीचे दिए गए उदाहरण में इंटरफेस किए गए विरासत कोड में संरचित व्यूअर में एक विशेषता "इनपुट" है (संरचित व्यूअर एक पेड़ या टेबल विजेट है, "इनपुट" इसके पीछे डेटा मॉडल है)। यह "इनपुट" किसी भी प्रकार का जावा संग्रह हो सकता है।

public void dragFinished(StructuredViewer structuredViewer, Class<T> inputElementClazz) {
    IStructuredSelection selection = (IStructuredSelection) structuredViewer.getSelection();
    // legacy code returns an Object from getFirstElement,
    // the developer knows/hopes it is of type inputElementClazz, but the compiler cannot know
    T firstElement = inputElementClazz.cast(selection.getFirstElement());

    // legacy code returns an object from getInput, so we deal with it as a Collection<?>
    Collection<?> unknownTypeCollection = (Collection<?>) structuredViewer.getInput();

    // for some operations we do not even need a collection with known types
    unknownTypeCollection.remove(firstElement);

    // nothing prevents us from building a Collection of a known type, should we really need one
    Collection<T> knownTypeCollection = new ArrayList<T>();
    for (Object object : unknownTypeCollection) {
        T aT = inputElementClazz.cast(object);
        knownTypeCollection.add(aT);
        System.out.println(aT.getClass());
    }

    structuredViewer.refresh();
}

स्वाभाविक रूप से, उपर्युक्त कोड रनटाइम त्रुटियों को दे सकता है यदि हम गलत डेटा प्रकारों के साथ विरासत कोड का उपयोग करते हैं (उदाहरण के लिए यदि हम जावा संग्रह के बजाय संरचित व्यूअर के "इनपुट" के रूप में एक सरणी सेट करते हैं)।

विधि को कॉल करने का उदाहरण:

dragFinishedStrategy.dragFinished(viewer, Product.class);

समस्या यहां निहित है:

... = (HashMap<String, String>)session.getAttribute("attributeKey");

session.getAttribute(...) का परिणाम एक object जो कुछ भी हो सकता है, लेकिन चूंकि आप "जानते हैं" यह HashMap<String, String> आप इसे पहले जांच किए बिना कास्टिंग कर रहे हैं। इस प्रकार, चेतावनी। Pedantic होने के लिए, जो जावा चाहता है कि आप इस मामले में हों, आपको परिणाम पुनर्प्राप्त करना चाहिए और instanceof के साथ इसकी संगतता को सत्यापित करना चाहिए।


Solution: Disable this warning in Eclipse. Don't @SuppressWarnings it, just disable it completely.

Several of the "solutions" presented above are way out of line, making code unreadable for the sake of suppressing a silly warning.





warnings