Java'da genel türün örneği oluşturulsun mu?


576

Java'da genel tipte bir örnek oluşturmak mümkün mü? Cevabın no( tip silme nedeniyle ) gördüklerime dayanarak düşünüyorum , ancak kimse eksik olduğum bir şey görebiliyorsa ilgilenirim:

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

DÜZENLEME: Süper Tip Jetonları sorunumu çözmek için kullanılabilir, ancak aşağıdaki cevaplardan bazılarının belirttiği gibi, çok sayıda yansıma tabanlı kod gerektirir.

Ian Robertson'un Artima Makalesinden önemli ölçüde farklı bir şey olup olmadığını görmek için bunu bir süre açık bırakacağım .


2
Sadece Android cihazda performansı test etti. 10000 işlem ve: 8-9 ms yeni SomeClass () alır, 9-11 ms Factory <SomeClass> .createInstance () alır ve 64-71 ms en kısa yansımayı alır: SomeClass z = SomeClass.class.newInstance (). Ve tüm testler tek bir try-catch bloğundaydı. Yansıma newInstance () 4 farklı istisna atar, hatırlıyor musun? Bu yüzden fabrika modelini kullanmaya karar verdim
Deepscorn


4
Java 8 ile, artık bir kurucu referansı veya bir lambda iletebilirsiniz, bu da bu sorunu geçici olarak çözmenizi sağlar. Ayrıntılar için aşağıdaki cevabıma bakın .
Daniel Pryden

Bu tür bir kod yazmak için kötü bir fikir olduğunu düşünüyorum, altında sorunu çözmek için daha zarif ve okunabilir yollardır.
Krzysztof Cichocki

1
@DavidCitron "bir süreliğine" dedi ... O zamandan beri on bir yıl geçti ...
MC İmparatoru

Yanıtlar:


332

Haklısın. Yapamazsın new E(). Ama bunu şu şekilde değiştirebilirsiniz:

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

Bu bir acı. Ama işe yarıyor. Fabrika desenine sarmak biraz daha tolere edilebilir.


11
Evet, bu çözümü gördüm, ancak yalnızca örneklemek istediğiniz türde bir Class nesnesine zaten başvuruyorsanız çalışır.
David Citron

11
Evet biliyorum. E.class yapabilseydiniz iyi olurdu ama bu sadece silme nedeniyle Object.class veriyor :)
Justin Rudd

6
Bu sorun için doğru yaklaşım budur. Genellikle istediğiniz şey değildir, ancak elde ettiğiniz şey budur.
Joachim Sauer

38
Ve createContents () yöntemini nasıl çağırırsınız?
Alexis Dufrenoy

8
Bu artık bunu yapmanın tek yolu değil, şimdi Class<?>Guava ve TypeToken kullanarak bir referans geçmesini gerektirmeyen daha iyi bir yol var, kod ve bağlantılar için bu cevaba bakın!

129

Bu yardımcı oluyorsa, ancak genel bir türü (anonim dahil) alt sınıfladığınızda, tür bilgisine yansıma yoluyla erişilebilir. Örneğin,

public abstract class Foo<E> {

  public E instance;  

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

}

Yani, Foo alt sınıfını aldığınızda, Bar örneğini alırsınız, örneğin,

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

Ama bu çok iş ve sadece alt sınıflar için çalışıyor. Yine de kullanışlı olabilir.


2
Evet, bu özellikle jenerik sınıf soyutsa güzel, bunu somut alt sınıflarda yapabilirsiniz :)
Pierre Henry

Sınıf Foosoyut değilse bu yöntem de işe yarar . Ama neden sadece Foo'nun anonim alt sınıflarında çalışıyor? Diyelim ki Foobeton yapıyoruz (dışarıda bırakıyoruz abstract), neden new Foo<Bar>();hataya neden olurken new Foo<Bar>(){};değil? (İstisna: "Sınıf ParameterizedType öğesine dönüştürülemez")
Tim Kuipers

