जावा में सामान्य प्रकार का उदाहरण बनाएँ?


576

क्या जावा में एक सामान्य प्रकार का उदाहरण बनाना संभव है? मैं इस बात पर आधारित हूं कि मैंने जो देखा है उसका उत्तर है no( टाइप इरेज़र के कारण ), लेकिन मुझे दिलचस्पी होगी कि अगर कोई मुझे देख सकता है तो मैं उसे देख सकता हूं:

class SomeContainer<E>
{
    E createContents()
    {
        return what???
    }
}

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

मैं इसे थोड़ी देर के लिए खुला छोड़ दूंगा कि क्या कोई भी नाटकीय रूप से इयान रॉबर्टसन के आर्टिमा आर्टिकल से अलग आता है ।


2
बस Android डिवाइस पर प्रदर्शन का परीक्षण किया। 10000 ऑपरेशंस और: 8-9 एमएस नए लोअरक्लास (), 9-11 एमएस लेता है, फैक्ट्री <someClass> .createInstance () और 64-71 एमएस सबसे छोटा रिफ्लेक्शन लेता है: SomeClass z = SomeClass.class.newInstance ()। और सभी परीक्षण एकल प्रयास-कैच ब्लॉक में थे। परावर्तन newInstance () 4 अलग-अलग अपवादों को फेंकता है, याद है? इसलिए मैंने फ़ैक्टरी पैटर्न का उपयोग करने का निर्णय लिया
दीपसंक


4
Java 8 के साथ, आप अब एक कंस्ट्रक्टर संदर्भ या एक लैम्ब्डा पास कर सकते हैं जो इस समस्या को काम करने के लिए बहुत तुच्छ बनाता है। देखें नीचे मेरा उत्तर जानकारी के लिए।
डैनियल प्रेडेन

मुझे लगता है कि इस तरह के कोड को लिखना बुरा है, नीचे की समस्या को हल करने के अधिक सुरुचिपूर्ण और पठनीय तरीके हैं।
क्रिज़ीस्तोफ़ सिचोकी

1
@DavidCitron "थोड़ी देर के लिए" उन्होंने कहा ... तब से ग्यारह साल हो गए ...
MC सम्राट

जवाबों:


332

तुम सही हो। तुम नहीं कर सकते new E()। लेकिन आप इसे बदल सकते हैं

private static class SomeContainer<E> {
    E createContents(Class<E> clazz) {
        return clazz.newInstance();
    }
}

यह एक पीड़ा है। लेकिन यह काम करता है। इसे फैक्ट्री पैटर्न में लपेटने से यह थोड़ा अधिक सहनीय हो जाता है।


11
हाँ, मैंने उस समाधान को देखा, लेकिन यह केवल तभी काम करता है जब आपके पास पहले से ही उस प्रकार के क्लास ऑब्जेक्ट का संदर्भ हो जिसे आप तत्काल करना चाहते हैं।
डेविड सिट्रन

11
हाँ मुझे पता हे। यह अच्छा होगा यदि आप ईक्लास कर सकते हैं, लेकिन यह केवल आपको वस्तु देता है। मिटा के कारण। :)
जस्टिन रुड

6
इस समस्या के लिए यह सही तरीका है। यह आमतौर पर वह नहीं है जो आप चाहते हैं, लेकिन यह आपको मिलता है।
जोआचिम सॉउर

38
और आप createContents () विधि को कैसे कहते हैं?
एलेक्सिस डुफ्रेनॉय

8
यह अब ऐसा करने का एकमात्र तरीका नहीं है, अब एक बेहतर तरीका है कि आपको Class<?>अमरूद और टाइपकोन का उपयोग करके एक संदर्भ में पारित करने की आवश्यकता नहीं है , कोड और लिंक के लिए यह उत्तर देखें!

129

यदि यह मदद करता है, तो दुन्नो, लेकिन जब आप एक गुमनाम प्रकार सहित (उप-नाम सहित) उप-टाइप करते हैं, तो प्रकार की जानकारी प्रतिबिंब के माध्यम से उपलब्ध होती है। जैसे,

public abstract class Foo<E> {

  public E instance;  

  public Foo() throws Exception {
    instance = ((Class)((ParameterizedType)this.getClass().
       getGenericSuperclass()).getActualTypeArguments()[0]).newInstance();
    ...
  }

}

इसलिए, जब आप फू को उप-वर्ग करते हैं, तो आपको बार उदाहरण का उदाहरण मिलता है,

// notice that this in anonymous subclass of Foo
assert( new Foo<Bar>() {}.instance instanceof Bar );

लेकिन यह बहुत काम है, और केवल उपवर्गों के लिए काम करता है। हालांकि काम हो सकता है।


