Tworzenie instancji klasy generycznej w Javie [duplikat]
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?
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.
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
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...");
}
}
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.
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
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.)
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);
}
}
}
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();
}
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;
}
}
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.
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