मेरे पास है;
List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();
क्या सूची का सामान्य प्रकार पुनर्प्राप्त करने का एक (आसान) तरीका है?
मेरे पास है;
List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();
क्या सूची का सामान्य प्रकार पुनर्प्राप्त करने का एक (आसान) तरीका है?
जवाबों:
यदि वे वास्तव में एक निश्चित वर्ग के क्षेत्र हैं, तो आप उन्हें प्रतिबिंब की थोड़ी मदद से प्राप्त कर सकते हैं:
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.
}
}
आप यह भी कर सकते हैं कि पैरामीटर प्रकार और वापसी प्रकार के तरीकों के लिए।
लेकिन अगर वे कक्षा / पद्धति के उसी दायरे के अंदर हैं, जहाँ आपको उनके बारे में जानना है, तो उन्हें जानने का कोई मतलब नहीं है, क्योंकि आप पहले ही उन्हें खुद घोषित कर चुके हैं।
<?>
बजाय (वाइल्डकार्ड प्रकार) का उपयोग कर रहा है <Class>
। अपने <?>
द्वारा <Class>
या जो भी बदलें <E>
।
आप विधि मापदंडों के लिए भी ऐसा कर सकते हैं:
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
संक्षिप्त उत्तर: नहीं।
यह शायद एक डुप्लिकेट है, अभी एक उपयुक्त एक नहीं मिल सकता है।
जावा कुछ प्रकार के इरेज़र नामक चीज़ का उपयोग करता है, जिसका अर्थ है रनटाइम के दौरान दोनों ऑब्जेक्ट्स बराबर हैं। कंपाइलर जानता है कि सूचियों में पूर्णांक या तार होते हैं, और जैसे कि एक सुरक्षित वातावरण बनाए रख सकता है। यह जानकारी रनटाइम के दौरान (ऑब्जेक्ट उदाहरण के आधार पर) खो जाती है, और सूची में केवल 'ऑब्जेक्ट' होते हैं।
आप वर्ग के बारे में थोड़ा पता लगा सकते हैं कि यह किस प्रकार का हो सकता है। यदि आप एक प्रकार को परिभाषित करते हैं
class <A extends MyClass> AClass {....}
AClass.class में केवल यह तथ्य होगा कि पैरामीटर A MyClass द्वारा बाउंड किया गया है, लेकिन इससे अधिक, यह बताने का कोई तरीका नहीं है।
List<Integer>
और List<String>
; उन मामलों में, टाइप इरैसेचर लागू नहीं होता है।
संग्रह का सामान्य प्रकार केवल तभी मायने रखता है जब उसमें वास्तव में वस्तुएं हों, सही? तो यह सिर्फ करना आसान नहीं है:
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());
}
Object
एस के संग्रह के रूप में घोषित करेंगे और जब आप उन्हें सूची से बाहर निकालेंगे, तो आप उन्हें उनके प्रकार के आधार पर एक उपयुक्त हैंडलर देंगे।
एक क्षेत्र के सामान्य प्रकार खोजने के लिए:
((Class)((ParameterizedType)field.getGenericType()).getActualTypeArguments()[0]).getSimpleName()
यदि आपको लौटे हुए प्रकार के सामान्य प्रकार प्राप्त करने की आवश्यकता है, तो मैंने इस दृष्टिकोण का उपयोग तब किया जब मुझे एक वर्ग में विधियां खोजने की आवश्यकता हुई जो कि वापस आ गए 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 है
स्टीव के के जवाब पर विस्तार:
/**
* 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);
}
}
रनटाइम पर, नहीं, आप नहीं कर सकते।
हालांकि प्रतिबिंब के माध्यम से प्रकार पैरामीटर हैं सुलभ। प्रयत्न
for(Field field : this.getDeclaredFields()) {
System.out.println(field.getGenericType())
}
विधि getGenericType()
प्रकार ऑब्जेक्ट देता है। इस मामले में, यह का एक उदाहरण हो जाएगा ParametrizedType
, जो बारी में तरीकों है getRawType()
(जो शामिल होंगे List.class
, इस मामले में) और getActualTypeArguments()
है, जो एक सरणी वापस आ जाएगी (इस मामले में, लंबाई एक की, जिसमें या तो String.class
या Integer.class
)।
method.getGenericParameterTypes()
विधि के मापदंडों के घोषित प्रकार प्राप्त करने के लिए उपयोग कर सकते हैं ।
एक ही समस्या थी, लेकिन मैंने इसके बजाय 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");
}
}
}
इसमें अनियंत्रित जातियों का उपयोग करना शामिल है इसलिए ऐसा तभी करें जब आपको पता हो कि यह एक सूची है, और यह किस प्रकार का हो सकता है।
आम तौर पर असंभव है, क्योंकि List<String>
और List<Integer>
एक ही रनटाइम क्लास साझा करते हैं।
आप सूची को पकड़े हुए क्षेत्र के घोषित प्रकार को प्रतिबिंबित करने में सक्षम हो सकते हैं, हालांकि (यदि घोषित प्रकार स्वयं एक प्रकार के पैरामीटर को संदर्भित नहीं करता है जिसका मूल्य आपको नहीं पता है)।
जैसा कि दूसरों ने कहा है, केवल सही उत्तर नहीं है, प्रकार मिटा दिया गया है।
यदि सूची में गैर-शून्य संख्या तत्व हैं, तो आप पहले तत्व के प्रकार (उदाहरण के लिए, यह getClass विधि का उपयोग करके) की जांच कर सकते हैं। यह आपको सूची का सामान्य प्रकार नहीं बताएगा, लेकिन यह मान लेना उचित होगा कि सामान्य प्रकार सूची में कुछ प्रकार के सुपरक्लास थे।
मैं दृष्टिकोण की वकालत नहीं करूंगा, लेकिन एक बंधन में यह उपयोगी हो सकता है।
List<Integer>
और List<String>
; उन मामलों में, टाइप इरैसेचर लागू नहीं होता है।
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();
}
}
getFieldGenericTypefieldGenericType
यह क्या नहीं पाया जा सकता है
प्रकार मिटा दिया जाता है इसलिए आप नहीं कर पाएंगे Http://en.wikipedia.org/wiki/Type_erasure और http://en.wikipedia.org/wiki/Generics_in_Java#Type_erasure देखें
List<Integer>
और List<String>
; उन मामलों में, टाइप इरैसेचर लागू नहीं होता है।
Field
इन्हें प्राप्त करने के लिए परावर्तन का उपयोग करें तब आप बस कर सकते हैं: field.genericType
उस प्रकार को प्राप्त करने के लिए जिसमें जेनेरिक के बारे में भी जानकारी हो।