I generics di Instantiating digitano in java

Duplicato: generici Java perché questo non funzionerà

Duplicato: istanziazione di una class generica in Java

Vorrei creare un object di tipo Generics in java. Si prega di suggerire come posso ottenere lo stesso.

Nota: questo può sembrare un banale problema generico. Ma scommetto .. non lo è. 🙂

supponiamo che abbia la dichiarazione di class come:

public class Abc { public T getInstanceOfT() { // I want to create an instance of T and return the same. } } 

 public class Abc { public T getInstanceOfT(Class aClass) { return aClass.newInstance(); } } 

Dovrai aggiungere la gestione delle eccezioni.

È necessario passare il tipo effettivo al runtime, poiché non fa parte del codice byte dopo la compilazione, quindi non c’è modo di saperlo senza fornirlo esplicitamente.

Nel codice che hai postato, è imansible creare un’istanza di T poiché non sai di che tipo si tratta:

 public class Abc { public T getInstanceOfT() { // There is no way to create an instance of T here // since we don't know its type } } 

Naturalmente è ansible se si ha un riferimento a Class e T ha un costruttore predefinito, basta chiamare newInstance() sull’object Class .

Se si sottoclass Abc si può anche aggirare il problema della cancellazione dei tipi e non si devono passare riferimenti di Class intorno a:

 import java.lang.reflect.ParameterizedType; public class Abc { T getInstanceOfT() { ParameterizedType superClass = (ParameterizedType) getClass().getGenericSuperclass(); Class type = (Class) superClass.getActualTypeArguments()[0]; try { return type.newInstance(); } catch (Exception e) { // Oops, no default constructor throw new RuntimeException(e); } } public static void main(String[] args) { String instance = new SubClass().getInstanceOfT(); System.out.println(instance.getClass()); } } class SubClass extends Abc { } 

Quello che hai scritto non ha senso, i generici in Java hanno lo scopo di aggiungere la funzionalità del polimorfismo parametrico agli oggetti.

Cosa significa? Vuol dire che vuoi mantenere alcune variabili di tipo delle tue classi indecise, per poter usare le tue classi con molti tipi diversi.

Ma la tua variabile di tipo T è un attributo che viene risolto in fase di esecuzione, il compilatore Java compilerà la class dimostrando la sicurezza del tipo senza cercare di sapere che tipo di object è T quindi è imansible lasciare che usi una variabile di tipo in un metodo statico. Il tipo è associato a un’istanza di runtime dell’object mentre public void static main(..) è associato alla definizione della class e in tale ambito T non significa nulla.

Se si desidera utilizzare una variabile di tipo all’interno di un metodo statico, è necessario dichiarare il metodo come generico (questo perché, come spiegato, le variabili di tipo di una class template sono correlate alla sua istanza di runtime ), non la class:

 class SandBox { public static  void myMethod() { T foobar; } } 

questo funziona, ma ovviamente non con il metodo main poiché non c’è modo di chiamarlo in un modo generico.

EDIT : Il problema è che a causa della cancellazione dei tipi solo una class generica viene compilata e passata a JVM. Type checker controlla solo se il codice è sicuro, poi da quando ha dimostrato che ogni tipo di informazione generica viene scartata.

Per istanziare T devi conoscere il tipo di T , ma può essere di molti tipi allo stesso tempo, quindi una soluzione con richiede solo la quantità minima di riflessione è usare la Class per istanziare nuovi oggetti:

 public class SandBox { Class reference; SandBox(Class classRef) { reference = classRef; } public T getNewInstance() { try { return reference.newInstance(); } catch (Exception e) { e.printStackTrace(); } return null; } public static void main(String[] args) { SandBox t = new SandBox(String.class); System.out.println(t.getNewInstance().getClass().getName()); } } 

Ovviamente questo implica che il tipo che vuoi istanziare:

  • non è un tipo primitivo
  • ha un costruttore predefinito

Per operare con diversi tipi di costruttori devi scavare più a fondo nella riflessione.

È necessario ottenere le informazioni sul tipo in modo statico. Prova questo:

 public class Abc { private Class clazz; public Abc(Class clazz) { this.clazz = clazz; } public T getInstanceOfT() throws InstantiationException, IllegalAccessException { return clazz.newInstance(); } } 

Usalo come tale:

  Abc abc = new Abc(String.class); abc.getInstanceOfT(); 

A seconda delle tue esigenze, potresti voler usare Class< ? extends T> Class< ? extends T> invece Class< ? extends T> .

L’unico modo per farlo funzionare è usare Reified Generics . E questo non è supportato in Java (ancora? È stato progettato per Java 7, ma è stato posticipato). Ad esempio, in C # è supportato assumendo che T abbia un costruttore predefinito . Puoi anche ottenere il tipo di runtime digitando typeof(T) e ottenere i costruttori da Type.GetConstructor() . Non faccio C # in modo che la syntax non sia valida, ma assomiglia all’incirca a questa:

 public class Foo where T:new() { public void foo() { T t = new T(); } } 

La migliore “soluzione” per questo in Java è passare una Class come argomento metodo invece di diverse risposte già evidenziate.

Innanzitutto, non è ansible accedere al parametro di tipo T nel metodo main static, solo su membri di class non statici (in questo caso).

In secondo luogo, non è ansible creare un’istanza di T perché Java implementa i generici con Cancellazione di tipi . Quasi tutte le informazioni generiche vengono cancellate al momento della compilazione.

Fondamentalmente, non puoi farlo:

 T member = new T(); 

Ecco un bel tutorial sui generici .

Non sembri capire come funziona Generics. Si consiglia di guardare http://java.sun.com/j2se/1.5.0/docs/guide/language/generics.html Fondamentalmente quello che si potrebbe fare è qualcosa di simile

 public class Abc { T someGenericThing; public Abc(){} public T getSomeGenericThing() { return someGenericThing; } public static void main(String[] args) { // create an instance of "Abc of String" Abc stringAbc = new Abc(); String test = stringAbc.getSomeGenericThing(); } } 

Stavo implementando lo stesso usando il seguente approccio.

 public class Abc { T myvar; public T getInstance(Class clazz) throws InstantiationException, IllegalAccessException { return clazz.newInstance(); } } 

Stavo cercando di trovare un modo migliore per ottenere lo stesso risultato.

Non è ansible?

Digitare soluzione di cancellazione

Ispirato dalla risposta di @ martin , ho scritto una class helper che mi consente di risolvere il problema della cancellazione del tipo. Usando questa class (e un po ‘brutto trucco) sono in grado di creare una nuova istanza da un tipo di modello:

 public abstract class C_TestClass { T createTemplateInstance() { return C_GenericsHelper.createTemplateInstance( this, 0 ); } public static void main( String[] args ) { ArrayList list = new C_TestClass >(){}.createTemplateInstance(); } } 

Il brutto trucco qui è quello di rendere abstract la class in modo tale che l’utente della class sia costretto a sottotitolarlo. Qui lo sto sottoclassi aggiungendo {} dopo la chiamata al costruttore. Questo definisce una nuova class anonima e ne crea un’istanza.

Una volta che la class generica è sottotipo con tipi di modelli concreti, sono in grado di recuperare i tipi di modello.


 public class C_GenericsHelper { /** * @param object instance of a class that is a subclass of a generic class * @param index index of the generic type that should be instantiated * @return new instance of T (created by calling the default constructor) * @throws RuntimeException if T has no accessible default constructor */ @SuppressWarnings( "unchecked" ) public static  T createTemplateInstance( Object object, int index ) { ParameterizedType superClass = (ParameterizedType )object.getClass().getGenericSuperclass(); Type type = superClass.getActualTypeArguments()[ index ]; Class instanceType; if( type instanceof ParameterizedType ) { instanceType = (Class )( (ParameterizedType )type ).getRawType(); } else { instanceType = (Class )type; } try { return instanceType.newInstance(); } catch( Exception e ) { throw new RuntimeException( e ); } } } 

Sembra che tu stia cercando di creare la class che funge da punto di ingresso alla tua applicazione come generica, e che non funzionerà … La JVM non saprà che tipo si suppone stia usando quando è istanziata come si avvia l’applicazione.

Tuttavia, se questo fosse il caso più generale, allora qualcosa del genere sarebbe ciò che stai cercando:

 public MyGeneric getMeAGenericObject(){ return new MyGeneric(); } 

o forse:

 MyGeneric objMyObject = new MyGeneric(); 

Ci sono modi di hacking attorno a questo quando devi davvero farlo.

Ecco un esempio di un metodo di trasformazione che trovo molto utile; e fornisce un modo per determinare la class concreta di un generico.

Questo metodo accetta una raccolta di oggetti come input e restituisce una matrice in cui ogni elemento è il risultato della chiamata a un getter di campo su ciascun object nella raccolta di input. Ad esempio, supponi di avere un List e desideri una String[] contenente il cognome di tutti.

Il tipo del valore di campo restituito dal getter è specificato dalla E generica, e ho bisogno di istanziare una matrice di tipo E[] per memorizzare il valore di ritorno.

Il metodo in sé è un po ‘brutto, ma il codice che scrivi che lo utilizza può essere molto più pulito.

Nota che questa tecnica funziona solo quando da qualche parte negli argomenti di input c’è un object il cui tipo corrisponde al tipo restituito, e puoi deterministicmente calcolarlo. Se le classi concrete dei tuoi parametri di input (o dei loro sottooggetti) non possono dirti nulla sui generici, questa tecnica non funzionerà.

 public  E[] array (Collection c) { if (c == null) return null; if (c.isEmpty()) return (E[]) EMPTY_OBJECT_ARRAY; final List collect = (List) CollectionUtils.collect(c, this); final Class elementType = (Class) ReflectionUtil.getterType(c.iterator().next(), field); return collect.toArray((E[]) Array.newInstance(elementType, collect.size())); } 

Il codice completo è disponibile qui: https://github.com/cobbzilla/cobbzilla-utils/blob/master/src/main/java/org/cobbzilla/util/collection/FieldTransformsr.java#L28

 Abc abcInstance = new Abc (); 

..per esempio