Soru Java'da jenerik tip örneği oluşturulsun mu?


Java'da genel bir tür örneği oluşturmak mümkün mü? Ben cevabın ne olduğunu gördüğüme göre düşünüyorum. no (tip silme nedeniyle), ama kimsenin kaybolduğum bir şeyi görmesiyle ilgilenirim:

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

EDIT: Bu çıkıyor Süper Tip Jetonları Sorunumu çözmek için kullanılabilir, ancak aşağıdaki yanıtlardan bazılarının gösterdiği gibi çok fazla yansıma tabanlı kod gerektirir.

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


491
2017-09-16 18:04


Menşei


Android cihazında sadece test edilmiş performans. 10000 işlemler ve: 8-9 ms yeni SomeClass () alır, 9-11 ms Fabrika <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ı. Reflection newInstance () 4 farklı istisna atar, hatırladın mı? Bu yüzden fabrika modelini kullanmaya karar verdim - Deepscorn
Ayrıca bakınız: stackoverflow.com/a/5684761/59087 - Dave Jarvis
Java 8 ile, şimdi bir yapıcı referansı veya bir lambda geçirebilirsiniz, bu da bu soruna geçici bir çözüm getirmektedir. Görmek cevabım aşağıda detaylar için. - Daniel Pryden
Bu tür bir kod yazmanın kötü bir fikir olduğunu düşünüyorum, altta yatan sorunu çözmek için daha zarif ve okunabilir yollar. - Krzysztof Cichocki


Cevaplar:


Haklısın. Yapamazsın new E(). Ancak bunu 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 düzeninde sarmak onu biraz daha tolere edebilir.


285
2017-09-16 18:10



Evet, bu çözümü gördüm, ancak yalnızca, örneğini oluşturmak istediğiniz türün bir Sınıf nesnesine başvurunuz varsa çalışır. - David Citron
Evet biliyorum. Eğer E.class yapabiliyor olsaydın güzel olurdu ama bu sadece silme nedeniyle Object.class verir :) - Justin Rudd
Bu problem için doğru yaklaşım budur. Genelde istediğin şey değil, ama aldığın şey bu. - Joachim Sauer
Ve createContents () yöntemini nasıl çağırırsınız? - Alexis Dufrenoy
Bunu yapmanın tek yolu bu değil, şimdi bir geçişi gerektirmeyen daha iyi bir yol var. Class<?> Guava ve TypeToken kullanarak referans, Kod ve linkler için bu cevabı gör! - feeling unwelcome


Bu yardımcı olursa, Dunno, ancak genel bir tür alt sınıf (anonim olarak) dahil, tür bilgisi yansıma yoluyla kullanılabilir. Örneğin.,

public abstract class Foo<E> {

  public E instance;  

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

}

Böylece, Foo alt sınıfını seçtiğinizde, örneğin Bar örneğini alırsınız.

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


111
2017-09-16 18:18



Evet, özellikle de jenerik sınıf soyut ise, bu güzeldir, bunu beton alt sınıflarında yapabilirsiniz :) - Pierre Henry
Bu yöntem ayrıca ders çalışıyorsa Foo soyut değil Ama neden sadece Foo'nun anonim alt sınıflarında çalışıyor? Farz edelim Foo beton (dışarıda bırakıyoruz abstract), neden olacak new Foo<Bar>(); bir hatayla sonuçlanırken new Foo<Bar>(){}; değil mi? (İstisna: "Class ParameterizedType'a dökülemez") - Tim Kuipers
@TimKuipers <E> içinde class Foo<E> herhangi bir belirli türe bağlı değildir. Her zaman istisnai davranışları göreceksiniz E değil statik bağlı olarak, new Foo<Bar>(), new Foo<T>() {...}veya class Fizz <E> extends Foo<E>. İlk dava statik olarak bağlı değil, sildim derleme zamanında. İkinci durum yerine başka bir tip değişkeni (T) yerine E ama hala bağlı değil. Ve son durumda, açık olmalı ki E hala bağlı değil. - William Price
Tür parametresini statik olarak bağlama örneği class Fizz extends Foo<Bar>- bu durumda, kullanıcılar Fizz bir şey olsun Foo<Bar> ve bir şey olamaz ama bir Foo<Bar>. Bu durumda, derleyici bu bilgiyi sınıf metadata kodlamaktan mutluluk duyar. Fizz ve bunu bir ParameterizedType yansıma kodu. Anonim bir iç sınıf oluşturduğunuzda new Foo<Bar>() {...} yerine aynı şeyi yapıyor, yerine Fizz Derleyici, dış sınıf derlenene kadar bilmediğiniz bir "anonim" sınıf adı oluşturur. - William Price
Tür bağımsız değişkenleri de bir ParameterizedType ise, bu çalışmaz dikkat edilmelidir. Örneğin, Foo<Bar<Baz>>. Bir örnek oluşturacaksınız ParameterizedTypeImpl açıkça oluşturulamaz. Bu nedenle, kontrol etmek iyi bir fikir olup olmadığını getActualTypeArguments()[0] bir dönüyor ParameterizedType. Öyleyse, ham türü almak ve bunun yerine bir örneği oluşturmak istiyorsunuz. - crush


Parayı geçmek için bir tür veya başka bir çeşit 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();
    }
}

81
2017-09-16 18:36



