जावा क्लास.कास्ट () बनाम कास्ट ऑपरेटर


107

मेरे C ++ दिनों के दौरान सिखाई जाने वाली C- स्टाइल कास्ट ऑपरेटर की बुराइयों के बारे में मुझे यह जानकर पहली बार में प्रसन्नता हुई कि Java 5 java.lang.Classमें एक castविधि प्राप्त हुई थी ।

मैंने सोचा कि आखिरकार हमारे पास कास्टिंग से निपटने का एक तरीका है।

पता चला है Class.castके रूप में ही नहीं है static_castC ++ में। यह अधिक पसंद है reinterpret_cast। यह एक संकलन त्रुटि उत्पन्न नहीं करेगा जहां यह अपेक्षित है और इसके बजाय रनटाइम के लिए स्थगित हो जाएगा। विभिन्न व्यवहारों को प्रदर्शित करने के लिए यहां एक सरल परीक्षण मामला है।

package test;

import static org.junit.Assert.assertTrue;

import org.junit.Test;


public class TestCast
{
    static final class Foo
    {
    }

    static class Bar
    {
    }

    static final class BarSubclass
        extends Bar
    {
    }

    @Test
    public void test ( )
    {
        final Foo foo = new Foo( );
        final Bar bar = new Bar( );
        final BarSubclass bar_subclass = new BarSubclass( );

        {
            final Bar bar_ref = bar;
        }

        {
            // Compilation error
            final Bar bar_ref = foo;
        }
        {
            // Compilation error
            final Bar bar_ref = (Bar) foo;
        }

        try
        {
            // !!! Compiles fine, runtime exception
            Bar.class.cast( foo );
        }
        catch ( final ClassCastException ex )
        {
            assertTrue( true );
        }

        {
            final Bar bar_ref = bar_subclass;
        }

        try
        {
            // Compiles fine, runtime exception, equivalent of C++ dynamic_cast
            final BarSubclass bar_subclass_ref = (BarSubclass) bar;
        }
        catch ( final ClassCastException ex )
        {
            assertTrue( true );
        }
    }
}

तो, ये मेरे सवाल हैं।

  1. क्या Class.cast()जेनिक्स की जमीन पर कब्जा कर लिया जाए? वहां इसके काफी वैध उपयोग हैं।
  2. जब संकलक Class.cast()का उपयोग किया जाता है तो संकलक त्रुटियों को उत्पन्न करना चाहिए और संकलन समय पर अवैध परिस्थितियों का निर्धारण किया जा सकता है?
  3. क्या जावा को C ++ के समान भाषा निर्माण के लिए एक कास्ट ऑपरेटर प्रदान करना चाहिए?

4
सरल उत्तर: (1) "पीढ़ी भूमि" कहाँ है? यह कैसे अलग है कि कास्ट ऑपरेटर अब कैसे उपयोग किया जाता है? (२) शायद। लेकिन अब तक लिखी सभी जावा कोड के 99% में, यह है अत्यंत किसी को भी उपयोग करने के लिए की संभावना नहीं Class.cast()है जब अवैध स्थिति संकलन समय पर निर्धारित किया जा सकता। उस मामले में, हर कोई लेकिन आप सिर्फ मानक कास्ट ऑपरेटर का उपयोग करते हैं। (3) जावा में भाषा निर्माण के रूप में एक कास्ट ऑपरेटर होता है। यह C ++ के समान नहीं है। ऐसा इसलिए है क्योंकि जावा के कई भाषा निर्माण C ++ के समान नहीं हैं। सतही समानता के बावजूद, जावा और सी ++ काफी अलग हैं।
डैनियल प्राइडेन

जवाबों:


117

मैंने केवल Class.cast(Object)"जेनरिक भूमि" में चेतावनी से बचने के लिए उपयोग किया है। मैं अक्सर चीजों को इस तरह से देखता हूं:

@SuppressWarnings("unchecked")
<T> T doSomething() {
    Object o;
    // snip
    return (T) o;
}

इसे अक्सर इसके द्वारा प्रतिस्थापित करना सबसे अच्छा है:

<T> T doSomething(Class<T> cls) {
    Object o;
    // snip
    return cls.cast(o);
}

कि Class.cast(Object)मैं कभी भर में आया है के लिए केवल उपयोग मामला है ।

संकलक चेतावनी के बारे में: मुझे संदेह है कि Class.cast(Object)संकलक के लिए विशेष नहीं है। इसे वैधानिक रूप से उपयोग किए जाने पर अनुकूलित किया जा सकता है (अर्थात Foo.class.cast(o)इसके बजाय cls.cast(o)) लेकिन मैंने इसका उपयोग करते हुए कभी किसी को नहीं देखा है - जो इस अनुकूलन को संकलक में कुछ बेकार बनाने का प्रयास करता है।


