it-swarm-tr.com

Java'da genel tür örneği oluştur?

Java'da genel bir tür örneği oluşturmak mümkün mü? Cevabın no (silme türünden dolayı) olduğuna dayanarak düşünüyorum, ancak birileri eksik olduğumu görebiliyorsa ilgilenirim:

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

EDIT: Sorunu çözmek için Süper Tip Belirteçleri kullanılabileceği ortaya çıktı, ancak aşağıdaki yanıtların bazılarının belirttiği gibi çok fazla yansıma tabanlı kod gerektiriyor.

Bu konuyu bir süre daha açık bırakacağım, birinin Ian Robertson'dan Artima Makalesi .

525
David Citron

Haklısın. new E() yapamazsınız. Ama onu değiştirebilirsin.

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

Bu bir acı. Ama işe yarıyor. Fabrika şablonuna sarmak, biraz daha tolere edilebilir kılar.

305
Justin Rudd

Dunno bu yardımcı olur, ancak genel bir türü (anonim dahil) alt sınıf, tür bilgileri 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'yu alt sınıflarken, örneğin Bar,

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

121
noah

Paranın karşılığını almak için bir tür veya başka 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();
    }
}
86

Java 8'de bunu kolayca başarmak için Supplier / işlevsel arayüzünü 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 satırdaki sözdizimi String::new, a constructor reference şeklindedir.

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

SomeContainer<BigInteger> bigIntegerContainer
    = new SomeContainer<>(() -> new BigInteger(1));
86
Daniel Pryden
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());
    }

}
23
Lars Bohl

Genel bir sınıf içinde yeni bir tür argüman örneğine ihtiyacınız varsa, kurucularınızı sınıfını talep ettirin ...

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'un Süper Tip Simgesi (STT) yaklaşımından çok daha basit (ve daha az problemli).
  • STT yaklaşımından çok daha etkili (kahvaltı için cep telefonunuzu yiyecek).

Eksileri:

  • Class'ı varsayılan bir yapıcıya geçiremezsiniz (Foo nihaidir bu yüzden). Varsayılan bir kurucuya gerçekten ihtiyacınız varsa, her zaman bir ayarlayıcı yöntemi ekleyebilirsiniz, ancak daha sonra onu aramayı hatırlamanız gerekir.
  • Robertson'un itirazı ... Bir kara koyundan çok daha fazla Bar (her ne kadar tip bir argüman sınıfı belirtmek seni tam olarak öldürmez). Ve Robertson’un iddialarına aykırı olarak bu, DRY ilkesini ihlal etmiyor çünkü derleyici türün doğruluğunu sağlayacak.
  • Tamamen Foo<L>proof değil. Yeni başlayanlar için ... newInstance(), type argüman sınıfının varsayılan bir kurucusuna sahip olmaması durumunda bir yalpalayıcı atar. Bu, yine de bilinen tüm çözümler için geçerlidir.
  • STT yaklaşımının toplam kapsüllemesi yoktur. Yine de büyük bir şey değil (STT’nin aşırı performansları göz önüne alındığında).
20
R2D2M2

Bunu şimdi yapabilirsiniz ve bir sürü 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 yansıma gerektiren yapıcıyı aramanız gerekirse, ama bu çok iyi belgelenmiştir, bu numara değil!

İşte TypeToken için JavaDoc .

19
user177800

Daha işlevsel bir yaklaşım düşünün: hiç bir şey yapmadan biraz E oluşturmak yerine (ki bu açıkça bir kod kokusu), nasıl oluşturulacağını bilen bir işlevi, yani.

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

From Java Tutorial - Generics Üzerindeki Kısıtlamalar :

Tip Parametrelerinin Örnekleri Oluşturulamıyor

Bir type parametresi örneği oluşturamazsınız. Örneğin, aşağıdaki kod 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 bir tür parametresinin nesnesi 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);
8
Sergiy Sokolenko

İş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ıyı kullanabilirsiniz (ancak E'nin bir örneğini gerektirir):

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

Örnekleme sırasında sınıf adını iki kez yazmak istemezseniz:

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

Java ne yazık ki yapmak istediğine izin vermiyor. Bkz. resmi geçici çözüm :

Bir type parametresi örneği oluşturamazsınız. Örneğin, aşağıdaki kod 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 bir tür parametresinin nesnesi 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);
5
Neepsnikeep

Derleme zamanında E ile çalışırken, asıl jenerik "E" türünü umursamıyorsunuz (ya yansımayı kullanıyorsunuz veya jenerik tipin temel sınıfıyla çalışıyorsunuzdur) bu yüzden alt sınıf E örneğini versin 

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
Ira

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

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

