জেনারিক ধরণের java.util.List পান


262

আমার আছে;

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

তালিকার জেনেরিক ধরণের পুনরুদ্ধার করার কি (সহজ) উপায় আছে?


প্রোগ্রামের ভিত্তিতে একটি তালিকা অবজেক্টটি পরীক্ষা করতে এবং এর জেনেরিক প্রকারটি দেখতে সক্ষম হতে। কোনও পদ্ধতি সংগ্রহের জেনেরিক ধরণের ভিত্তিতে অবজেক্টগুলি সন্নিবেশ করতে চাইতে পারে। সংকলন সময়ের পরিবর্তে রানটাইমে জেনেরিকগুলি প্রয়োগ করে এমন ভাষায় এটি সম্ভব This
স্টিভ কুও

4
ডান - রানটাইম সনাক্তকরণের অনুমতি দেওয়ার একমাত্র উপায়টি উপ-শ্রেণিবদ্ধকরণ: আপনি প্রকৃতপক্ষে জেনেরিক প্রকারটি প্রসারিত করতে পারেন এবং তারপরে প্রতিফলন সন্ধান করুন টাইপ ঘোষণার সাহায্যে সাব টাইপটি ব্যবহার করেছেন। এটি বেশ খানিকটা প্রতিবিম্ব, তবে সম্ভব। দুর্ভাগ্যক্রমে কার্যকর করার কোনও সহজ উপায় নেই যে একজনকে জেনেরিক উপ-শ্রেণি ব্যবহার করতে হবে।
স্টেক্সম্যান

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
বালুসসি: এটি আমার কাছে ইনজেকশন কাঠামোর মতো মনে হচ্ছে .. এটাই যেভাবেই হোক আমি ব্যবহারের ধরণের।
ফলস্ট্রো

1
@ ললুউইউইয় টাইপলিটালাল ব্যবহার না করে আমি গুয়ারা থেকে টাইপটোকেন ব্যবহার করার পরামর্শ দিই। github.com/google/guava/wiki/RiflectionExplained
বেবিবার্গ

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

মেথডে.জেটজেনেরিকপ্যারামিটারটাইপস (); পদ্ধতি কী?
নিও রবি

18

সংক্ষিপ্ত উত্তর: না।

এটি সম্ভবত একটি সদৃশ, এখনই উপযুক্ত খুঁজে পাচ্ছেন না।

জাভা টাইপ ইরেজর নামক কিছু ব্যবহার করে যার অর্থ রানটাইম উভয় বস্তু সমান। সংকলকটি জানে তালিকাগুলিতে পূর্ণসংখ্যা বা স্ট্রিং রয়েছে এবং যেমন কোনও ধরণের নিরাপদ পরিবেশ বজায় রাখতে পারে। রানটাইমের সময় এই তথ্যটি (কোনও বস্তুর উদাহরণের ভিত্তিতে) হারিয়ে গেছে এবং তালিকায় কেবল 'অবজেক্ট' রয়েছে।

আপনি ক্লাস সম্পর্কে কিছুটা জানতে পারেন, এটি কী ধরণের দ্বারা প্যারামিটারাইজড হতে পারে, তবে সাধারণত এটি এমন কিছু যা "অবজেক্ট", অর্থাৎ যেকোন কিছুতে প্রসারিত করে। যদি আপনি কোনও প্রকারটি সংজ্ঞায়িত করেন

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

AClass.class কেবলমাত্র প্যারামিটার A মাইক্লাস দ্বারা আবদ্ধ তা কেবল থাকবে, তবে এর চেয়ে বেশি বলার উপায় নেই।


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
এবং যদি আপনার খালি সংগ্রহ থাকে? বা আপনার যদি একটি পূর্ণসংখ্যার এবং একটি স্ট্রিং সহ <Object> সংগ্রহ রয়েছে?
ফালসি