8
मुझे लगता है कि इस मामले में सही तरीका यह होगा कि पहले इस तरह की जाँच करें: यदि (cls.isInstance (o)) {वापसी cls.cast (o); } यदि आप सुनिश्चित हैं कि सिवाय इसके कि प्रकार सही होगा, को छोड़कर।
पूस

1
दूसरा संस्करण बेहतर क्यों है? क्या पहला संस्करण अधिक कुशल नहीं है क्योंकि कॉलर का कोड वैसे भी गतिशील कास्ट होगा?
user1944408

1
@ user1944408 जब तक आपके प्रदर्शन के बारे में सख्ती से बात कर रहे हैं, हालांकि यह बहुत नगण्य है। मैं ClassCastException प्राप्त करने के विचार को पसंद नहीं करूँगा जहाँ कोई स्पष्ट मामला नहीं है। इसके अतिरिक्त, दूसरा वह बेहतर काम करता है जहां कंपाइलर टी को अनुमान नहीं लगा सकता है, जैसे list.add (यह। <string> doSomething ()) बनाम list.add (doSomething (String.class))
sfussngger

2
लेकिन आप अभी भी ClassCastException प्राप्त कर सकते हैं जब आप संकलन समय (o) को कॉल करते हैं, जबकि आपको संकलन समय में कोई चेतावनी नहीं मिल रही है। मैं पहले वेरिएंट को पसंद करता हूं, लेकिन जब मैं कास्टिंग नहीं कर सकता तो मुझे वापस लौटने के लिए उदाहरण की आवश्यकता होने पर मैं दूसरे संस्करण का उपयोग करूंगा। उस मामले में मैं एक कोशिश पकड़ ब्लॉक में cls.cast (ओ) लपेटेंगे। मैं भी आपके साथ सहमत हूं कि यह भी बेहतर है जब कंपाइलर टी का अनुमान नहीं लगा सकता है। उत्तर के लिए धन्यवाद।
user1944408

1
जब मैं कक्षा <T> cls को गतिशील होने की आवश्यकता होती है, तो मैं दूसरे संस्करण का उपयोग करता हूं, उदाहरण के लिए यदि मैं प्रतिबिंब का उपयोग करता हूं, लेकिन अन्य सभी मामलों में मैं पहले वाले को पसंद करता हूं। वैसे मुझे लगता है कि यह सिर्फ एक व्यक्तिगत स्वाद है।
user1944408

20

सबसे पहले, आप लगभग किसी भी कास्ट को करने के लिए दृढ़ता से हतोत्साहित होते हैं, इसलिए आपको इसे यथासंभव सीमित करना चाहिए! आप जावा के संकलन-समय के लाभों को दृढ़ता से टाइप की गई सुविधाओं से खो देते हैं।

किसी भी मामले में, Class.cast()मुख्य रूप से उपयोग किया जाना चाहिए जब आप Classप्रतिबिंब के माध्यम से टोकन प्राप्त करते हैं । यह लिखना अधिक मुहावरेदार है

MyObject myObject = (MyObject) object

बजाय

MyObject myObject = MyObject.class.cast(object)

संपादित करें: संकलन समय पर त्रुटियां

ओवर ऑल, जावा रन टाइम पर ही कास्ट चेक करता है। हालाँकि, संकलक एक त्रुटि जारी कर सकता है यदि यह साबित कर सकता है कि ऐसी जातियाँ कभी सफल नहीं हो सकती हैं (जैसे किसी वर्ग को किसी अन्य वर्ग में डालना जो एक सुपरटेप नहीं है और एक अंतिम वर्ग प्रकार को वर्ग / इंटरफ़ेस में डाल सकता है जो अपने प्रकार के पदानुक्रम में नहीं है)। यहाँ तब से Fooऔर Barऐसी कक्षाएं हैं जो एक दूसरे के पदानुक्रम में नहीं हैं, कलाकार कभी सफल नहीं हो सकते हैं।


मैं इस बात से पूरी तरह सहमत हूं कि जातियों का इस्तेमाल संयम से किया जाना चाहिए, इसीलिए कुछ ऐसा जो आसानी से खोजा जा सकता है, कोड रिफैक्टिंग / रखरखाव के लिए एक बहुत अच्छा लाभ होगा। लेकिन समस्या यह है कि हालांकि पहली नज़र में Class.castयह बिल को फिट करने के लिए लगता है कि यह हल करने की तुलना में अधिक समस्याएं पैदा करता है।
अलेक्जेंडर पोगरेबनेक

16

भाषाओं के बीच निर्माण और अवधारणाओं के अनुवाद और अनुवाद के लिए यह हमेशा समस्याग्रस्त और अक्सर भ्रामक होता है। कास्टिंग कोई अपवाद नहीं है। विशेष रूप से क्योंकि जावा एक गतिशील भाषा है और C ++ कुछ अलग है।

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

