जावा: इंस्टेंसोफ़ और जेनरिक


135

इससे पहले कि मैं एक मूल्य सूचकांक के लिए अपनी सामान्य डेटा संरचना के माध्यम से देखता हूं, मैं यह देखना चाहता हूं कि क्या यह उस प्रकार thisका उदाहरण है जिसे पैरामीट्रिक किया गया है।

जब मैं ऐसा करता हूं तो ग्रहण की शिकायत होती है:

@Override
public int indexOf(Object arg0) {
    if (!(arg0 instanceof E)) {
        return -1;
    }

यह त्रुटि संदेश है:

टाइप पैरामीटर ई के खिलाफ इंस्टाफॉफ़ चेक नहीं कर सकता है। इसके बजाय इसका उपयोग करें ऑब्जेक्ट क्योंकि सामान्य प्रकार की जानकारी रनटाइम पर मिटा दी जाएगी

इसे करने का बेहतर तरीका क्या है?

जवाबों:


79

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

आप इस तरह से अपनी वस्तु के लिए एक कारखाना बनाकर इसे पकड़ सकते हैं:

 public static <T> MyObject<T> createMyObject(Class<T> type) {
    return new MyObject<T>(type);
 }

और फिर ऑब्जेक्ट के कंस्ट्रक्टर स्टोर में उस प्रकार, इतना चर है ताकि आपकी विधि इस तरह दिख सके:

        if (arg0 != null && !(this.type.isAssignableFrom(arg0.getClass()))
        {
            return -1;
        }

3
मुझे नहीं लगता कि आप चाहते हैं Class.isAssignableFrom
टॉम हॉल्टिन -

@ इसके बाद, मैंने इसे स्मृति से पिछली रात लिखा था, और मैंने इसे वास्तव में कक्षा (duh!) पास करने के लिए तय किया था, लेकिन अन्यथा, मुझे समझ नहीं आता कि आप इसे क्यों नहीं चाहेंगे (शायद मुझे आज सुबह और अधिक कॉफी चाहिए, मुझे ' केवल मेरे पहले कप पर)।
यिशई

मैं टॉम के साथ हूं। क्या आप स्पष्ट कर सकते हैं? IsAssignableFrom () का उपयोग करना नौकरी के लिए मेरी पसंद होता। शायद मुझे कुछ याद आ रहा है?
luis.espinal

6
@ लूलिस, टॉम की टिप्पणी का मतलब शायद यह है कि मुझे उपयोग करना चाहिए () और वास्तविक arg0 पैरामीटर पास करें। इसमें अशक्त जांच से बचने का लाभ है।
यिशै

1
मेरे पास एक समान स्थिति है लेकिन मैं जवाब को पूरी तरह से नहीं समझ सकता। क्या आप कृपया मेरे जैसे डमी के लिए इसे बेहतर बता सकते हैं?
मरिउज़ो

40

जेनरिक के साथ रनटाइम प्रकार की जाँच के दो विकल्प:

विकल्प 1 - अपने निर्माता को भ्रष्ट करें

मान लेते हैं कि आप indexOf (...) को ओवरराइड कर रहे हैं, और आप केवल प्रदर्शन के लिए प्रकार की जांच करना चाहते हैं, अपने आप को संपूर्ण संग्रह को पुन: सहेजने के लिए।

इस तरह से गंदे निर्माण करें:

public MyCollection<T>(Class<T> t) {

    this.t = t;
}

फिर आप प्रकार की जांच करने के लिए isAssignableFrom का उपयोग कर सकते हैं ।

public int indexOf(Object o) {

    if (
        o != null &&

        !t.isAssignableFrom(o.getClass())

    ) return -1;

//...

हर बार जब आप अपनी वस्तु को पलटाते हैं तो आपको खुद को दोहराना होगा:

new MyCollection<Apples>(Apples.class);

आप यह तय कर सकते हैं कि यह इसके लायक नहीं है। ArrayList.indexOf (...) के कार्यान्वयन में , वे जाँच नहीं करते हैं कि प्रकार मेल खाता है।

विकल्प 2 - इसे विफल होने दें

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

protected abstract void abstractMethod(T element);

आप इसे इस तरह से उपयोग कर सकते हैं:

public int indexOf(Object o) {

    try {

        abstractMethod((T) o);

    } catch (ClassCastException e) {

//...

आप ऑब्जेक्ट को टी (आपके जेनेरिक प्रकार) में डाल रहे हैं, बस संकलक को मूर्ख बनाने के लिए। आपकी कास्ट रनटाइम पर कुछ भी नहीं करती है , लेकिन आप तब भी एक क्लासकैस्टैसेप्शन प्राप्त करेंगे जब आप गलत प्रकार की वस्तु को अपने अमूर्त तरीके से पास करने की कोशिश करेंगे।

नोट 1: यदि आप अपनी सार पद्धति में अतिरिक्त अनियंत्रित जातियाँ कर रहे हैं, तो आपकी ClassCastException यहाँ पकड़ी जाएगी। यह अच्छा या बुरा हो सकता है, इसलिए इसके माध्यम से सोचें।

नोट 2: जब आप Instof का उपयोग करते हैं तो आपको एक निशुल्क चेक मिलता है । चूंकि आप इसका उपयोग नहीं कर सकते हैं, इसलिए आपको अपने नंगे हाथों से नल की जांच करने की आवश्यकता हो सकती है।


18

पुरानी पोस्ट, लेकिन सामान्य उदाहरण के लिए एक आसान तरीका है।

public static <T> boolean isInstanceOf(Class<T> clazz, Class<T> targetClass) {
    return clazz.isInstance(targetClass);
}

21
यह स्पष्ट नहीं है कि आप वास्तव में यहाँ क्या योगदान दे रहे हैं।
एंड्रयू

12

बशर्ते आपकी कक्षा सामान्य पैरामीटर के साथ एक वर्ग का विस्तार करती है, आप इसे प्रतिबिंब के माध्यम से रनटाइम पर भी प्राप्त कर सकते हैं, और फिर तुलना के लिए इसका उपयोग कर सकते हैं, अर्थात

class YourClass extends SomeOtherClass<String>
{

   private Class<?> clazz;

   public Class<?> getParameterizedClass()
   {
      if(clazz == null)
      {
         ParameterizedType pt = (ParameterizedType)this.getClass().getGenericSuperclass();
          clazz = (Class<?>)pt.getActualTypeArguments()[0];
       }
       return clazz;
    }
}

ऊपर के मामले में, रनटाइम पर आपको getParameterizedClass () से String.class मिल जाएगा, और यह कैश हो जाता है ताकि आपको कई चेक पर कोई प्रतिबिंब ओवरहेड न मिले। ध्यान दें कि आप ParameterizedType.getActualTypeArguments () विधि से अनुक्रमणिका द्वारा अन्य पैरामीटर प्रकार प्राप्त कर सकते हैं।


7

मुझे भी यही समस्या थी और यहाँ मेरा समाधान है (बहुत विनम्र, @george: इस बार संकलन और काम ...)।

मेरा प्रोबेट एक अमूर्त वर्ग के अंदर था जो ऑब्जर्वर को लागू करता है। ऑब्ज़र्वेबल फेयर मेथड अपडेट (...) ऑब्जेक्ट क्लास के साथ जो किसी भी तरह का ऑब्जेक्ट हो सकता है।

मैं केवल टाइप T के ऑब्जेक्ट को हैंडलर करना चाहता हूं

समाधान क्रम में प्रकारों की तुलना करने में सक्षम होने के लिए, निर्माणकर्ता को कक्षा पास करना है।

public abstract class AbstractOne<T> implements Observer {

  private Class<T> tClass;
    public AbstractOne(Class<T> clazz) {
    tClass = clazz;
  }

  @Override
  public void update(Observable o, Object arg) {
    if (tClass.isInstance(arg)) {
      // Here I am, arg has the type T
      foo((T) arg);
    }
  }

  public abstract foo(T t);

}

कार्यान्वयन के लिए हमें बस कक्षा को कंस्ट्रक्टर के पास भेजना होगा

public class OneImpl extends AbstractOne<Rule> {
  public OneImpl() {
    super(Rule.class);
  }

  @Override
  public void foo(Rule t){
  }
}

5

या आप ई उदा में डालने का असफल प्रयास पकड़ सकते हैं

public int indexOf(Object arg0){
  try{
    E test=(E)arg0;
    return doStuff(test);
  }catch(ClassCastException e){
    return -1;
  }
}

1
+1 एक साधारण जांच के लिए एक व्यावहारिक गैर-ओवर इंजीनियर समाधान! काश मैं
higuaro

24
यह काम नहीं करेगा। ई रनटाइम पर मिटा दिया जाता है, इसलिए कास्ट विफल नहीं होगा, आपको बस इसके बारे में एक संकलक चेतावनी मिलेगी।
यिशै

1

तकनीकी रूप से आपको ऐसा नहीं करना चाहिए, यह जेनरिक की बात है, इसलिए आप संकलन-प्रकार की जाँच कर सकते हैं:

public int indexOf(E arg0) {
   ...
}

लेकिन तब अगर आप एक वर्ग पदानुक्रम है तो @Override एक समस्या हो सकती है। नहीं तो यिशै का जवाब देखिए।


हाँ, सूची इंटरफ़ेस मांग करता है कि फ़ंक्शन ऑब्जेक्ट पैरामीटर लेता है।
निक हेइनर

आप सूची लागू कर रहे हैं? आप सूची <E> लागू क्यों नहीं कर रहे हैं?
जेसन एस

(उदाहरण के लिए देखें ArrayList की घोषणा: java.sun.com/j2se/1.5.0/docs/api/java/util/ArrayList.html )
जेसन एस

@Rosarch: सूची इंटरफ़ेस का इंडेक्सऑफ़ () विधि की आवश्यकता नहीं है कि दिया गया तर्क उसी प्रकार का है जिस ऑब्जेक्ट की आप तलाश कर रहे हैं। यह सिर्फ। to () और इसके लिए अलग-अलग प्रकार की वस्तुएं हो सकती हैं। () एक-दूसरे के लिए। इसे हटाने () विधि के लिए एक ही मुद्दा है, देखें: stackoverflow.com/questions/104799/…
newacct

1
[[[विदुषी]]] कोई बात नहीं, मैंने सोचा था कि IndexOf () वस्तु के बजाय एक पैरामीटर के रूप में E की आवश्यकता है। (उन्होंने ऐसा क्यों किया?!?!)
जेसन एस

1

ऑब्जेक्ट का रनटाइम प्रकार फ़िल्टर करने के लिए एक अपेक्षाकृत मनमानी स्थिति है। मैं सुझाव देता हूं कि आप इस संग्रह से दूर रहें। यह केवल एक निर्माण में पारित एक फिल्टर के लिए अपने संग्रह प्रतिनिधि होने से हासिल की है।

public interface FilterObject {
     boolean isAllowed(Object obj);
}

public class FilterOptimizedList<E> implements List<E> {
     private final FilterObject filter;
     ...
     public FilterOptimizedList(FilterObject filter) {
         if (filter == null) {
             throw NullPointerException();
         }
         this.filter = filter;
     }
     ...
     public int indexOf(Object obj) {
         if (!filter.isAllows(obj)) {
              return -1;
         }
         ...
     }
     ...
}

     final List<String> longStrs = new FilterOptimizedList<String>(
         new FilterObject() { public boolean isAllowed(Object obj) {
             if (obj == null) {
                 return true;
             } else if (obj instanceof String) {
                 String str = (String)str;
                 return str.length() > = 4;
             } else {
                 return false;
             }
         }}
     );

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