2
@TimKuipers <E>in class Foo<E>, belirli bir türe bağlı değildir. Sen ne zaman istisnai davranışı görürsünüz Edeğildir statik olduğu gibi bağlı: new Foo<Bar>(), new Foo<T>() {...}veya class Fizz <E> extends Foo<E>. İlk durum statik olarak bağlı değildir , derleme zamanında silinir . İkinci durum, yerine başka bir tip değişkeni (T) yerine geçer, Eancak hala bağlanmamıştır. Ve son durumda E, hala bağlı olmadığı açık olmalıdır .
William Price

4
Tür parametresini statik olarak bağlamanın bir örneği class Fizz extends Foo<Bar>- bu durumda, kullanıcılar Fizza olan Foo<Bar>ve a olmayan bir şey elde eden kullanıcılar olur Foo<Bar>. Bu durumda, derleyici bu bilgiyi sınıf meta verilerine kodlamaktan Fizzve onu ParameterizedTypeyansıma koduna sunmaktan mutluluk duyar . Anonim bir iç sınıf oluşturduğunuzda new Foo<Bar>() {...}aynı şeyi yapıyor gibi , ancak Fizzderleyici dış sınıf derleninceye kadar bilmeyeceğiniz bir "anonim" sınıf adı oluşturur.
William Price

4
Tür bağımsız değişkenleri de bir ParameterizedType ise, bunun işe yaramayacağına dikkat edilmelidir. Örneğin Foo<Bar<Baz>>,. Açık bir şekilde oluşturulamayacak bir örneği ParameterizedTypeImploluşturacaksınız. Bu nedenle, getActualTypeArguments()[0]geri dönüp dönmediğini kontrol etmek iyi bir fikirdir ParameterizedType. Öyleyse, ham türü almak ve bunun yerine bir örnek oluşturmak istersiniz.
ezmek

106

Java 8'de bunu Supplierkolayca gerçekleştirmek için fonksiyonel arayüzü kullanabilirsiniz:

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

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

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

Bu sınıfı şöyle inşa edersiniz:

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

Bu String::newsatırdaki sözdizimi bir yapıcı başvurusudur .

Yapıcı argüman alıyorsa bunun yerine lambda ifadesini kullanabilirsiniz:

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

6
İyi bir. Yansımayı ve istisnaları tedavi etmek zorunda kalmaz.
Bruno Leite

2
Çok hoş. Maalesef Android kullanıcıları için bu, API düzey 24 veya üstü gerektirir.
Michael Updike

1
Daha az yaygara ve işlevsel yaklaşıma göre bu benim görüşüme göre cevap kabul edilecektir
Tomas

2
… Ve bunun arkasındaki teknik modelin Java'nın lambda ifadeleri ve yöntem referansları desteğinden bile daha eski olduğunu gösteren bu daha eski cevaptan farklı değilken, derleyicinizi yükselttikten sonra bu eski kodu bile kullanabilirsiniz ...
Holger

Sadece koymak mümkün SomeContainer stringContainer = new SomeContainer(String::new);mü?
Aaron Franke

87

Parayı geçmek için bir tür soyut fabrikaya ihtiyacınız olacak:

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();
    }
}

..ve Factory.create () nasıl görünüyor?
OhadR

6
@OhadR Factory<>bir arayüzdür ve beden yoktur. Buradaki nokta, paranın bir örneği oluşturmak için gerekli kodu "bilen" yöntemlere geçirmesi için bir dolaylar katmanına ihtiyacınız olmasıdır. Bunu metalinguistikten ziyade normal kodla yapmak Classveya Constructoryansıma tüm dünyayı incittiğinden daha iyidir .
Tom Hawtin - çakmak hattı

2
Şimdi günler böyle bir yöntem referans ifadesi ile bir fabrika örneği oluşturabilirsiniz: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
Genel bir tür kullanırsanız, bu kod tarafından iyi bir yaklaşım ClassCastException neden olabilir. Daha sonra actualType argümanını geri alırsınız. Bunun da ParamterizedType olup olmadığını kontrol etmeli ve öyleyse RawType (veya bundan daha iyi bir şey) döndürmelisiniz. Bu konuda başka bir sorun, bu kod da bir kez daha ClassCastExeption atar daha sonra genişletmek olmasıdır.
Damian Leszczyński - Vash

3
Nedeni: java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl, java.lang.Class'a aktarılamıyor
juan

@ DamianLeszczyński-Vash ile de başarısız olur, örneğinclass GenericHome<T> extends Home<T>{}
Holger