শ্রেণীর জন্য চুক্তির জন্য কেবলমাত্র চূড়ান্ত অবজেক্টের তালিকা পাস হতে পারে এবং তালিকাটি বাতিল, খালি, বা তালিকার ধরণটি অবৈধ থাকলে পদ্ধতি কলটি বাতিল হয়ে যাবে।
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);
   }
}

স্টিভ কে এর উত্তরের মতো একই সমস্যা: তালিকাটি খালি থাকলে কী হবে? যদি তালিকাটি << বিষয়> টাইপের হয় তবে এতে একটি স্ট্রিং এবং একটি পূর্ণসংখ্যা রয়েছে? আপনার কোডটির অর্থ হ'ল তালিকার প্রথম আইটেমটি যদি স্ট্রিং হয় এবং তেমন কোনও পূর্ণসংখ্যা না থাকে তবে আপনি কেবল আরও স্ট্রিংস যুক্ত করতে পারবেন ...
সাব্রুনার

তালিকাটি খালি থাকলে আপনার ভাগ্যের বাইরে। যদি তালিকাটি অবজেক্ট হয় এবং এটিতে আসলে কোনও অবজেক্ট থাকে তবে আপনি ঠিক আছেন, তবে এতে যদি জিনিসগুলির মিশ্রণ থাকে তবে আপনার ভাগ্যের বাইরে। ক্ষয়ের অর্থ এটি আপনার পক্ষে যতটা সম্ভব তত ভাল। খালি আমার মতামত নেই। এটির প্রভাবগুলি একবারে দুর্বলভাবে বোঝা যায় এবং আদর্শ উদ্বেগের আকর্ষণীয় এবং পছন্দসই ব্যবহারের ক্ষেত্রে সমাধান করতে অপর্যাপ্ত হয়। কোন ধরণের শ্রেণি তৈরি করা বাধা দেয় যা এটি জিজ্ঞাসা করা যেতে পারে যে এটি কী ধরণের লাগে, তবে ক্লাসে অন্তর্নির্মিত কাজগুলি কেবল এটি নয়। সংগ্রহে তাদের কী রয়েছে তা জানা উচিত, তবে হায় ...
ggb667

4

রানটাইমের সময়, না, আপনি পারবেন না।

তবে প্রতিফলন মাধ্যমে টাইপ প্যারামিটার হয় প্রবেশযোগ্য। চেষ্টা

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

পদ্ধতিটি getGenericType()একটি টাইপ অবজেক্ট প্রদান করে। এক্ষেত্রে এটি একটি উদাহরণ হবে ParametrizedType, যার পরিবর্তে পদ্ধতি রয়েছে getRawType()(যার List.classমধ্যে রয়েছে এই পদ্ধতিতে অন্তর্ভুক্ত থাকবে ) এবং getActualTypeArguments(), যা একটি অ্যারে (এই ক্ষেত্রে, দৈর্ঘ্যের এক, উভয়ই থাকবে String.classবা Integer.class) প্রদান করবে।


2
এবং এটি কোনও শ্রেণীর ক্ষেত্রগুলির পরিবর্তে কোনও পদ্ধতি দ্বারা প্রাপ্ত পরামিতিগুলির জন্য কাজ করে ???
খুলবে

@ ওপেনসাস আপনি method.getGenericParameterTypes()কোনও পদ্ধতির পরামিতিগুলির ঘোষিত ধরণের পেতে ব্যবহার করতে পারেন ।
রেডিওডেফ 15'18

4

একই সমস্যা ছিল, তবে আমি পরিবর্তে উদাহরণ ব্যবহার করেছি। এটি এইভাবে করেছেন:

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এটি কী খুঁজে পাওয়া যায় না
শরিফ

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জেনেরিক সম্পর্কেও তথ্য রয়েছে এমন টাইপটি পেতে।


কিছু উদাহরণ কোড যুক্ত করার কথা বিবেচনা করুন, অন্যথায় এটি কোনও মন্তব্যের জন্য উপযুক্ত এবং উত্তর নয়
আলেক্সি কামেনস্কি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.