सवाल सामान्य प्रकार का java.util.List प्राप्त करें


मेरे पास है;

List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();

क्या सामान्य प्रकार की सूची पुनर्प्राप्त करने के लिए एक (आसान) तरीका है?


213
2017-12-21 21:08


मूल


एक सूची वस्तु का प्रोग्रामेटिक रूप से निरीक्षण करने और इसके सामान्य प्रकार को देखने में सक्षम होने के लिए। एक विधि संग्रह के सामान्य प्रकार के आधार पर वस्तुओं को सम्मिलित करना चाह सकता है। यह उन भाषाओं में संभव है जो संकलन समय के बजाय रनटाइम पर जेनरिक लागू करते हैं। - Steve Kuo
दाएं - रनटाइम डिटेक्शन को अनुमति देने का एकमात्र तरीका उप-वर्गीकरण द्वारा किया जाता है: आप वास्तव में सामान्य प्रकार का विस्तार कर सकते हैं और फिर प्रतिबिंब खोज प्रकार घोषणा का उपयोग कर सकते हैं जो उपप्रकार का उपयोग किया जाता है। यह काफी प्रतिबिंब है, लेकिन संभव है। दुर्भाग्यवश यह लागू करने का कोई आसान तरीका नहीं है कि किसी को सामान्य उप-वर्ग का उपयोग करना चाहिए। - StaxMan
निश्चित रूप से स्ट्रिंगलिस्ट में स्ट्रिंग्स और पूर्णांकलिस्ट पूर्णांक हैं? इसे और अधिक जटिल क्यों बनाते हैं? - Ben Thurley


जवाब:


यदि वे वास्तव में एक निश्चित वर्ग के क्षेत्र हैं, तो आप प्रतिबिंब की थोड़ी मदद के साथ उन्हें प्राप्त कर सकते हैं:

package test;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;

public class Test {

    List<String> stringList = new ArrayList<String>();
    List<Integer> integerList = new ArrayList<Integer>();

    public static void main(String... args) throws Exception {
        Field stringListField = Test.class.getDeclaredField("stringList");
        ParameterizedType stringListType = (ParameterizedType) stringListField.getGenericType();
        Class<?> stringListClass = (Class<?>) stringListType.getActualTypeArguments()[0];
        System.out.println(stringListClass); // class java.lang.String.

        Field integerListField = Test.class.getDeclaredField("integerList");
        ParameterizedType integerListType = (ParameterizedType) integerListField.getGenericType();
        Class<?> integerListClass = (Class<?>) integerListType.getActualTypeArguments()[0];
        System.out.println(integerListClass); // class java.lang.Integer.
    }
}

आप पैरामीटर प्रकारों और विधियों के प्रकार के प्रकार के लिए भी ऐसा कर सकते हैं।

लेकिन अगर वे कक्षा / विधि के समान दायरे में हैं जहां आपको उनके बारे में जानना है, तो उन्हें जानने का कोई मतलब नहीं है, क्योंकि आपने पहले ही उन्हें स्वयं घोषित कर दिया है।


335
2017-12-21 21:18



निश्चित रूप से ऐसी स्थितियां हैं जहां यह उपयोगी है। उदाहरण के लिए विन्यास रहित ओआरएम ढांचे। - BalusC
.. जो फ़ील्ड नाम जानने के लिए सभी फ़ील्ड प्राप्त करने के लिए कक्षा # getDeclaredFields () का उपयोग कर सकते हैं। - BalusC
बलुस: यह मेरे लिए इंजेक्शन ढांचे की तरह लगता है .. वैसे भी इसका मतलब है जिसका मतलब है। - falstro
यह EnumSet के लिए भी काम है। धन्यवाद। - AHHP
@loolooyyyy टाइपलाइटरल का उपयोग करने के बजाय, मैं Guava से TypeToken का उपयोग करने की सलाह देता हूं। github.com/google/guava/wiki/ReflectionExplained - Babyburger


आप विधि पैरामीटर के लिए भी ऐसा ही कर सकते हैं:

Type[] types = method.getGenericParameterTypes();
//Now assuming that the first parameter to the method is of type List<Integer>
ParameterizedType pType = (ParameterizedType) types[0];
Class<?> clazz = (Class<?>) pType.getActualTypeArguments()[0];
System.out.println(clazz); //prints out java.lang.Integer

17
2018-02-27 14:11





संक्षिप्त उत्तर: नहीं।

यह शायद एक डुप्लिकेट है, अभी एक उपयुक्त नहीं मिल सकता है।

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

आप कक्षा के बारे में थोड़ा सा पता लगा सकते हैं, इसे किस प्रकार से parametrized किया जा सकता है, लेकिन आम तौर पर यह कुछ भी है जो "ऑब्जेक्ट", यानी कुछ भी फैलाता है। यदि आप एक प्रकार को परिभाषित करते हैं

class <A extends MyClass> AClass {....}