2
हां, यह विशेष रूप से अच्छा है अगर सामान्य वर्ग सार है, तो आप इसे कंक्रीट उपवर्गों में कर सकते हैं :)
पियरे हेनरी

यदि क्लास Fooअमूर्त नहीं है, तो यह विधि भी काम करती है। लेकिन यह केवल फू के अनाम उपवर्गों पर ही क्यों काम करता है? मान लीजिए कि हम Fooठोस बनाते हैं (हम बाहर निकल जाते हैं abstract), तो new Foo<Bar>();परिणाम में त्रुटि क्यों होगी , जबकि new Foo<Bar>(){};नहीं? (अपवाद: "वर्ग को पैरामीटरकृत नहीं किया जा सकता है")
टिम कूइपर

2
@TimKuipers <E>में class Foo<E>किसी विशेष प्रकार के लिए बाध्य नहीं है। आप असाधारण व्यवहार जब भी देखेंगे Eनहीं है स्थैतिक रूप में के रूप में, के लिए बाध्य: new Foo<Bar>(), new Foo<T>() {...}, या class Fizz <E> extends Foo<E>। पहला मामला सांख्यिकीय रूप से बाध्य नहीं है, इसे संकलन समय पर मिटा दिया जाता है। दूसरा मामला दूसरे प्रकार के चर (T) के स्थान पर प्रतिस्थापित करता है, Eलेकिन फिर भी अनबाउंड है। और आखिरी मामले में यह स्पष्ट होना चाहिए कि Eअभी भी अनबाउंड है।
विलियम प्राइस

4
प्रकार के पैरामीटर को सांख्यिकीय रूप से बाध्य करने का एक उदाहरण होगा class Fizz extends Foo<Bar>- इस मामले में, उपयोगकर्ताओं को Fizzकुछ ऐसा मिलता है जो एक है Foo<Bar>और कुछ भी नहीं हो सकता है लेकिन ए Foo<Bar>। तो इस मामले में, संकलक उस जानकारी को वर्ग मेटाडेटा में एन्कोड करने के लिए खुश है Fizzऔर इसे ParameterizedTypeप्रतिबिंब कोड के रूप में उपलब्ध करा सकता है । जब आप एक अनाम आंतरिक वर्ग बनाते हैं जैसे new Foo<Bar>() {...}यह वही काम कर रहा है, Fizzतो कंपाइलर के बजाय एक "अनाम" वर्ग नाम उत्पन्न करता है जिसे आप तब तक नहीं जान पाएंगे जब तक बाहरी वर्ग संकलित नहीं हो जाता।
विलियम प्राइस

4
यह ध्यान दिया जाना चाहिए कि यह काम नहीं करेगा यदि प्रकार तर्क भी एक ParameterizedType हैं। उदाहरण के लिए, Foo<Bar<Baz>>। आप एक ऐसा उदाहरण बना रहे हैं, ParameterizedTypeImplजिसे स्पष्ट रूप से नहीं बनाया जा सकता। इसलिए, यह जांचना एक अच्छा विचार है कि getActualTypeArguments()[0]क्या लौट रहा है ParameterizedType। यदि यह है, तो आप कच्चे प्रकार को प्राप्त करना चाहते हैं, और इसके बजाय एक उदाहरण बनाना चाहते हैं।
क्रश

106

जावा 8 में आप Supplierबहुत आसानी से इसे प्राप्त करने के लिए कार्यात्मक इंटरफ़ेस का उपयोग कर सकते हैं:

class SomeContainer<E> {
  private Supplier<E> supplier;

  SomeContainer(Supplier<E> supplier) {
    this.supplier = supplier;
  }

  E createContents() {
    return supplier.get();
  }
}

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

SomeContainer<String> stringContainer = new SomeContainer<>(String::new);

String::newउस रेखा पर वाक्य रचना एक रचनाकार संदर्भ है

यदि आपका निर्माता तर्क देता है तो आप इसके बजाय एक लंबोदर अभिव्यक्ति का उपयोग कर सकते हैं:

SomeContainer<BigInteger> bigIntegerContainer
    = new SomeContainer<>(() -> new BigInteger(1));

6
अच्छा था। यह प्रतिबिंब और अपवादों का इलाज करने से बचता है।
ब्रूनो लेइट

2
बहुत अच्छा। दुर्भाग्य से एंड्रॉइड उपयोगकर्ताओं के लिए, इसके लिए एपीआई स्तर 24 या उच्चतर की आवश्यकता होती है।
माइकल अपडेटाइक

1
कम उपद्रव और कार्यात्मक दृष्टिकोण के अनुसार यह मेरी राय में स्वीकार किया जाएगा
टॉमस