..and Factory.create () nasıl görünüyor? - OhadR
@OhadR Factory<> bir arayüz ve bu yüzden vücut yok. Önemli olan, parayı bir örneği oluşturmak için gerekli kodu "bilmesi" yöntemlerine aktarmak için bir indirme katmanına ihtiyaç duymanızdır. Bunu metal dilbilimden ziyade normal kodla yapmak daha iyidir Class veya Constructor Yansıma, bütün bir acı dünyasını beraberinde getirir. - Tom Hawtin - tackline
Artık günlerde, şu şekilde bir yöntem referans ifadesiyle bir fabrika örneği oluşturabilirsiniz: SomeContainer<SomeElement> cont = new SomeContainer<>(SomeElement::new); - Lii


Java 8'de kullanabilirsiniz Supplier Bu kolayca elde etmek için fonksiyonel arayüz:

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

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

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

Bu sınıfı böyle yaparsın:

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

Sözdizimi String::new bu hat üzerinde bir yapıcı referansı.

Yapıcınız argüman alırsa, bunun yerine bir lambda ifadesi kullanabilirsiniz:

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

73
2018-03-30 16:51



İyi bir. Yansımadan ve istisnaları tedavi etmekten kaçınır. - Bruno Leite
Çok hoş. Ne yazık ki Android kullanıcıları için bu, API seviyesi 24 veya üstü gerektirir. - Michael Updike
Daha az yaygara ve fonksiyonel yaklaşıma göre bu benim görüşüme göre cevap kabul edilecektir. - Tomas


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

}

21
2017-09-03 12:41



Bu kod tarafından iyi bir yaklaşım, genel bir jenerik türünde kullanırsanız ClassCastException neden olabilir. Sonra actualType argümanını geri alırsınız. Ayrıca ParamterizedType olduğunu kontrol edip eğer RawType (veya bundan daha iyi bir şey) döndürürseniz kontrol etmelisiniz. Bununla ilgili bir başka konu da, bu kodun ClassCastExeption'ı atacağı zaman daha fazla uzattığımız zamandır. - Damian Leszczyński - Vash
Nedeniyle: java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl java.lang.Class'a dönüştürülemez - juan


Genel bir sınıf içinde bir tür argümanının yeni bir örneğine ihtiyacınız varsa, yapıcı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 Super Type Token (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:

  • Sınıfı varsayılan bir kurucuya aktaramazsınız (bu yüzden Foo finaldir). Eğer varsayılan bir kurucuya gerçekten ihtiyacınız varsa, her zaman bir setter metodu ekleyebilir, ancak daha sonra ona bir çağrı vermeyi hatırlamanız gerekir.
  • Robertson itiraz ... Bir kara koyundan daha fazla Bar (tip argüman sınıfını belirtmekle birlikte bir kez daha seni öldürmeyecek). Ve Robertson'un iddialarına aykırı bir şekilde, bu, DRY prensibini ihlal etmemektedir, çünkü derleyici, tür doğruluğunu sağlayacaktır.
  • Tam olarak değil Foo<L>kanıt. Yeni başlayanlar için... newInstance() type argument sınıfı varsayılan bir kurucuya sahip değilse bir wobbler atar. Bu yine de bilinen tüm çözümlere uygulanır.
  • STT yaklaşımının toplam kapsüllemesini yok eder. Gerçi büyük bir anlaşma değil (STT'nin aşırı performans yükü dikkate alınarak).

18
2018-01-07 07:11





Bunu şimdi yapabilirsiniz ve bir demet 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, bazı yansıma gerektirecek yapıcıyı çağırmak gerekirse, ama bu çok iyi belgelenmiş, bu hile değildir!

İşte TypeToken için JavaDoc.


17
2017-08-08 02:03



Bu çözüm, tıpkı @ noah'ın yansıma ile verdiği cevap gibi sınırlı bir dizi dava için çalışır. Bugün hepsini denedim ... Ve parametre sınıfının bir örneğini parametrelenmiş sınıfa iletmek için sona erdi (.newInstance () 'ı arayabilmek için). "Generics" in çok büyük eksikliği ... yeni Foo <Bar> (Bar.class); ... sınıf 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 çalışır, jenerik parametreleri alan statik fabrika yöntemleri bile - feeling unwelcome


Daha işlevsel bir yaklaşımı düşünün: bir E'nin hiçbir şeyden çıkmasını (açıkça bir kod kokusu) oluşturmak yerine, nasıl oluşturulacağını bilen bir işlevi iletin, örn.

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

12
2018-04-16 16:20



Teknik olarak bir işlevi geçmiyorsunuz, işlev nesnesi (olarak da bilinir funktoru). - Lorne Laliberte
Ya da belki de yakalamanın üstesinden gelmek Exception kullanım Supplier<E> yerine. - Martin D


itibaren Java Eğitimi - Jenerikle İlgili Kısıtlamalar:

Tür Parametreleri Örnekleri Oluşturulamıyor

Bir tür parametresi örneği 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);
}

Bir geçici çözüm olarak, yansıma yoluyla bir tür 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 şu şekilde çağırabilirsiniz:

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

8
2017-09-13 13:12





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

DÜZENLEME: Alternatif olarak bu kurucuyu kullanabilirsiniz (ancak E örneğini gerektirir):

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

6
2017-09-16 18:14



Evet, bu jenerikler olmadan bile aynı şekilde çalışır - jeneriklerle birlikte bu konteynerin somutlaştırılması biraz gereksiz hale gelir (“E” nin iki kere olduğunu belirtmeniz gerekir). - David Citron
iyi, Java ve jenerik kullandığınızda olanlar ... güzel değiller ve ciddi sınırlamalar var ... - Mike Stone


Aşağıdaki gibi örnekleme sırasında sınıf adının iki kez yazılmasını 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
2017-09-17 20:19