सामान्य प्रकार के java.util.ist प्राप्त करें


262

मेरे पास है;

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

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


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

4
राइट - रनटाइम डिटेक्शन की अनुमति देने के एकमात्र तरीके के बारे में उप-क्लासिंग द्वारा है: आप वास्तव में सामान्य प्रकार का विस्तार कर सकते हैं और फिर प्रतिबिंब खोज प्रकार घोषणा का उपयोग कर सकते हैं जो उपप्रकार का उपयोग करता है। यह थोड़ा प्रतिबिंब है, लेकिन संभव है। दुर्भाग्य से यह लागू करने का कोई आसान तरीका नहीं है कि किसी को सामान्य उप-वर्ग का उपयोग करना चाहिए।
स्टैक्मैन मैन 10'10

1
निश्चित रूप से स्ट्रिंगलिस्ट में तार और पूर्णांक शामिल होते हैं पूर्णांक? इसे और अधिक जटिल क्यों बनाते हैं?
बेन थर्ले

जवाबों:


408

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

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.
    }
}

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

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


17
निश्चित रूप से ऐसे हालात हैं जहां यह उपयोगी है। उदाहरण के लिए विन्यास रहित ORM चौखटे।
बालुस

3
.. जो क्षेत्र का नाम जानने की आवश्यकता के बिना सभी क्षेत्रों को प्राप्त करने के लिए कक्षा # getDeclaredFields () का उपयोग कर सकते हैं।
बालुस

1
BalusC: यह मेरे लिए एक इंजेक्शन ढाँचे की तरह लगता है .. इस तरह का उपयोग मैं वैसे भी था।
फालस्ट्रो

1
@loolooyyyy टाइप करने के बजाय टाइप लिटरल का उपयोग करें, मैं अमरूद से टाइपटॉकन का उपयोग करने की सलाह देता हूं। github.com/google/guava/wiki/ReflectionExplained
बेबीबर्गर

1
@ यदि आपका चर (वर्ग प्रकार) के <?>बजाय (वाइल्डकार्ड प्रकार) का उपयोग कर रहा है <Class>। अपने <?>द्वारा <Class>या जो भी बदलें <E>
बालूसक

19

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

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

Method.getGenericParameterTypes () में; विधि क्या है?
नव रवि

18

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

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

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

आप वर्ग के बारे में थोड़ा पता लगा सकते हैं कि यह किस प्रकार का हो सकता है। यदि आप एक प्रकार को परिभाषित करते हैं

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

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


1
यह सच होगा यदि सामान्य वर्ग का ठोस वर्ग निर्दिष्ट नहीं है; अपने उदाहरण में, वह स्पष्ट रूप से सूचियों की घोषणा कर रहा है List<Integer>और List<String>; उन मामलों में, टाइप इरैसेचर लागू नहीं होता है।
हैरोल्डो_कॉ

13

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

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

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

एक और चीज जो आप कर सकते हैं, वह है सूची को ऐसे सदस्यों को प्राप्त करने की प्रक्रिया, जो सही प्रकार के हों, दूसरों की अनदेखी करना (या उन्हें अलग तरह से संसाधित करना)।

Map<Class<?>, List<Object>> classObjectMap = myCollection.stream()
    .filter(Objects::nonNull)
    .collect(Collectors.groupingBy(Object::getClass));

// Process the list of the correct class, and/or handle objects of incorrect
// class (throw exceptions, etc). You may need to group subclasses by
// filtering the keys. For instance:

List<Number> numbers = classObjectMap.entrySet().stream()
        .filter(e->Number.class.isAssignableFrom(e.getKey()))
        .flatMap(e->e.getValue().stream())
        .map(Number.class::cast)
        .collect(Collectors.toList());

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

यदि आप अन्य वर्गों की वस्तुओं को पूरी तरह से अनदेखा करना चाहते हैं, तो यह बहुत सरल हो जाता है:

List<Number> numbers = myCollection.stream()
    .filter(Number.class::isInstance)
    .map(Number.class::cast)
    .collect(Collectors.toList());

आप यह सुनिश्चित करने के लिए एक उपयोगिता विधि भी बना सकते हैं कि किसी सूची में एक विशिष्ट वर्ग से मेल खाते केवल वे ही आइटम हों:

public <V> List<V> getTypeSafeItemList(Collection<Object> input, Class<V> cls) {
    return input.stream()
            .filter(cls::isInstance)
            .map(cls::cast)
            .collect(Collectors.toList());
}

5
और अगर आपके पास एक खाली संग्रह है? या अगर आपके पास एक संग्रह और एक स्ट्रिंग के साथ एक वस्तु <ऑब्जेक्ट> है?
फाल्सी

वर्ग के लिए अनुबंध के लिए केवल अंतिम वस्तुओं की सूची की आवश्यकता हो सकती है, और यह कि विधि कॉल अशक्त होगी यदि सूची शून्य, रिक्त है, या यदि सूची प्रकार अमान्य है।
ggb667

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

यदि आप एक सूत्र में दर्शाते हैं और एक निर्माता उपभोक्ता परिदृश्य में प्रदर्शित होने के बाद इसे संसाधित करते हैं, तो यह "बात" कर सकता है। यदि सूची में गलत वस्तु प्रकार है तो बुरी चीजें हो सकती हैं। मुझे लगता है कि रोकने के लिए आपको सोने से प्रसंस्करण को रोकना होगा जब तक कि सूची में कम से कम एक आइटम जारी रखने से पहले नहीं था।
ggb667