2
... और यह इस पुराने उत्तर से भिन्न नहीं है कि यह दिखाते हुए कि इसके पीछे का तकनीकी पैटर्न लैम्ब्डा एक्सप्रेशंस और विधि संदर्भों के लिए जावा के समर्थन से भी पुराना है, जबकि आप अपने कंपाइलर को अपग्रेड करने के बाद भी उनके साथ उस पुराने कोड का उपयोग कर सकते हैं ...
Holger

क्या सिर्फ लगाना संभव होगा SomeContainer stringContainer = new SomeContainer(String::new);?
एरोन फ्रेंके

87

हिरन को पास करने के लिए आपको किसी प्रकार के अमूर्त कारखाने की आवश्यकता होगी।

interface Factory<E> {
    E create();
}

class SomeContainer<E> {
    private final Factory<E> factory;
    SomeContainer(Factory<E> factory) {
        this.factory = factory;
    }
    E createContents() {
        return factory.create();
    }
}

..और कैसे Factory.create () दिखता है?
ओधाद

6
@ ओएचडीआर Factory<>एक इंटरफेस है और इसलिए इसमें कोई बॉडी नहीं है। मुद्दा यह है कि आपको उन तरीकों से हिरन को पार करने के लिए अप्रत्यक्ष की एक परत की आवश्यकता है जो एक उदाहरण के निर्माण के लिए आवश्यक कोड को "जानते" हैं। धातु विज्ञान के बजाय सामान्य कोड के साथ ऐसा करना बेहतर है Classया Constructorप्रतिबिंब के रूप में चोट की एक पूरी दुनिया लाती है।
टॉम हॉल्टिन -

2
अब दिन आप इस तरह की विधि संदर्भ अभिव्यक्ति के साथ एक कारखाना उदाहरण बना सकते हैं:SomeContainer<SomeElement> cont = new SomeContainer<>(SomeElement::new);
Lii

24
package org.foo.com;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * Basically the same answer as noah's.
 */
public class Home<E>
{

    @SuppressWarnings ("unchecked")
    public Class<E> getTypeParameterClass()
    {
        Type type = getClass().getGenericSuperclass();
        ParameterizedType paramType = (ParameterizedType) type;
        return (Class<E>) paramType.getActualTypeArguments()[0];
    }

    private static class StringHome extends Home<String>
    {
    }

    private static class StringBuilderHome extends Home<StringBuilder>
    {
    }

    private static class StringBufferHome extends Home<StringBuffer>
    {
    }   

    /**
     * This prints "String", "StringBuilder" and "StringBuffer"
     */
    public static void main(String[] args) throws InstantiationException, IllegalAccessException
    {
        Object object0 = new StringHome().getTypeParameterClass().newInstance();
        Object object1 = new StringBuilderHome().getTypeParameterClass().newInstance();
        Object object2 = new StringBufferHome().getTypeParameterClass().newInstance();
        System.out.println(object0.getClass().getSimpleName());
        System.out.println(object1.getClass().getSimpleName());
        System.out.println(object2.getClass().getSimpleName());
    }

}

3
यदि आप जेनेरिक में जेनेरिक प्रकार का उपयोग करते हैं तो इस कोड के अच्छे दृष्टिकोण से क्लासकैस्ट अपवाद हो सकता है। तब आप वास्तविक टाइप तर्क को वापस लेते हैं। आपको जांचना चाहिए कि यह भी ParamterizedType है और यदि ऐसा है तो उसका रॉ टाइप (या इससे बेहतर कुछ) लौटाएं। इसके साथ एक और मुद्दा यह है कि जब हम अधिक विस्तार करते हैं तो एक बार यह कोड क्लासकैस्ट एक्सोटेशन भी फेंक देगा।
डेमियन लेस्ज़्ज़स्की - वैश

3
Java.lang.ClassCastException: की वजह से sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl java.lang.Class में ढाला नहीं जा सकता है
Juan

@ DamianLeszczyński-Vash के साथ भी विफल हो जाएगा, जैसेclass GenericHome<T> extends Home<T>{}
Holger

22

यदि आपको जेनेरिक क्लास के अंदर एक प्रकार के तर्क की एक नई आवृत्ति की आवश्यकता है, तो अपने रचनाकारों को इसकी कक्षा की मांग करें ...

public final class Foo<T> {

    private Class<T> typeArgumentClass;

    public Foo(Class<T> typeArgumentClass) {

        this.typeArgumentClass = typeArgumentClass;
    }

    public void doSomethingThatRequiresNewT() throws Exception {

        T myNewT = typeArgumentClass.newInstance();
        ...
    }
}

उपयोग:

Foo<Bar> barFoo = new Foo<Bar>(Bar.class);
Foo<Etc> etcFoo = new Foo<Etc>(Etc.class);