22

Genel bir sınıf içinde yeni bir argüman örneğine ihtiyacınız varsa, o zaman yapıcılarınızın sınıfını talep etmesini sağlayın ...

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();
        ...
    }
}

Kullanımı:

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

Artıları:

  • Robertson'ın Süper Tip Jetonu (STT) yaklaşımından çok daha basit (ve daha az problemli).
  • STT yaklaşımından çok daha verimli (kahvaltı için cep telefonunuzu yiyecektir).

Eksileri:

  • Class'u varsayılan bir kurucuya geçiremezsiniz (bu yüzden Foo sondur). Gerçekten varsayılan bir kurucuya ihtiyacınız varsa, her zaman bir ayarlayıcı yöntemi ekleyebilirsiniz, ancak daha sonra onu aramayı unutmayın.
  • Robertson'ın itirazı ... Kara koyunlardan daha fazla Bar (tip argüman sınıfını bir kez daha belirtmek seni tam olarak öldürmeyecek olsa da). Ve Robertson'ın iddialarının aksine, bu zaten KURU prensibini ihlal etmiyor çünkü derleyici tip doğruluğunu sağlayacaktır.
  • Tamamen Foo<L>kanıt değil . Yeni başlayanlar için...newInstance() tür argüman sınıfının varsayılan bir yapıcısı yoksa bir wobbler atar. Bu yine de bilinen tüm çözümler için geçerlidir.
  • STT yaklaşımının toplam kapsüllenmesinin olmaması. Büyük bir anlaşma olsa (STT çirkin performans yükü dikkate alınarak).

22

Bunu şimdi yapabilirsiniz ve bir grup yansıma kodu gerektirmez.

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()) {};
    }
}

Tabii ki, biraz yansıma gerektirecek kurucuyu çağırmanız gerekiyorsa, ancak bu çok iyi belgelenmiş, bu hile değil!

İşte TypeToken için JavaDoc .


6
Bu çözüm, tıpkı @ noah'ın yansıma ile verdiği cevap gibi, sınırlı sayıda dava için işe yarar. Hepsini bugün denedim ... Ve (.newInstance ()) çağırabilmeniz için parametre sınıfının bir örneğini parametreli sınıfa geçirmeyi bitirdim. "Generics" in çok büyük eksikliği ... yeni Foo <Bar> (Bar.class); ... class Foo <T> {özel final Sınıfı <T> mTFactory; Foo (Sınıf <T> tClass) {mTFactory = tClass; ...} T örneği = tFactory.newInstance (); }
yvolk

bu, her durumda, genel parametreleri alan statik fabrika yöntemleri bile çalışır

13

Daha işlevsel bir yaklaşım düşünün: Hiçbir şeyden (açıkça bir kod kokusu olan) bir E oluşturmak yerine, nasıl oluşturulacağını bilen bir işlev geçirin, yani

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

6
Teknik olarak, bir fonksiyon geçen değiliz Eğer geçiyoruz fonksiyon nesnesi (aynı zamanda olarak da bilinir funktor ).
Lorne Laliberte

3
Ya da bunun yerine yakalama Exceptionkullanımının üstesinden gelmek için Supplier<E>.
Martin D

10

Gönderen Java Eğitimi - Jenerik ilişkin kısıtlamalar :

Tür Parametrelerinin Örnekleri Oluşturulamıyor

Bir tür parametresinin örneğini oluşturamazsınız. Örneğin, aşağıdaki kod bir derleme zamanı hatasına neden olur:

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

Geçici bir çözüm olarak, yansıma yoluyla type parametresinin bir nesnesini oluşturabilirsiniz:

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

Ekleme yöntemini aşağıdaki gibi çağırabilirsiniz:

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

6

İşte geldiğim bir seçenek, yardımcı olabilir:

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();
    }
}

EDIT: Alternatif olarak bu yapıcı kullanabilirsiniz (ancak bir E örneği gerektirir):

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

Evet, bu jenerikler olmadan bile aynı şekilde çalışır - jeneriklerle bu kabın somutlaştırılması biraz gereksiz hale gelir ("E" nin iki kez ne olduğunu belirtmeniz gerekir).
David Citron

