Tworzenie instancji klasy generycznej w Javie [duplikat]

to pytanie ma już odpowiedzi tutaj : Instantiating generics type in java (12 odpowiedzi) Zamknięte 10 miesięcy temu . Wiem, że generyki Javy są nieco gorsze od. Net.]}

Mam klasę generyczną Foo<T> i naprawdę muszę utworzyć instancję T w Foo używając konstruktora bez parametrów. Jak można obejść ograniczenia Javy?

 157
Author: arshajii, 2009-07-07

10 answers

Jedną z opcji jest podanie Bar.class (lub dowolnego typu, który Cię interesuje - dowolny sposób określenia odpowiedniej referencji Class<T>) i zachowanie tej wartości jako pola:

public class Test {
    public static void main(String[] args) throws IllegalAccessException,
            InstantiationException {
        Generic<Bar> x = new Generic<>(Bar.class);
        Bar y = x.buildOne();
    }
}

public class Generic<T> {
    private Class<T> clazz;

    public Generic(Class<T> clazz) {
        this.clazz = clazz;
    }

    public T buildOne() throws InstantiationException, IllegalAccessException {
        return clazz.newInstance();
    }
}

public class Bar {
    public Bar() {
        System.out.println("Constructing");
    }
}

Inną opcją jest posiadanie interfejsu "fabrycznego" i przekazanie fabryki konstruktorowi klasy generic. To jest bardziej elastyczne i nie musisz się martwić o wyjątki od odbicia.

 176
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-01-02 14:01:28

I to jest implementacja Fabryczna, jak zasugerował Jon Skeet :

interface Factory<T> {
    T factory();
}

class Araba {
    //static inner class for Factory<T> implementation
    public static class ArabaFactory implements Factory<Araba> {
        public Araba factory() {
            return new Araba();
        }
    }
    public String toString() { return "Abubeee"; }
}

class Generic<T> {
    private T var;

    Generic(Factory<T> fact) {
        System.out.println("Constructor with Factory<T> parameter");
        var = fact.factory();
    }
    Generic(T var) {
        System.out.println("Constructor with T parameter");
        this.var = var;
    }
    T get() { return var; }
}

public class Main {
    public static void main(String[] string) {
        Generic<Araba> gen = new Generic<Araba>(new Araba.ArabaFactory());
        System.out.print(gen.get());
    }
}

Wyjście:

Constructor with Factory<T> parameter
Abubeee
 49
Author: Özgür,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:53

Oto dość wymyślony sposób, aby to zrobić bez jawnego użycia argumentu konstruktora. Musisz rozszerzyć parametryzowaną klasę abstrakcyjną.

public class Test {   
    public static void main(String [] args) throws Exception {
        Generic g = new Generic();
        g.initParameter();
    }
}

import java.lang.reflect.ParameterizedType;
public abstract class GenericAbstract<T extends Foo> {
    protected T parameter;

    @SuppressWarnings("unchecked")
    void initParameter() throws Exception, ClassNotFoundException, 
        InstantiationException {
        // Get the class name of this instance's type.
        ParameterizedType pt
            = (ParameterizedType) getClass().getGenericSuperclass();
        // You may need this split or not, use logging to check
        String parameterClassName
            = pt.getActualTypeArguments()[0].toString().split("\\s")[1];
        // Instantiate the Parameter and initialize it.
        parameter = (T) Class.forName(parameterClassName).newInstance();
    }
}

public class Generic extends GenericAbstract<Foo> {
}

public class Foo {
    public Foo() {
        System.out.println("Foo constructor...");
    }
}
 7
Author: Glenn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-12 16:26:55

Naprawdę muszę utworzyć instancję T w Foo używając parametru-less konstruktor

Prosta odpowiedź brzmi:" nie możesz tego zrobić " java używa typu erasure, aby zaimplementować generyki, które uniemożliwiałyby ci to.

Jak można obejść ograniczenia Javy?

Jednym ze sposobów (mogą być inne) jest przekazanie obiektu, który przekazałbyś instancji T do konstruktora Foo<T>. Lub możesz mieć metodę setBar(T theInstanceofT);, Aby uzyskać t zamiast tworzenie instancji w klasie it self.

 4
Author: hhafez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-07 11:10:49

Dla Javy 8 ....

Jest dobre rozwiązanie w https://stackoverflow.com/a/36315051/2648077 post.

To używa Java 8 Supplier functional interface

 2
Author: Alireza Fattahi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-18 05:37:57

Generyki w Javie są na ogół potężniejsze niż w C#.

Jeśli chcesz zbudować obiekt, ale bez podłączania konstruktora / metody statycznej, użyj abstrakcyjnej fabryki. Powinieneś być w stanie znaleźć szczegółowe informacje i samouczki na temat abstrakcyjnego wzorca Fabrycznego w dowolnej książce podstawowych wzorców projektowych, wstępie do OOP lub na całym interwebs. Nie warto tutaj powielać kodu, poza tym wspomnieć, że składnia zamknięcia Javy jest do bani.