AClass.class में केवल यह तथ्य होगा कि पैरामीटर ए MyClass द्वारा बाध्य है, लेकिन उससे अधिक, बताने का कोई तरीका नहीं है।


16
2017-12-21 21:10



यह सच होगा यदि जेनेरिक की ठोस कक्षा निर्दिष्ट नहीं है; अपने उदाहरण में, वह स्पष्ट रूप से सूचियों को घोषित कर रहा है List<Integer> तथा List<String>; उन मामलों में, टाइप मिटाएं लागू नहीं होता है। - Haroldo_OK


यदि आपको सामान्य प्रकार का लौटा प्रकार प्राप्त करने की आवश्यकता है, तो मैंने इस दृष्टिकोण का उपयोग किया जब मुझे कक्षा में विधियों को खोजने की आवश्यकता थी जो एक लौटा Collection और फिर अपने सामान्य प्रकारों तक पहुंचें:

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;

public class Test {

    public List<String> test() {
        return null;
    }

    public static void main(String[] args) throws Exception {

        for (Method method : Test.class.getMethods()) {
            Class returnClass = method.getReturnType();
            if (Collection.class.isAssignableFrom(returnClass)) {
                Type returnType = method.getGenericReturnType();
                if (returnType instanceof ParameterizedType) {
                    ParameterizedType paramType = (ParameterizedType) returnType;
                    Type[] argTypes = paramType.getActualTypeArguments();
                    if (argTypes.length > 0) {
                        System.out.println("Generic type is " + argTypes[0]);
                    }
                }
            }
        }

    }

}

यह आउटपुट:

सामान्य प्रकार वर्ग java.lang.String है


8
2018-03-29 12:07





संग्रह का सामान्य प्रकार केवल तभी मायने रखता है जब वास्तव में इसमें वस्तुएं हों, है ना? तो बस इतना करना आसान नहीं है:

Collection<?> myCollection = getUnknownCollectionFromSomewhere();
Class genericClass = null;
Iterator it = myCollection.iterator();
if (it.hasNext()){
    genericClass = it.next().getClass();
}
if (genericClass != null) { //do whatever we needed to know the type for

रनटाइम में सामान्य प्रकार जैसी कोई चीज़ नहीं है, लेकिन रनटाइम पर अंदर की वस्तुओं को घोषित जेनेरिक के समान प्रकार की गारंटी दी जाती है, इसलिए प्रक्रिया करने से पहले आइटम की कक्षा का परीक्षण करना पर्याप्त आसान है।


7
2018-02-06 06:06



और यदि आपके पास खाली संग्रह है? या यदि आपके पास एक संग्रह <ऑब्जेक्ट> एक इंटीजर और स्ट्रिंग के साथ है? - Falci
कक्षा के लिए अनुबंध के लिए केवल अंतिम वस्तुओं की सूचियों की आवश्यकता हो सकती है, और यदि सूची शून्य, खाली है, या सूची प्रकार अमान्य है तो विधि कॉल शून्य हो जाएगी। - ggb667
खाली संग्रह के मामले में, रनटाइम पर किसी भी सूची प्रकार को असाइन करना सुरक्षित है, क्योंकि इसमें कुछ भी नहीं है, और टाइप एरर होने के बाद, सूची एक सूची है एक सूची है। इस वजह से, मैं किसी भी उदाहरण के बारे में सोचने में असमर्थ हूं जहां खाली संग्रह का प्रकार मायने रखता है। - Steve K
वैसे यह "मामला हो सकता है" अगर आप किसी थ्रेड में संदर्भ पर थे और एक बार उत्पादक उपभोक्ता परिदृश्य में ऑब्जेक्ट्स दिखने लगते थे तो इसे संसाधित किया जाता था। अगर सूची में गलत ऑब्जेक्ट प्रकार होते हैं तो बुरी चीजें हो सकती हैं। मुझे लगता है कि इसे रोकने से पहले आपको सूची में कम से कम एक आइटम होने तक सोना जारी रखना होगा। - ggb667
यदि आपके पास उस तरह का निर्माता / उपभोक्ता परिदृश्य है, तो सूची के लिए योजना बनाने के लिए एक निश्चित सामान्य प्रकार के लिए योजना बनाये बिना यह सुनिश्चित किए बिना कि सूची में क्या रखा जा सकता है, वैसे भी खराब डिजाइन है। इसके बजाय आप इसे एक संग्रह के रूप में घोषित करेंगे Objectएस और जब आप उन्हें सूची से बाहर खींचते हैं, तो आप उन्हें अपने प्रकार के आधार पर उचित हैंडलर को दे देंगे। - Steve K


स्टीव के जवाब पर विस्तार:

/** 
* Performs a forced cast.  
* Returns null if the collection type does not match the items in the list.
* @param data The list to cast.
* @param listType The type of list to cast to.
*/
static <T> List<T> castListSafe(List<?> data, Class<T> listType){
    List<T> retval = null;
    //This test could be skipped if you trust the callers, but it wouldn't be safe then.
    if(data!=null && !data.isEmpty() && listType.isInstance(data.iterator().next().getClass())) {
        @SuppressWarnings("unchecked")//It's OK, we know List<T> contains the expected type.
        List<T> foo = (List<T>)data;
        return retval;
    }
    return retval;
}
Usage:

protected WhateverClass add(List<?> data) {//For fluant useage
    if(data==null) || data.isEmpty(){
       throw new IllegalArgumentException("add() " + data==null?"null":"empty" 
       + " collection");
    }
    Class<?> colType = data.iterator().next().getClass();//Something
    aMethod(castListSafe(data, colType));
}

aMethod(List<Foo> foo){
   for(Foo foo: List){
      System.out.println(Foo);
   }
}

aMethod(List<Bar> bar){
   for(Bar bar: List){
      System.out.println(Bar);
   }
}

6
2017-09-13 17:44



स्टीव के जवाब के समान ही समस्याएं: क्या सूची खाली है तो क्या होगा? क्या होगा यदि सूची <ऑब्जेक्ट> प्रकार है जिसमें स्ट्रिंग और एक इंटीजर है? आपका कोड का अर्थ है कि यदि आप सूची में पहला आइटम स्ट्रिंग है, तो आप केवल स्ट्रिंग्स जोड़ सकते हैं, और कोई भी integeres नहीं है ... - subrunner
यदि सूची खाली है तो आप भाग्य से बाहर हैं। यदि सूची ऑब्जेक्ट है और इसमें वास्तव में एक ऑब्जेक्ट होता है जो आप ठीक हैं, लेकिन यदि इसमें चीजों का मिश्रण है तो आप भाग्य से बाहर हैं। मिटा का मतलब है कि यह उतना अच्छा है जितना आप कर सकते हैं। मेरी राय में त्रुटियों की कमी है। इसकी चिंताओं को एक बार गंभीर चिंता के दिलचस्प और वांछित उपयोग मामलों को संबोधित करने के लिए खराब समझ में आता है और अपर्याप्त होता है। कुछ भी कक्षा बनाने से रोकता है जिसे पूछा जा सकता है कि यह किस प्रकार लेता है, लेकिन यह नहीं है कि वर्गों में निर्मित कैसे काम करता है। संग्रहों को पता होना चाहिए कि वे क्या हैं, लेकिन हां ... - ggb667


जेनेरिक प्रकार के एक फ़ील्ड को खोजने के लिए:

((Class)((ParameterizedType)field.getGenericType()).getActualTypeArguments()[0]).getSimpleName()

5
2017-11-18 14:42





रनटाइम पर, नहीं, आप नहीं कर सकते।

हालांकि प्रतिबिंब के प्रकार पैरामीटर के माध्यम से कर रहे हैं सुलभ। प्रयत्न

for(Field field : this.getDeclaredFields()) {
    System.out.println(field.getGenericType())
}

प्रक्रिया getGenericType() एक प्रकार वस्तु देता है। इस मामले में, यह एक उदाहरण होगा ParametrizedType, जो बदले में तरीकों है getRawType() (जिसमें शामिल होगा List.class, इस मामले में) और getActualTypeArguments(), जो एक सरणी लौटाएगा (इस मामले में, लंबाई एक, जिसमें या तो शामिल है String.class या Integer.class)।


4
2017-12-21 21:19



और क्या यह कक्षा के क्षेत्रों के बजाय विधि द्वारा प्राप्त पैरामीटर के लिए काम करता है ??? - opensas


आम तौर पर असंभव, क्योंकि List<String> तथा List<Integer> एक ही रनटाइम कक्षा साझा करें।

आप सूची धारण करने वाले फ़ील्ड के घोषित प्रकार पर प्रतिबिंबित करने में सक्षम हो सकते हैं, हालांकि (यदि घोषित प्रकार स्वयं एक प्रकार पैरामीटर का संदर्भ नहीं देता है जिसका मूल्य आप नहीं जानते हैं)।


3
2017-12-21 21:15





जैसा कि अन्य ने कहा है, एकमात्र सही उत्तर नहीं है, प्रकार मिटा दिया गया है।

यदि सूची में तत्वों की एक गैर-शून्य संख्या है, तो आप पहले तत्व के प्रकार की जांच कर सकते हैं (उदाहरण के लिए, इसकी getClass विधि का उपयोग कर)। यह आपको सामान्य प्रकार की सूची नहीं बताएगा, लेकिन यह मानना ​​उचित होगा कि सामान्य प्रकार सूची में प्रकारों के कुछ सुपरक्लास थे।

मैं दृष्टिकोण की वकालत नहीं करता, लेकिन एक बाध्य में यह उपयोगी हो सकता है।


2
2017-12-21 21:15



यह सच होगा यदि जेनेरिक की ठोस कक्षा निर्दिष्ट नहीं है; अपने उदाहरण में, वह स्पष्ट रूप से सूचियों को घोषित कर रहा है List<Integer> तथा List<String>; उन मामलों में, टाइप मिटाएं लागू नहीं होता है। - Haroldo_OK