Java ve jenerikleri kullandığınızda olan şey bu ... hoş değiller ve ciddi sınırlamalar var ...
Mike Stone

6

Örnekleme sırasında aşağıdaki gibi iki kez sınıf adı yazmak istemiyorsanız:

new SomeContainer<SomeType>(SomeType.class);

Fabrika yöntemini kullanabilirsiniz:

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

Gibi:

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

Java maalesef ne yapmak istediğinize izin vermiyor. Resmi geçici çözüme bakın :

Bir tür parametresinin örneğini oluşturamazsınız. Örneğin, aşağıdaki kod bir derleme zamanı hatasına neden olur:

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

Geçici bir çözüm olarak, yansıma yoluyla type parametresinin bir nesnesini oluşturabilirsiniz:

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

Ekleme yöntemini aşağıdaki gibi çağırabilirsiniz:

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

Lütfen bunu yaparken neden aşağı indirdiğinizi söyler misiniz? Resmi geçici çözümün neden kötü bir çözüm olduğunu anlamıyorum. Teşekkürler.
Neepsnikeep

Sanırım oyları düşürüyorsunuz, çünkü cevabınız aslında Justin Rudd'unkiyle aynı: stackoverflow.com/a/75254/103412
Torsten

5

Derleme zamanında E ile çalışırken, gerçek "E" genel türünü (ya yansıma kullanırsınız veya genel türün temel sınıfıyla çalışırsınız) gerçekten önemsemezsiniz, bu nedenle alt sınıfın E örneğini vermesine izin verin.

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

Kullanabilirsiniz:

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

Ancak, paketler dahil olmak üzere tam sınıf adını sağlamanız gerekir, örn. java.io.FileInputStream. Bunu matematik ifadeleri ayrıştırıcısı oluşturmak için kullandım.


15
Ve çalışma zamanında genel türün tam sınıf adını nasıl alırsınız?
David Citron

Söz konusu sınıfın bir örneğini kullanarak kaydetmeniz gerekir. Çok uygun olsa da yapılabilir. Eğer jenerikinizin E tipi (veya T ya da her neyse) bir üyesi varsa, onun ikili ismini almak sadece foo.getClass().getName(). THAT örneği nereden geliyor? Şu anda üzerinde çalıştığım projede bir kurucuya geçiyorum.
Mark Storer

3

Umarım bu yardım etmek için çok geç değildir !!!

Java, tür güvenliğidir, yalnızca Object örnek oluşturabilir.

Benim durumumda parametreleri createContentsyönteme geçiremiyorum. Benim çözümüm tüm feryat cevaplar yerine uzatmalar kullanmaktır.

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

Parametreleri geçiremediğim örnek durumum bu.

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

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

Genel sınıfınızı hiçbir nesne türüyle genişletirseniz yansıma oluşturma çalışma zamanı hatası oluşturur. Genel türünüzü nesneye genişletmek için bu hatayı zaman hatasını derlemeye dönüştürün.


3

TypeToken<T>Sınıfı kullanın :

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

GSON yerine Guava kullanıyorsanız, biraz farklı:(T) new TypeToken<T>(getClass()){}.getRawType().newInstance();
Jacob van Lingen

2

Bunu yapabileceğimi düşündüm, ama oldukça hayal kırıklığına uğradı: işe yaramadı, ama yine de paylaşmaya değer olduğunu düşünüyorum.

Belki birileri düzeltebilir:

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()+"]");

    }
}

Ürettiği:

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 numaralı çizgi [*].

Tek uygun çözüm @JustinRudd tarafından verilen çözümdür.


2

@ Nuh'un cevabının geliştirilmesi.

Değişmek için sebep

a] Sırayı değiştirdiğinizde 1'den fazla genel tür kullanılırsa daha güvenlidir.

b] Sınıf genel tip imzası zaman zaman değişir, böylece çalışma süresindeki açıklanamayan istisnalar sizi şaşırtmaz.

Sağlam Kod

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);
            }
        }
    }
}

Veya tek astarı kullanın

Bir Satır Kodu

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

2

yapabileceğiniz şey -

  1. İlk olarak bu genel sınıfın değişkenini bildirin

    2.Ondan bir kurucu yap ve nesneyi başla

  2. Sonra kullanmak istediğiniz her yerde kullanın