जावा और सी ++ में भी जेनरिक काफी अलग हैं। अपने आप पर चिंता न करें कि आप जावा में C ++ चीजें कैसे करते हैं। आपको यह सीखने की जरूरत है कि जावा तरीके से कैसे काम करें।


सभी जानकारी रनटाइम पर नहीं होती है। जैसा कि आप देख सकते हैं मेरे उदाहरण (Bar) fooसे संकलन समय पर एक त्रुटि उत्पन्न करता है, लेकिन Bar.class.cast(foo)नहीं करता है। मेरी राय में अगर इसे इस तरीके से इस्तेमाल किया जाए तो यह होना चाहिए।
अलेक्जेंडर पोगरेबनीक

5
@Alexander Pogrebnyak: फिर ऐसा मत करो! Bar.class.cast(foo)स्पष्ट रूप से संकलक को बताता है कि आप रनटाइम पर कास्ट करना चाहते हैं। यदि आप कलाकारों की वैधता पर एक संकलन-समय की जांच चाहते हैं, तो आपकी एकमात्र पसंद (Bar) fooस्टाइल कास्ट करना है।
डैनियल प्राइडेन

आपको क्या लगता है कि उसी तरह से करने का तरीका क्या है? चूँकि जावा कई श्रेणी की विरासत का समर्थन नहीं करता है।
यमूर

13

Class.cast()शायद ही कभी जावा कोड में उपयोग किया जाता है। यदि इसका उपयोग किया जाता है, तो आमतौर पर उन प्रकारों के साथ होता है जो केवल रनटाइम पर ज्ञात होते हैं (जैसे कि उनके संबंधित Classवस्तुओं के माध्यम से और कुछ प्रकार के पैरामीटर द्वारा)। यह केवल कोड में वास्तव में उपयोगी है जो जेनरिक का उपयोग करता है (यही कारण है कि इसे पहले पेश नहीं किया गया था)।

यह इसके समान नहीं है reinterpret_cast, क्योंकि यह आपको रनटाइम पर किसी सामान्य कास्ट (यानी आप सामान्य प्रकार के मापदंडों को तोड़ सकते हैं , लेकिन "वास्तविक" प्रकारों को नहीं तोड़ सकते हैं ) प्रकार की प्रणाली को तोड़ने की अनुमति नहीं देगा ।

सी-स्टाइल कास्ट ऑपरेटर की बुराइयाँ आमतौर पर जावा पर लागू नहीं होती हैं। जावा कोड जो सी-स्टाइल कास्ट की तरह दिखता है, वह जावा में dynamic_cast<>()एक संदर्भ प्रकार के साथ सबसे अधिक समान है (याद रखें: जावा में रनटाइम प्रकार की जानकारी है)।

आम तौर पर जावा कास्टिंग के साथ C ++ कास्टिंग ऑपरेटरों की तुलना करना बहुत कठिन है क्योंकि जावा में आप केवल संदर्भ कभी भी डाल सकते हैं और कोई भी रूपांतरण कभी भी वस्तुओं के लिए नहीं होता है (केवल इस सिंटैक्स का उपयोग करके आदिम मूल्यों को परिवर्तित किया जा सकता है)।


dynamic_cast<>()एक संदर्भ प्रकार के साथ।
टॉम हॉकिन -

@ टिप्पणी: क्या यह सही है? मेरा C ++ बहुत जंग खा रहा है, मुझे इसके बारे में बहुत कुछ फिर से बताना पड़ा ;-)
Joachim Sauer

+1 के लिए: "सी-स्टाइल कास्ट ऑपरेटर की बुराइयाँ आमतौर पर जावा पर लागू नहीं होती हैं।" काफी सच। मैं इस प्रश्न पर टिप्पणी के रूप में उन सटीक शब्दों को पोस्ट करने वाला था।
डैनियल प्रेडेन

4

आमतौर पर कास्ट ऑपरेटर को क्लास # कास्ट मेथड के लिए पसंद किया जाता है क्योंकि यह अधिक संक्षिप्त है और कोड के साथ समवर्ती मुद्दों को थूकने के लिए कंपाइलर द्वारा विश्लेषण किया जा सकता है।

क्लास # कास्ट कंपटीशन के दौरान रन-टाइम पर टाइप चेकिंग की जिम्मेदारी लेता है।

क्लास # कास्ट के लिए निश्चित रूप से उपयोग-मामले हैं, खासकर जब यह चिंतनशील संचालन की बात आती है।

चूँकि लैम्ब्डा जावा में आई है, इसलिए मैं व्यक्तिगत रूप से क्लास # कास्ट का उपयोग करना पसंद करता हूं, अगर मैं सार प्रकारों के साथ काम कर रहा हूं, उदाहरण के लिए।