पेशेवरों:

  • रॉबर्टसन के सुपर टाइप टोकन (एसटीटी) दृष्टिकोण की तुलना में बहुत सरल (और कम समस्याग्रस्त)।
  • STT दृष्टिकोण की तुलना में बहुत अधिक कुशल (जो नाश्ते के लिए आपका सेलफोन खाएगा)।

विपक्ष:

  • एक डिफ़ॉल्ट कंस्ट्रक्टर को कक्षा नहीं दे सकते (यही कारण है कि फू अंतिम है)। यदि आपको वास्तव में डिफॉल्ट कंस्ट्रक्टर की आवश्यकता है तो आप हमेशा एक सेटर विधि जोड़ सकते हैं, लेकिन बाद में आपको उसे कॉल करना याद रखना चाहिए।
  • रॉबर्टसन की आपत्ति ... एक काली भेड़ की तुलना में अधिक बार्स (हालांकि टाइप तर्क वर्ग को एक बार में निर्दिष्ट करना वास्तव में आपको नहीं मारेगा)। और रॉबर्टसन के दावों के विपरीत यह वैसे भी DRY प्रिंसिपल का उल्लंघन नहीं करता है क्योंकि कंपाइलर टाइप शुद्धता सुनिश्चित करेगा।
  • पूरी तरह से Foo<L>सबूत नहीं । शुरुआत के लिए ... newInstance()टाइप तर्क वर्ग के पास डिफ़ॉल्ट कंस्ट्रक्टर न होने पर एक वॉबलर फेंक देगा। यह वैसे भी सभी ज्ञात समाधानों पर लागू होता है।
  • एसटीटी दृष्टिकोण के कुल एनकैप्सुलेशन को कम करता है। हालांकि कोई बड़ी बात नहीं है (STT के अपमानजनक प्रदर्शन को देखते हुए)।

22

अब आप यह कर सकते हैं और इसे प्रतिबिंब कोड के एक गुच्छा की आवश्यकता नहीं है।

import com.google.common.reflect.TypeToken;

public class Q26289147
{
    public static void main(final String[] args) throws IllegalAccessException, InstantiationException
    {
        final StrawManParameterizedClass<String> smpc = new StrawManParameterizedClass<String>() {};
        final String string = (String) smpc.type.getRawType().newInstance();
        System.out.format("string = \"%s\"",string);
    }

    static abstract class StrawManParameterizedClass<T>
    {
        final TypeToken<T> type = new TypeToken<T>(getClass()) {};
    }
}

बेशक अगर आपको कंस्ट्रक्टर को कॉल करने की आवश्यकता है जो कुछ प्रतिबिंब की आवश्यकता होगी, लेकिन यह बहुत अच्छी तरह से प्रलेखित है, यह चाल नहीं है!

यहाँ TypeToken के लिए JavaDoc है


6
यह समाधान मामलों के सीमित सेट के लिए काम करता है, जैसे प्रतिबिंब के साथ @ नूह का उत्तर। मैंने आज उन सभी की कोशिश की ... और मैं पैरामीटर श्रेणी के एक उदाहरण को पैरामीटर किए गए वर्ग (उदाहरण के लिए .newInstance ()) में सक्षम होने के लिए समाप्त कर दिया। "जेनरिक" की बहुत बड़ी कमी ... नया फू <बार> (बार.क्लास); ... कक्षा फू <टी> {निजी अंतिम कक्षा <टी> mTFactory; फू (कक्षा <T> tClass) {mTFactory = tClass; ...} टी उदाहरण = tFactory.newInstance (); }
यवोक

यह सभी मामलों में काम करता है, यहां तक ​​कि स्थिर कारखाने के तरीके जो जेनेरिक पैरामीटर लेते हैं

13

एक अधिक कार्यात्मक दृष्टिकोण के बारे में सोचें: कुछ ई से कुछ भी बनाने के बजाय (जो स्पष्ट रूप से एक कोड गंध है), एक फ़ंक्शन पास करें जो जानता है कि कैसे एक बनाएं, अर्थात

E createContents(Callable<E> makeone) {
     return makeone.call(); // most simple case clearly not that useful
}

6
तकनीकी रूप से आप एक फंक्शन नहीं पास कर रहे हैं, आप एक फंक्शन ऑब्जेक्ट (जिसे फ़नकार के रूप में भी जाना जाता है ) पास कर रहे हैं।
लोर्ने लालटेते

3
या शायद इसके बजाय पकड़ने के Exceptionउपयोग से उबरने के लिए Supplier<E>
मार्टिन डी

10

से जावा ट्यूटोरियल - जेनेरिक्स पर प्रतिबंध :

टाइप पैरामीटर के उदाहरण नहीं बना सकते

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