Kullanabilirsiniz:

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

Ancak, örneğin paketler de dahil olmak üzere tam bir sınıf adı girmeniz gerekir. Java.io.FileInputStream. Bunu bir matematik ifadesi çözümleyici oluşturmak için kullandım.

3
Jaroslav Smid

@ Noah'ın cevabının geliştirilmesi. 

Değişmek için sebep

a] Sıralamayı değiştirdiğiniz takdirde 1 genel tip kullanılıyorsa daha güvenlidir.

b] A sınıfı jenerik imzası zaman zaman değişiyor, böylece çalışma zamanında açıklanamayan istisnalar karşısında şaşıracaksınız.

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 bir astar kullanın

Bir Satır Kodu

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

Bunu yapabileceğimi düşündüm, ama oldukça hayal kırıklığına uğradım: işe yaramadı ama bence hala paylaşmaya değer 

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)

Satır 26, [*] olanıdır.

Uygulanabilir tek çözüm, @JustinRudd tarafından verilen çözümdür.

2

Robertson makalesinde anlatılanlara benzer teknikler kullanarak sizin için E öğesini çözebilecek çeşitli kütüphaneler var. İşte E ile temsil edilen ham sınıfı çözmek için TypeTools kullanan bir createContents uygulaması:

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

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

0
Jonathan

new E() ifadesini kastediyorsanız, bu imkansızdır. Ve bunun her zaman doğru olmadığını da eklerdim - E'nin halka açık bir kurucu yapıp yapamadığını nasıl bilebilirsin? Ancak, oluşturmayı her zaman bir örnek oluşturmayı bilen başka bir sınıfa devredebilir - Class<E> veya bunun gibi özel kodunuz olabilir

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

class IntegerFactory implements Factory<Integer>{    
  private static int i = 0; 
  Integer create() {        
    return i++;    
  }
}
0
Pavel Feldman
return   (E)((Class)((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]).newInstance();
0
Rachid

Bunu aşağıdaki snippet ile başarabilirsiniz:

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

İşte createContents tarafından temsil edilen ham sınıfı çözmek için TypeTools kullanan bir E uygulaması:

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

Bu yaklaşım sadece SomeContainer alt sınıfı olduğunda işe yarar, bu nedenle E öğesinin gerçek değeri bir tür tanımında yakalanır:

class SomeStringContainer extends SomeContainer<String>

Aksi halde E'nin değeri çalışma zamanında silinir ve kurtarılamaz.

0
Jonathan

yapabileceğin şey -

  1. İlk önce bu genel sınıfın değişkenini bildiriniz.

    2. Sonra bir kurucu yapıp o nesneyi başlat

  2. O zaman kullanmak istediğiniz her yerde kullanın.

örnek-

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
Sudhanshu Jain

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

0
Adam Rosenfield

Umarım bu yardımcı olmak için çok geç değil !!!

Java tip güvenliğidir, yalnızca Nesne örnek oluşturabilir.

Benim durumumda parametreleri createContents metoduna geçiremiyorum. Benim çözümüm, aşağıdaki tüm cevaplar yerine genişlemeler kullanıyor.

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

Bu benim parametrem olan, benim örnek olayım.

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

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

Yansımayı kullanmak, genel sınıfınızı hiçbir nesne türüyle genişletmiyorsanız, çalışma zamanı hatası oluşturur. Genel türünüzü nesneye genişletmek için bu hatayı zaman derlemesine dönüştürün. 

0
Se Song

Burada, @noah, @Lars Bohl ve diğerlerinin de söylediği ParameterizedType.getActualTypeArguments temelli geliştirilmiş bir çözüm. 

Uygulamadaki ilk küçük gelişme. Fabrika örneği döndürmemeli, bir tür döndürmelidir. Class.newInstance() işlevini kullanarak örnek döndürdüğünüzde kullanım kapsamını azaltırsınız. Çünkü sadece tartışmasız yapıcılar böyle çağrılabilir. Daha iyi bir yol, bir tür döndürmek ve bir 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 uzatma yoluyla yeniden tanımlamanın yalnızca bir yolunu göstermiştir. @noah sadece {} ile bir örnek oluşturarak. Her iki vakayı 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: TypeReference istemcilerini zorlayabilirsiniz, örnek bu sınıfı soyut yaparak {} işlevini kullanır: public abstract class TypeReference<T>. Yapmadım, sadece silinmiş test durumunu göstermek için. 

0
Alexandr