Dog findMyDog(String name, Breed breed) {
    return lostAnimals.stream()
                      .filter(Dog.class::isInstance)
                      .map(Dog.class::cast)
                      .filter(dog -> dog.getName().equalsIgnoreCase(name))
                      .filter(dog -> dog.getBreed() == breed)
                      .findFirst()
                      .orElse(null);
}

3

C ++ और Java अलग-अलग भाषाएं हैं।

जावा सी-स्टाइल कास्ट ऑपरेटर सी / सी ++ संस्करण की तुलना में बहुत अधिक प्रतिबंधित है। प्रभावी रूप से जावा कास्ट C ++ डायनामिककास्ट की तरह है यदि आपके द्वारा जिस ऑब्जेक्ट को नए वर्ग में नहीं डाला जा सकता है वह आपको रन टाइम मिलेगा (या यदि कोड में पर्याप्त जानकारी है तो कंपाइल टाइम) अपवाद। इस प्रकार जावा में C प्रकार का उपयोग नहीं करने का C ++ विचार एक अच्छा विचार नहीं है


0

सबसे अधिक उल्लेख के रूप में बदसूरत कास्ट चेतावनियों को हटाने के अलावा, क्लास.कास्ट रन-टाइम कास्ट है जिसे ज्यादातर जेनेरिक कास्टिंग के साथ उपयोग किया जाता है, सामान्य जानकारी के कारण रन टाइम पर मिटा दिया जाएगा और कुछ को प्रत्येक जेनेरिक वस्तु के रूप में कैसे माना जाएगा, इसके कारण ऐसा नहीं होता है एक प्रारंभिक ClassCastException फेंक दें।

उदाहरण के लिए सर्विस लॉकर ऑब्जेक्ट बनाते समय इस ट्रिक का उपयोग करते हैं, एस पी = सर्विस.कास्ट (c.newInstance ()) की जांच करें; जब एसपी = (एस) c.newInstance (); कोई चेतावनी नहीं दिखा सकता है और 'सुरक्षा टाइप करें: ऑब्जेक्ट से S पर अनियंत्रित कास्ट' (समान ऑब्जेक्ट P = (ऑब्जेक्ट) c.newInstance ();)

-साथ ही यह जांचता है कि कास्ट की गई वस्तु कास्टिंग क्लास का उदाहरण है या नहीं, तो यह कास्ट ऑपरेटर को इसे दबाकर चेतावनी देने और छिपाने के लिए उपयोग करेगा।

गतिशील कलाकारों के लिए जावा कार्यान्वयन:

@SuppressWarnings("unchecked")
public T cast(Object obj) {
    if (obj != null && !isInstance(obj))
        throw new ClassCastException(cannotCastMsg(obj));
    return (T) obj;
}




    private S nextService() {
        if (!hasNextService())
            throw new NoSuchElementException();
        String cn = nextName;
        nextName = null;
        Class<?> c = null;
        try {
            c = Class.forName(cn, false, loader);
        } catch (ClassNotFoundException x) {
            fail(service,
                 "Provider " + cn + " not found");
        }
        if (!service.isAssignableFrom(c)) {
            fail(service,
                 "Provider " + cn  + " not a subtype");
        }
        try {
            S p = service.cast(c.newInstance());
            providers.put(cn, p);
            return p;
        } catch (Throwable x) {
            fail(service,
                 "Provider " + cn + " could not be instantiated",
                 x);
        }
        throw new Error();          // This cannot happen
    }

0

व्यक्तिगत रूप से, मैंने इसका उपयोग POJO कनवर्टर के JSON के निर्माण से पहले किया है। इस मामले में कि फ़ंक्शन के साथ संसाधित किए गए JSONObject में एक सरणी या नेस्टेड JSONObjects है (यह अनुमान लगाते हुए कि यहां डेटा एक आदिम प्रकार का नहीं है या String), मैं class.cast()इस तरीके का उपयोग करके सेटर विधि को लागू करने का प्रयास करता हूं :

public static Object convertResponse(Class<?> clazz, JSONObject readResultObject) {
    ...
    for(Method m : clazz.getMethods()) {
        if(!m.isAnnotationPresent(convertResultIgnore.class) && 
            m.getName().toLowerCase().startsWith("set")) {
        ...
        m.invoke(returnObject,  m.getParameters()[0].getClass().cast(convertResponse(m.getParameters()[0].getType(), readResultObject.getJSONObject(key))));
    }
    ...
}

यकीन नहीं होता कि यह बेहद मददगार है, लेकिन जैसा कि पहले कहा class.cast()जा चुका है, परावर्तन मेरे विचार से बहुत कम वैध उपयोग के मामलों में से एक है, कम से कम अब आपके पास एक और उदाहरण है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.