public static <E> void append(List<E> list) {
    E elem = new E();  // compile-time error
    list.add(elem);
}

वर्कअराउंड के रूप में, आप प्रतिबिंब के माध्यम से एक प्रकार के पैरामीटर का एक ऑब्जेक्ट बना सकते हैं:

public static <E> void append(List<E> list, Class<E> cls) throws Exception {
    E elem = cls.newInstance();   // OK
    list.add(elem);
}

आप निम्नानुसार अपेंडेंट विधि लागू कर सकते हैं:

List<String> ls = new ArrayList<>();
append(ls, String.class);

6

यहाँ एक विकल्प है जो मैं लेकर आया हूँ, इससे मदद मिल सकती है:

public static class Container<E> {
    private Class<E> clazz;

    public Container(Class<E> clazz) {
        this.clazz = clazz;
    }

    public E createContents() throws Exception {
        return clazz.newInstance();
    }
}

संपादित करें: वैकल्पिक रूप से आप इस कंस्ट्रक्टर का उपयोग कर सकते हैं (लेकिन इसके लिए ई की आवश्यकता है):

@SuppressWarnings("unchecked")
public Container(E instance) {
    this.clazz = (Class<E>) instance.getClass();
}

हाँ, यह जेनरिक के बिना भी समान काम करता है - जेनरिक के साथ इस कंटेनर की तात्कालिकता कुछ हद तक बेमानी हो जाती है (आपको यह निर्दिष्ट करना होगा कि "ई" दो बार क्या है)।
डेविड सिट्रन

ठीक है, यह तब होता है जब आप जावा और जेनेरिक का उपयोग करते हैं ... वे सुंदर नहीं होते हैं, और गंभीर सीमाएं होती हैं ...
माइक स्टोन

6

अगर आप इंस्टेंटेशन के दौरान दो बार क्लास का नाम नहीं लिखना चाहते हैं जैसे:

new SomeContainer<SomeType>(SomeType.class);

आप कारखाने विधि का उपयोग कर सकते हैं:

<E> SomeContainer<E> createContainer(Class<E> class); 

जैसे की:

public class Container<E> {

    public static <E> Container<E> create(Class<E> c) {
        return new Container<E>(c);
    }

    Class<E> c;

    public Container(Class<E> c) {
        super();
        this.c = c;
    }

    public E createInstance()
            throws InstantiationException,
            IllegalAccessException {
        return c.newInstance();
    }

}

6

जावा दुर्भाग्यपूर्ण रूप से आपको क्या करने की अनुमति नहीं देता है। आधिकारिक समाधान देखें :

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

public static <E> void append(List<E> list) {
    E elem = new E();  // compile-time error
    list.add(elem);
}

वर्कअराउंड के रूप में, आप प्रतिबिंब के माध्यम से एक प्रकार के पैरामीटर का एक ऑब्जेक्ट बना सकते हैं:

public static <E> void append(List<E> list, Class<E> cls) throws Exception {
    E elem = cls.newInstance();   // OK
    list.add(elem);
}

आप निम्नानुसार अपेंडेंट विधि लागू कर सकते हैं:

List<String> ls = new ArrayList<>();
append(ls, String.class);

क्या आप मुझे बता सकते हैं कि जब आप ऐसा कर रहे हैं तो आप क्यों निराश हो रहे हैं? मैं यह नहीं देखता कि आधिकारिक समाधान खराब समाधान क्यों है। धन्यवाद।
Neepsnikeep

मुझे लगता है कि आपको वोट कम मिलते हैं, क्योंकि आपका जवाब अनिवार्य रूप से जस्टिन रुड का है: stackoverflow.com/a/75254/103412
Torsten

5

जब आप ई के साथ संकलित समय पर काम कर रहे हों तो आप वास्तविक जेनेरिक प्रकार "ई" की परवाह नहीं करते हैं (या तो आप प्रतिबिंब का उपयोग करते हैं या जेनेरिक प्रकार के आधार वर्ग के साथ काम करते हैं) तो उपवर्ग को ई का उदाहरण दें।

Abstract class SomeContainer<E>
{

    abstract protected  E createContents();
    public doWork(){
        E obj = createContents();
        // Do the work with E 

     }
}


BlackContainer extends SomeContainer<Black>{
    Black createContents() {
        return new  Black();
    }
}

4

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

Class.forName(String).getConstructor(arguments types).newInstance(arguments)

लेकिन आपको पैकेज सहित सटीक वर्ग नाम की आपूर्ति करने की आवश्यकता है, जैसे। java.io.FileInputStream। मैंने इसका इस्तेमाल गणित के भाव पार्सर बनाने के लिए किया।