IIRC, C # ma specjalny przypadek do określenia typu ogólnego ma konstruktor no-args. Ta nieprawidłowość z definicji zakłada, że kod klienta chce korzystać z tej konkretnej formy konstrukcji i zachęca do zmienności.

Używanie do tego refleksji jest po prostu niewłaściwe. Generics w Javie to funkcja typowania statycznego w czasie kompilacji. Próby użycia ich w czasie wykonywania są wyraźną oznaką, że coś idzie nie tak. Odbicie powoduje szczegółowy kod, awarie uruchomieniowe, niezaznaczone zależności i luki w zabezpieczeniach. (Class.forName jest szczególnie zły.)

 1
Author: Tom Hawtin - tackline,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-07 09:36:44

Z https://stackoverflow.com/a/2434094/848072 . potrzebujesz domyślnego konstruktora dla klasy T.


import java.lang.reflect.ParameterizedType;

class Foo {

  public bar() {
    ParameterizedType superClass = (ParameterizedType) getClass().getGenericSuperclass();
    Class type = (Class) superClass.getActualTypeArguments()[0];
    try {
      T t = type.newInstance();
      //Do whatever with t
    } catch (Exception e) {
      // Oops, no default constructor
      throw new RuntimeException(e);
    } 
  }
}

 1
Author: albfan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:19

Mogę to zrobić w testach JUnit.

Chciałem przetestować elewację Hibernate, więc szukałem ogólnego sposobu, aby to zrobić. Zauważ, że fasada również implementuje ogólny interfejs. Tutaj T jest klasą bazy danych, a U kluczem głównym. Ifacade<T,U> jest fasadą umożliwiającą dostęp do obiektu bazy danych T za pomocą klucza głównego U.

public abstract class GenericJPAController<T, U, C extends IFacade<T,U>>

{
    protected static EntityManagerFactory emf;

    /* The properties definition is straightforward*/
    protected T testObject;
    protected C facadeManager;

    @BeforeClass
    public static void setUpClass() {


        try {
            emf = Persistence.createEntityManagerFactory("my entity manager factory");

        } catch (Throwable ex) {
            System.err.println("Failed to create sessionFactory object." + ex);
            throw new ExceptionInInitializerError(ex);
        }

    }

    @AfterClass
    public static void tearDownClass() {
    }

    @Before
    public void setUp() {
    /* Get the class name*/
        String className = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[2].getTypeName();

        /* Create the instance */
        try {
            facadeManager = (C) Class.forName(className).newInstance();
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
            Logger.getLogger(GenericJPAController.class.getName()).log(Level.SEVERE, null, ex);
        }
        createTestObject();
    }

    @After
    public void tearDown() {
    }

    /**
     * Test of testFindTEntities_0args method, of class
     * GenericJPAController<T, U, C extends IFacade<T,U>>.
     * @throws java.lang.ClassNotFoundException
     * @throws java.lang.NoSuchMethodException
     * @throws java.lang.InstantiationException
     * @throws java.lang.IllegalAccessException
     */
    @Test
    public void  testFindTEntities_0args() throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException {

        /* Example of instance usage. Even intellisense (NetBeans) works here!*/
        try {
            List<T> lista = (List<T>) facadeManager.findAllEntities();
            lista.stream().forEach((ct) -> {
                System.out.println("Find all: " + stringReport());
            });
        } catch (Throwable ex) {
            System.err.println("Failed to access object." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }


    /**
     *
     * @return
     */
    public abstract String stringReport();

    protected abstract T createTestObject();
    protected abstract T editTestObject();
    protected abstract U getTextObjectIndex();
}
 1
Author: Krauss,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-19 02:41:19

Użyj metody Constructor.newInstance. Metoda Class.newInstance jest przestarzała od wersji Java 9 w celu zwiększenia rozpoznawania WYJĄTKÓW instancji przez kompilator.

public class Foo<T> {   
    public Foo()
    {
        Class<T> newT = null; 
        instantiateNew(newT);
    }

    T instantiateNew(Class<?> clsT)
    {
        T newT;
        try {
            newT = (T) clsT.getDeclaredConstructor().newInstance();
        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
            | InvocationTargetException | NoSuchMethodException | SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return null;
        }
        return newT;
    }
}
 1
Author: Erik Ward Eeshwar Das,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-02-24 21:29:48

Szybkie rozwiązanie, które zadziałało. Widzę, że już jest na to odpowiedź i może to nawet nie być najlepszy sposób, aby to zrobić. Ponadto, do mojego rozwiązania będziesz potrzebował Gson .

Jednak natrafiłem na sytuację, w której musiałem stworzyć instancję klasy generycznej typu java.lang.reflect.Type.

Poniższy kod utworzy instancję danej klasy ze zmiennymi instancji null.

T object = new Gson().fromJson("{}", myKnownType);

Gdzie {[2] } jest znane przed ręką i uzyskane przez TypeToken.getType().

Ty można teraz ustawić odpowiednie właściwości tego obiektu. Ponownie, może to nie być najlepszy sposób, aby to zrobić, ale działa jako szybkie rozwiązanie, jeśli tego potrzebujesz.

 0
Author: Markymark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-10 01:14:51