misal-

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.

E e = getEntity (varlık);


0

Söylediğiniz gibi, tip silme nedeniyle gerçekten yapamazsınız. Yansımayı kullanarak bunu yapabilirsiniz, ancak çok fazla kod ve çok fazla hata işleme gerektirir.


Yansımayı kullanarak nasıl yapardın? Gördüğüm tek yöntem Class.getTypeParameters () yöntemidir, ancak bu yalnızca bildirilen türleri döndürür, çalışma zamanı türlerini döndürmez.
David Citron


0

Eğer demek new E() istiyorsan, imkansız. Ve bunun her zaman doğru olmadığını ekleyeceğim - E'nin ortak argüman yapıcısı olup olmadığını nasıl anlarsınız? Ancak, oluşturma işlemini her zaman nasıl bir örnek oluşturulacağını bilen başka bir sınıfa devredebilirsiniz - olabilir Class<E>veya bunun gibi özel kodunuz

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
Bu, orijinal sorudaki örneğimde işe yaramıyor. Üst sınıf SomeContainerbasitçe Object. Bu nedenle, this.getClass().getGenericSuperclass()a değil Class(bir sınıf java.lang.Object) döndürür ParameterizedType. Bu aslında akran yanıtı tarafından da belirtildi stackoverflow.com/questions/75175/… .
David Citron

1
Tamamen yanlış: "main" iş parçacığındaki özel durum java.lang.ClassCastException: java.lang.Class java.lang.reflect.ParameterizedType
Aubin

0

Bunu aşağıdaki snippet ile yapabilirsiniz:

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
Tamamen yanlış: "main" iş parçacığındaki istisna java.lang.ClassCastException: java.lang.Class java.lang.reflect.ParameterizedType
Aubin

0

ERobertson makalesinde tartışılanlara benzer teknikler kullanarak sizin için çözebileceğiniz çeşitli kütüphaneler vardır . E tarafından temsil edilen ham sınıfı çözmek için TypeToolscreateContents kullanan bir uygulama :

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

Bu, getClass () öğesinin SomeContainer öğesinin bir alt sınıfına çözümlendiğini ve E'nin gerçek parametreleştirilmiş değerinin bir alt sınıfta yakalanmadığı zaman çalışma zamanında silineceği için başarısız olacağını varsayar.


0

Aşağıda temsil edilen ham sınıfı çözümlemek için TypeToolscreateContents kullanan bir uygulama :E

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

Bu yaklaşım yalnızca SomeContaineralt sınıflara sahipse çalışır , böylece gerçek değeri Ebir tür tanımında yakalanır:

class SomeStringContainer extends SomeContainer<String>

Aksi takdirde, E değeri çalışma zamanında silinir ve kurtarılamaz.


-1

Bir sınıf yükleyici ve sınıf adı, sonunda bazı parametreler ile yapabilirsiniz.

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);

bu sadece sınıf nesnesini geçmekten daha kötü
newacct

Sınıf yükleyicisine açıkça başvurmanıza gerek yoktur.
Stefan Reich

-1

İşte size dayalı gelişmiş bir çözüm ParameterizedType.getActualTypeArguments@Noah, @Lars Bohl ve diğerleri tarafından daha önce bahsedilen .

Uygulamada ilk küçük gelişme. Fabrika örneği değil, bir tür döndürmelidir. Kullanarak örneği döndürdüğünüzde Class.newInstance()kullanım kapsamını azaltırsınız. Çünkü sadece argüman olmayan yapıcılar bu şekilde çağrılabilir. Daha iyi bir yol, bir tür döndürmek ve istemcinin hangi kurucuyu çağırmak istediğini seçmesine izin vermektir:

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);
    }

  }
}

İşte bir kullanım örnekleri. @Lars Bohl, geneneric'i genişletme yoluyla elde etmenin sadece signe bir yolunu gösterdi. @noah yalnızca ile bir örnek oluşturarak {}. İşte her iki durumu da gösteren testler:

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);
  }
}

Not: Eğer müşterileri zorlayabilir TypeReferencehep kullanım {}örneği bu sınıf soyut yaparak oluşturulduğunda: public abstract class TypeReference<T>. Bunu yapmadım, sadece silinen test durumunu göstermek için.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.