यदि आपके पास उस प्रकार का निर्माता / उपभोक्ता परिदृश्य है, तो सूची के लिए योजना बनाने के लिए एक निश्चित सामान्य प्रकार के बिना यह सुनिश्चित करना कि सूची में क्या रखा जा सकता है, वैसे भी खराब डिजाइन है। इसके बजाय आप इसे Objectएस के संग्रह के रूप में घोषित करेंगे और जब आप उन्हें सूची से बाहर निकालेंगे, तो आप उन्हें उनके प्रकार के आधार पर एक उपयुक्त हैंडलर देंगे।
स्टीव के

11

एक क्षेत्र के सामान्य प्रकार खोजने के लिए:

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

10

यदि आपको लौटे हुए प्रकार के सामान्य प्रकार प्राप्त करने की आवश्यकता है, तो मैंने इस दृष्टिकोण का उपयोग तब किया जब मुझे एक वर्ग में विधियां खोजने की आवश्यकता हुई जो कि वापस आ गए 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 है


6

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

/** 
* 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<? super 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);
   }
}

स्टीव के जवाब के साथ भी वही समस्याएं: यदि सूची खाली है तो क्या होगा? क्या होगा यदि सूची प्रकार की है <वस्तु> जिसमें एक स्ट्रिंग और एक एंगर शामिल है? आपके कोड का मतलब है कि आप केवल अधिक स्ट्रिंग्स जोड़ सकते हैं यदि सूची में पहला आइटम एक स्ट्रिंग है, और कोई पूर्णांक नहीं है ...
सबरनर

यदि सूची खाली है तो आप भाग्य से बाहर हैं। यदि सूची वस्तु है और इसमें वास्तव में एक वस्तु शामिल है, तो आप ठीक हैं, लेकिन यदि इसमें कुछ चीजों का मिश्रण है, तो आप भाग्य से बाहर हैं। Erasure का मतलब है यह उतना ही अच्छा है जितना आप कर सकते हैं। मेरी राय में इरेज़र की कमी है। विशिष्ट चिंता के दिलचस्प और वांछित उपयोग मामलों को संबोधित करने के लिए इसके प्रभाव एक बार में खराब समझे और अपर्याप्त हैं। कुछ भी नहीं एक वर्ग बनाने से रोकता है कि यह पूछा जा सकता है कि यह किस प्रकार का है, लेकिन बस यह नहीं है कि कक्षाओं में कैसे काम किया जाता है। संग्रह में यह जानना चाहिए कि वे क्या हैं, लेकिन अफसोस ...
ggb667 15

4

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

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

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

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


2
और क्या यह एक वर्ग के क्षेत्रों के बजाय एक विधि द्वारा प्राप्त मापदंडों के लिए काम करता है ???
खुलता है

@opensas आप method.getGenericParameterTypes()विधि के मापदंडों के घोषित प्रकार प्राप्त करने के लिए उपयोग कर सकते हैं ।
रेडियोडफ

4

एक ही समस्या थी, लेकिन मैंने इसके बजाय Instof का उपयोग किया। क्या यह इस तरह से था:

List<Object> listCheck = (List<Object>)(Object) stringList;
    if (!listCheck.isEmpty()) {
       if (listCheck.get(0) instanceof String) {
           System.out.println("List type is String");
       }
       if (listCheck.get(0) instanceof Integer) {
           System.out.println("List type is Integer");
       }
    }
}

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


3

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

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


2

जैसा कि दूसरों ने कहा है, केवल सही उत्तर नहीं है, प्रकार मिटा दिया गया है।

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

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


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

2
import org.junit.Assert;
import org.junit.Test;

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

public class GenericTypeOfCollectionTest {
    public class FormBean {
    }

    public class MyClazz {
        private List<FormBean> list = new ArrayList<FormBean>();
    }

    @Test
    public void testName() throws Exception {
        Field[] fields = MyClazz.class.getFields();
        for (Field field : fields) {
            //1. Check if field is of Collection Type
            if (Collection.class.isAssignableFrom(field.getType())) {
                //2. Get Generic type of your field
                Class fieldGenericType = getFieldGenericType(field);
                //3. Compare with <FromBean>
                Assert.assertTrue("List<FormBean>",
                  FormBean.class.isAssignableFrom(fieldGenericType));
            }
        }
    }

    //Returns generic type of any field
    public Class getFieldGenericType(Field field) {
        if (ParameterizedType.class.isAssignableFrom(field.getGenericType().getClass())) {
            ParameterizedType genericType =
             (ParameterizedType) field.getGenericType();
            return ((Class)
              (genericType.getActualTypeArguments()[0])).getSuperclass();
        }
        //Returns dummy Boolean Class to compare with ValueObject & FormBean
        return new Boolean(false).getClass();
    }
}

1
getFieldGenericTypefieldGenericTypeयह क्या नहीं पाया जा सकता है
19

0

प्रकार मिटा दिया जाता है इसलिए आप नहीं कर पाएंगे Http://en.wikipedia.org/wiki/Type_erasure और http://en.wikipedia.org/wiki/Generics_in_Java#Type_erasure देखें


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

0

Fieldइन्हें प्राप्त करने के लिए परावर्तन का उपयोग करें तब आप बस कर सकते हैं: field.genericTypeउस प्रकार को प्राप्त करने के लिए जिसमें जेनेरिक के बारे में भी जानकारी हो।


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