15
और आपको रनटाइम के दौरान जेनेरिक प्रकार का सटीक वर्ग नाम कैसे मिलता है?
डेविड सिट्रॉन

आपको उस वर्ग के उदाहरण का उपयोग करके इसे सहेजना होगा। संभव है, हालांकि संभव नहीं है। यदि आपके जेनेरिक में E (या T या जो भी) का सदस्य है, तो यह बाइनरी नाम है foo.getClass().getName()। यह उदाहरण कहाँ से आता है? वर्तमान में मैं जिस प्रोजेक्ट पर काम कर रहा हूं, उसमें कंस्ट्रक्टर में से एक पास कर रहा हूं।
मार्क स्टॉपर

3

आशा है कि यह मदद करने के लिए बहुत देर नहीं हुई !!!

जावा टाइप-सेफ्टी है, केवल ऑब्जेक्ट उदाहरण बनाने में सक्षम है।

मेरे मामले में मैं createContentsविधि के लिए मापदंडों को पारित नहीं कर सकता । मेरा समाधान सभी बलो उत्तर के बजाय फैली हुई है।

private static class SomeContainer<E extends Object> {
    E e;
    E createContents() throws Exception{
        return (E) e.getClass().getDeclaredConstructor().newInstance();
    }
}

यह मेरा उदाहरण मामला है जो मैं मापदंडों को पारित नहीं कर सकता।

public class SomeContainer<E extends Object> {
    E object;

    void resetObject throws Exception{
        object = (E) object.getClass().getDeclaredConstructor().newInstance();
    }
}

यदि आप किसी भी प्रकार के ऑब्जेक्ट के साथ अपने सामान्य वर्ग का विस्तार करते हैं, तो प्रतिबिंब बनाने के लिए रन टाइम एरर का उपयोग करें। ऑब्जेक्ट के लिए अपने सामान्य प्रकार को विस्तारित करने के लिए समय त्रुटि को संकलित करने के लिए इस त्रुटि को परिवर्तित करें।


3

TypeToken<T>कक्षा का उपयोग करें :

public class MyClass<T> {
    public T doSomething() {
        return (T) new TypeToken<T>(){}.getRawType().newInstance();
    }
}

यदि आप GSON के बजाय अमरूद का उपयोग करते हैं, तो यह थोड़ा अलग है:(T) new TypeToken<T>(getClass()){}.getRawType().newInstance();
याकूब वैन

2

मुझे लगा कि मैं ऐसा कर सकता हूं, लेकिन काफी निराश: यह काम नहीं करता है, लेकिन मुझे लगता है कि यह अभी भी साझा करने के लायक है।

शायद कोई सही कर सकता है:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface SomeContainer<E> {
    E createContents();
}

public class Main {

    @SuppressWarnings("unchecked")
    public static <E> SomeContainer<E> createSomeContainer() {
        return (SomeContainer<E>) Proxy.newProxyInstance(Main.class.getClassLoader(),
                new Class[]{ SomeContainer.class }, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Class<?> returnType = method.getReturnType();
                return returnType.newInstance();
            }
        });
    }

    public static void main(String[] args) {
        SomeContainer<String> container = createSomeContainer();

    [*] System.out.println("String created: [" +container.createContents()+"]");

    }
}

यह उत्तपन करता है:

Exception in thread "main" java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.String
    at Main.main(Main.java:26)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

लाइन 26 के साथ एक है [*]

एकमात्र व्यवहार्य समाधान @JustinRudd द्वारा एक है


2

@ नूह के उत्तर का एक दोष।

बदलाव के कारण

a] अधिक सुरक्षित है यदि आपने आदेश को बदल दिया है तो 1 सामान्य प्रकार का उपयोग किया जाता है।

b] एक वर्ग सामान्य प्रकार के हस्ताक्षर समय-समय पर बदलते रहते हैं ताकि आप रनटाइम में अस्पष्टीकृत अपवादों से आश्चर्यचकित न हों।

मजबूत कोड

public abstract class Clazz<P extends Params, M extends Model> {

    protected M model;

    protected void createModel() {
    Type[] typeArguments = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments();
    for (Type type : typeArguments) {
        if ((type instanceof Class) && (Model.class.isAssignableFrom((Class) type))) {
            try {
                model = ((Class<M>) type).newInstance();
            } catch (InstantiationException | IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

या एक लाइनर का उपयोग करें

एक पंक्ति कोड

model = ((Class<M>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1]).newInstance();

2

आप क्या कर सकते हैं -

  1. पहले उस सामान्य वर्ग के चर को घोषित करें

    2. जब वह इसका निर्माण करता है और उस वस्तु को तुरंत बदल देता है

  2. फिर आप जहां चाहें इसका इस्तेमाल करें

उदाहरण-

1

private Class<E> entity;

2

public xyzservice(Class<E> entity) {
        this.entity = entity;
    }



public E getEntity(Class<E> entity) throws InstantiationException, IllegalAccessException {
        return entity.newInstance();
    }

3।

ई ई = getEntity (इकाई);


0

जैसा कि आपने कहा, आप वास्तव में इसे प्रकार से मिटाए जाने के कारण नहीं कर सकते। आप इसे प्रतिबिंब का उपयोग करके सॉर्ट कर सकते हैं, लेकिन इसके लिए बहुत सारे कोड और बहुत सी त्रुटि से निपटने की आवश्यकता होती है।


तुम भी कैसे यह प्रतिबिंब का उपयोग करेंगे? मेरे द्वारा देखी जाने वाली एकमात्र विधि Class.getTypeParameters () है, लेकिन यह केवल घोषित प्रकारों को लौटाती है, रन-टाइम प्रकारों को नहीं।
डेविड सिट्रन

इसके बारे मेँ कह रहे हो आ? artima.com/weblogs/viewpost.jsp?thread=208860
डेविड

0

अगर तुम्हारा मतलब है new E() तो यह असंभव है। और मैं यह जोड़ना चाहूंगा कि यह हमेशा सही नहीं होता है - आप कैसे जानते हैं कि ई में सार्वजनिक नो-आर्ग कंस्ट्रक्टर है? लेकिन आप हमेशा किसी अन्य वर्ग को निर्माण सौंप सकते हैं जो जानता है कि कैसे एक उदाहरण बनाया जा सकता है - यह Class<E>इस तरह से या आपके कस्टम कोड हो सकता है

interface Factory<E>{
    E create();
}    

class IntegerFactory implements Factory<Integer>{    
  private static int i = 0; 
  Integer create() {        
    return i++;    
  }
}

0
return   (E)((Class)((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]).newInstance();

1
मूल प्रश्न में यह मेरे उदाहरण में काम नहीं करता है। सुपरक्लास SomeContainerबस के लिए है Object। इसलिए, this.getClass().getGenericSuperclass()एक Class(वर्ग java.lang.Object) देता है, नहीं ParameterizedType। यह वास्तव में पहले से ही सहकर्मी जवाब stackoverflow.com/questions/75175/… के साथ ही बताया गया था ।
डेविड सिट्रन

1
पूरी तरह से गलत: थ्रेड में अपवाद "मुख्य" java.lang.ClassCastException: java.lang.Class को java.lang.reflect.ParameterizedType पर नहीं डाला जा सकता
ऑबिन

0

आप इसे निम्नलिखित स्निपेट के साथ प्राप्त कर सकते हैं:

import java.lang.reflect.ParameterizedType;

public class SomeContainer<E> {
   E createContents() throws InstantiationException, IllegalAccessException {
      ParameterizedType genericSuperclass = (ParameterizedType)
         getClass().getGenericSuperclass();
      @SuppressWarnings("unchecked")
      Class<E> clazz = (Class<E>)
         genericSuperclass.getActualTypeArguments()[0];
      return clazz.newInstance();
   }
   public static void main( String[] args ) throws Throwable {
      SomeContainer< Long > scl = new SomeContainer<>();
      Long l = scl.createContents();
      System.out.println( l );
   }
}

4
पूरी तरह से गलत: थ्रेड में अपवाद "मुख्य" java.lang.ClassCastException: java.lang.Class को java.lang.reflect.ParameterizedType पर नहीं डाला जा सकता
ऑबिन

0

विभिन्न पुस्तकालय हैं जो Eआपके लिए तकनीकों का उपयोग करके हल कर सकते हैं जो कि रॉबर्टसन के लेख के समान है। यहां ई द्वारा दर्शाए गए कच्चे वर्ग को हल करने के लिए टाइपटूल का createContentsउपयोग करने वाले का एक कार्यान्वयन है :

E createContents() throws Exception {
  return TypeTools.resolveRawArgument(SomeContainer.class, getClass()).newInstance();
}

यह मानता है कि getClass () SomeContainer के एक उपवर्ग का समाधान करता है और अन्यथा विफल होगा क्योंकि E का वास्तविक मानदंड मान रनटाइम पर मिटा दिया गया होगा यदि यह उपवर्ग में कैप्चर नहीं किया गया है।


0

इसके द्वारा प्रस्तुत कच्चे वर्ग को हल करने के लिए TypeTools का createContentsउपयोग करने वाला यहां का कार्यान्वयन है :E

E createContents() throws Exception {
  return TypeTools.resolveRawArgument(SomeContainer.class, getClass()).newInstance();
}

यह दृष्टिकोण केवल तभी काम करता है, जब SomeContainerउप-वर्गित किया जाता है, इसलिए इसका वास्तविक मान Eएक प्रकार की परिभाषा में कैप्चर किया गया है:

class SomeStringContainer extends SomeContainer<String>

अन्यथा E का मान रनटाइम पर मिटाया जाता है और पुनर्प्राप्त करने योग्य नहीं है।


-1

आप एक क्लास लोडर और वर्ग के नाम के साथ, आखिरकार कुछ मापदंडों के साथ कर सकते हैं।

final ClassLoader classLoader = ...
final Class<?> aClass = classLoader.loadClass("java.lang.Integer");
final Constructor<?> constructor = aClass.getConstructor(int.class);
final Object o = constructor.newInstance(123);
System.out.println("o = " + o);

यह सिर्फ क्लास ऑब्जेक्ट पास करने से भी बदतर है
newacct

आपको कक्षा लोडर को स्पष्ट रूप से संदर्भित करने की आवश्यकता नहीं है।
स्टीफन रीच

-1

यहाँ एक बेहतर समाधान है, पर आधारित है ParameterizedType.getActualTypeArguments पहले से ही @ जनाब, @ लार्स बोहल और कुछ अन्य लोगों द्वारा उल्लिखित है।

कार्यान्वयन में पहला छोटा सुधार। फैक्ट्री को वापस नहीं आना चाहिए, लेकिन एक प्रकार। जैसे ही आप उदाहरण का उपयोग करके वापस आते हैं, Class.newInstance()आप उपयोग के दायरे को कम करते हैं। क्योंकि केवल नो-आर्ग्युमेंट्स कंस्ट्रक्टरों को इस तरह से आमंत्रित किया जा सकता है। एक बेहतर तरीका यह है कि आप किसी प्रकार को लौटाएं, और एक ग्राहक को चुनने की अनुमति दें, कि वह किस निर्माता को आमंत्रित करना चाहता है:

public class TypeReference<T> {
  public Class<T> type(){
    try {
      ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
      if (pt.getActualTypeArguments() == null || pt.getActualTypeArguments().length == 0){
        throw new IllegalStateException("Could not define type");
      }
      if (pt.getActualTypeArguments().length != 1){
        throw new IllegalStateException("More than one type has been found");
      }
      Type type = pt.getActualTypeArguments()[0];
      String typeAsString = type.getTypeName();
      return (Class<T>) Class.forName(typeAsString);

    } catch (Exception e){
      throw new IllegalStateException("Could not identify type", e);
    }

  }
}

यहाँ एक उपयोग उदाहरण है। @ लार्स बोहल ने विस्तार के माध्यम से पुन: जेनराइक प्राप्त करने का केवल एक संकेत तरीका दिखाया है। @ केवल के साथ एक उदाहरण बनाने के माध्यम से {}। यहाँ दोनों मामलों को प्रदर्शित करने के लिए परीक्षण दिए गए हैं:

import java.lang.reflect.Constructor;

public class TypeReferenceTest {

  private static final String NAME = "Peter";

  private static class Person{
    final String name;

    Person(String name) {
      this.name = name;
    }
  }

  @Test
  public void erased() {
    TypeReference<Person> p = new TypeReference<>();
    Assert.assertNotNull(p);
    try {
      p.type();
      Assert.fail();
    } catch (Exception e){
      Assert.assertEquals("Could not identify type", e.getMessage());
    }
  }

  @Test
  public void reified() throws Exception {
    TypeReference<Person> p = new TypeReference<Person>(){};
    Assert.assertNotNull(p);
    Assert.assertEquals(Person.class.getName(), p.type().getName());
    Constructor ctor = p.type().getDeclaredConstructor(NAME.getClass());
    Assert.assertNotNull(ctor);
    Person person = (Person) ctor.newInstance(NAME);
    Assert.assertEquals(NAME, person.name);
  }

  static class TypeReferencePerson extends TypeReference<Person>{}

  @Test
  public void reifiedExtenension() throws Exception {
    TypeReference<Person> p = new TypeReferencePerson();
    Assert.assertNotNull(p);
    Assert.assertEquals(Person.class.getName(), p.type().getName());
    Constructor ctor = p.type().getDeclaredConstructor(NAME.getClass());
    Assert.assertNotNull(ctor);
    Person person = (Person) ctor.newInstance(NAME);
    Assert.assertEquals(NAME, person.name);
  }
}

ध्यान दें: आप इस वर्ग को अमूर्त बनाकर उदाहरण के रूप में TypeReferenceउपयोग किए {}जाने पर हमेशा ग्राहकों को मजबूर कर सकते हैं public abstract class TypeReference<T>:। मैंने ऐसा नहीं किया है, केवल मिटाए गए मामले को दिखाने के लिए।

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