Esempio di creazione di un oggetto utilizzando Class.newInstance()
Immagina di essere incaricato di creare un oggetto usando la riflessione. Cominciamo?
Inizieremo scrivendo il codice per la classe che vogliamo istanziare:
public class Employee {
private String name;
private String lastName;
private int age;
{
age = -1;
name = "Rob";
surname = "Stark";
}
public Employee(String name, String surname, int age) {
this.name = name;
this.surname = surname;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return lastName;
}
public void setSurname(String surname) {
this.surname = surname;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", surname='" + surname + '\'' +
", age=" + age +
'}';
}
}
Questa sarà la nostra classe — con diversi campi, un costruttore con parametri, getter e setter, un metodo toString() e un blocco di inizializzazione. Ora passiamo alla seconda parte: creare un oggetto usando la riflessione. Il primo approccio che esamineremo utilizzerà Class.newInstance() .
public class Main {
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
Employee employee = Employee.class.newInstance();
System.out.println("age is " + employee.getAge());
}
}
Eccellente! Eseguiamo il nostro codice e osserviamo che l'età viene visualizzata. Ma otteniamo un errore relativo a un costruttore predefinito mancante. Si scopre che questo metodo ci consente solo di ottenere un oggetto creato utilizzando un costruttore predefinito. Aggiungiamo un costruttore predefinito per la nostra classe e testiamo nuovamente il codice.
Messaggio di errore:
Codice del nuovo costruttore
public Employee() { }
Dopo aver aggiunto il costruttore, ecco l'output:
Grande! Abbiamo capito come funziona questo metodo. Ora diamo un'occhiata sotto il cofano. Aprendo la documentazione, vediamo che il nostro metodo è già deprecato :
Può anche lanciare InstantiationException e IllegalAccessException . Di conseguenza, la documentazione suggerisce di utilizzare l'altro modo di creare un oggetto, vale a dire Constructor.newInstance() . Analizziamo nel dettaglio come funziona la classe Constructor .
metodi getConstructors e getDeclaredConstructors
Per lavorare con la classe Constructor , dobbiamo prima ottenere un'istanza. Abbiamo due metodi per questo: getConstructors e getDeclaredConstructors .
Il primo restituisce un array di costruttori pubblici e il secondo restituisce un array di tutti i costruttori di classi.
Concediamo alla nostra classe un po' di privacy, o meglio, creiamo alcuni costruttori privati per aiutare a dimostrare come funzionano questi metodi.
Aggiungiamo alcuni costruttori privati:
private Employee(String name, String surname) {
this.name = name;
this.lastName = lastName;
}
Guardando il codice, nota che uno dei costruttori è privato:
Testiamo i nostri metodi:
public class Main {
public static void main(String[] args) {
Class employeeClass = Employee.class;
System.out.println("getConstructors:");
printAllConstructors(employeeClass);
System.out.println("\n" +"getDeclaredConstructors:");
printDeclaredConstructors(employeeClass);
}
static void printDeclaredConstructors(Class<?> c){
for (Constructor<?> constructor : c.getDeclaredConstructors()) {
System.out.println(constructor);
}
}
static void printAllConstructors(Class<?> c){
for (Constructor<?> constructor : c.getConstructors()) {
System.out.println(constructor);
}
}
}
E otteniamo questo risultato:
public com.codegym.Employee(java.lang.String,java.lang.String,int)
public.com.codegym.Employee()
getDeclaredConstructors:
private com.codegym.Employee(java.lang.String,java.lang .String)
public com.codegym.Employee(java.lang.String,java.lang.String,int)
public com.codegym.Employee()
Ok, è così che otteniamo l'accesso all'oggetto Constructor . Ora possiamo parlare di cosa può fare.
La classe java.lang.reflect.Constructor ei suoi metodi più importanti
Diamo un'occhiata ai metodi più importanti e al loro funzionamento:
Metodo | Descrizione |
---|---|
getNome() | Restituisce il nome di questo costruttore come stringa. |
getModifiers() | Restituisce i modificatori di accesso Java codificati come numero. |
getExceptionTypes() | Restituisce una matrice di oggetti Class che rappresentano i tipi di eccezioni dichiarati dal costruttore. |
getParametri() | Restituisce una matrice di oggetti Parameter che rappresentano tutti i parametri. Restituisce una matrice di lunghezza 0 se il costruttore non ha parametri. |
getParameterTypes() | Restituisce una matrice di oggetti Class che rappresentano i tipi di parametro formali nell'ordine di dichiarazione. |
getGenericParameterTypes() | Restituisce una matrice di oggetti Type che rappresentano i tipi di parametri formali nell'ordine di dichiarazione. |
getName() & getModifiers()
Avvolgiamo il nostro array in un elenco per renderlo comodo da usare. Scriveremo anche i metodi getName e getModifiers :
static List<Constructor<?>> getAllConstructors(Class<?> c) {
return new ArrayList<>(Arrays.asList(c.getDeclaredConstructors()));
}
static List<String> getConstructorNames(List<Constructor<?>> constructors) {
List<String> result = new ArrayList<>();
for (Constructor<?> constructor : constructors) {
result.add(constructor.toString());
}
return result;
}
static List<String> getConstructorModifiers(List<Constructor<?>> constructors) {
List<String> result = new ArrayList<>();
for (Constructor<?> constructor : constructors) {
result.add(Modifier.toString(constructor.getModifiers()));
}
return result;
}
E il nostro metodo principale , dove chiameremo tutto:
public static void main(String[] args) {
Class employeeClass = Employee.class;
var constructors = getAllConstructors(employeeClass);
var constructorNames = getConstructorNames(constructors);
var constructorModifiers = getConstructorModifiers(constructors);
System.out.println("Employee class:");
System.out.println("Constructors :");
System.out.println(constructorNames);
System.out.println("Modifiers :");
System.out.println(constructorModifiers);
}
E ora vediamo tutte le informazioni che vogliamo:
Costruttori:
[private com.codegym.Employee(java.lang.String), public
com.codegym.Employee(java.lang.String,java.lang.String,int), public com.codegym.Employee() ]
Modificatori :
[privato, pubblico, pubblico]
getExceptionTypes()
Questo metodo ci consente di ottenere un array delle eccezioni che il nostro costruttore può generare. Modifichiamo uno dei nostri costruttori e scriviamo un nuovo metodo.
Qui cambiamo leggermente il nostro attuale costruttore:
private Employee(String name, String surname) throws Exception {
this.name = name;
this.lastName = lastName;
}
E qui abbiamo un metodo per ottenere i tipi di eccezione e aggiungerlo a main :
static List<Class<?>> getConstructorExceptionTypes(Constructor<?> c) {
return new ArrayList<>(Arrays.asList(c.getExceptionTypes()));
}
var constructorExceptionTypes = getConstructorExceptionTypes(constructors.get(0));
System.out.println("Exception types :");
System.out.println(constructorExceptionTypes);
Sopra, abbiamo effettuato l'accesso al primo costruttore nel nostro elenco. Discuteremo come ottenere un costruttore specifico un po' più tardi.
E guarda l'output dopo aver aggiunto throws Exception :
[classe java.lang.Exception]
E prima di aggiungere l'eccezione:
[]
Tutto è meraviglioso, ma come vediamo quali parametri sono richiesti dai nostri costruttori? Scopriamo anche questo.
getParameters() & getParameterTypes() & getGenericParameterTypes()
Ricominciamo affinando il nostro costruttore privato. Ora sarà simile a questo:
private Employee(String name, String surname, List<String> list) {
this.name = name;
this.lastName = lastName;
}
E abbiamo tre metodi aggiuntivi: getParameters per ottenere l'ordine dei parametri e i relativi tipi, getParameterTypes per ottenere i tipi di parametro e getGenericParameterTypes per ottenere i tipi racchiusi in generics .
static List<Parameter> getConstructorParameters(Constructor<?> c) {
return new ArrayList<>(Arrays.asList(c.getParameters()));
}
static List<Class<?>> getConstructorParameterTypes(Constructor<?> c) {
return new ArrayList<>(Arrays.asList(c.getParameterTypes()));
}
static List<Type> getConstructorParametersGenerics(Constructor<?> c) {
return new ArrayList<>(Arrays.asList(c.getGenericParameterTypes()));
}
E aggiungiamo qualche informazione in più al nostro metodo principale già non così piccolo:
var constructorParameterTypes = getConstructorParameterTypes(constructors.get(0));
var constructorParameters = getConstructorParameters(constructors.get(0));
var constructorParametersGenerics = getConstructorParametersGenerics(constructors.get(0));
System.out.println("Constructor parameters :");
System.out.println(constructorParameters);
System.out.println("Parameter types :");
System.out.println(constructorParameterTypes);
System.out.println("Constructor parameter types :");
System.out.println(constructorParametersGenerics);
Guardando l'output, vediamo informazioni molto dettagliate sui parametri dei nostri costruttori:
[java.lang.String arg0, java.lang.String arg1, java.util.List<java.lang.String> arg2]
Tipi di parametro:
[class java.lang.String, class java.lang.String, interface java.util.List]
Tipi di parametri del costruttore:
[class java.lang.String, class java.lang.String, java.util.List<java.lang.String>]
Ciò dimostra chiaramente la differenza tra ciascun metodo. Vediamo che abbiamo opzioni separate per ottenere informazioni su tipi di parametri, tipi di wrapping e tutto in generale. Super! Ora che conosciamo la classe Constructor , possiamo tornare all'argomento principale del nostro articolo: la creazione di oggetti.
Creazione di un oggetto utilizzando Constructor.newInstance()
Il secondo modo per creare oggetti è chiamare il metodo newInstance sul costruttore. Diamo un'occhiata a un esempio funzionante e vediamo come possiamo ottenere un particolare costruttore.
Se vuoi ottenere un singolo costruttore, dovresti usare il metodo getConstructor (da non confondere con getConstructors , che restituisce un array di tutti i costruttori). Il metodo getConstructor restituisce il costruttore predefinito.
public static void main(String[] args) throws NoSuchMethodException {
Class employeeClass = Employee.class;
Constructor<?> employeeConstructor = employeeClass.getConstructor();
System.out.println(employeeConstructor);
}
E se vogliamo ottenere un costruttore specifico, dobbiamo passare i tipi di parametro del costruttore a questo metodo.
Non dimenticare che possiamo ottenere il nostro costruttore privato solo utilizzando il metodo getDeclaredConstructor .
Constructor<?> employeeConstructor2 = employeeClass.getDeclaredConstructor(String.class, String.class, List.class);
System.out.println(employeeConstructor2);
È così che possiamo ottenere un costruttore specifico. Ora proviamo a creare oggetti utilizzando costruttori privati e pubblici.
Costruttore pubblico:
Class employeeClass = Employee.class;
Constructor<?> employeeConstructor = employeeClass.getConstructor(String.class, String.class, int.class);
System.out.println(employeeConstructor);
Employee newInstance = (Employee) employeeConstructor.newInstance("Rob", "Stark", 10);
System.out.println(newInstance);
Il risultato è un oggetto con cui possiamo lavorare:
Impiegato{name='Rob' cognome='Stark', età=10}
Tutto funziona alla grande! Ora proveremo con un costruttore privato:
Constructor<?> declaredConstructor = employeeClass.getDeclaredConstructor(String.class, String.class, List.class);
System.out.println(declaredConstructor);
Employee newInstance2 = (Employee) declaredConstructor.newInstance("Rob", "Stark", new ArrayList<>());
System.out.printf(newInstance2.toString());
Il risultato è un errore sulla privacy del nostro costruttore:
Java non è riuscito a creare un oggetto utilizzando questo costruttore, ma in realtà c'è qualcosa di magico che possiamo fare nel metodo principale . Possiamo il livello di accesso del nostro costruttore, rendendo possibile la creazione di oggetti della nostra classe:
declaredConstructor.setAccessible(true);
Risultato della creazione di un oggetto
Impiegato{nome='Rob', cognome='Stark', età=-1}
Non impostiamo l'età nel nostro costruttore, quindi rimane uguale a quando è stato inizializzato.
Meraviglioso, riassumiamo!
Vantaggi della creazione di oggetti utilizzando Constructor.newInstance()
Entrambi i metodi condividono lo stesso nome, ma ci sono differenze tra loro:
Classe.nuovaistanza() | Constructor.newInstance() |
---|---|
Può chiamare solo un costruttore no-arg . | Può chiamare qualsiasi costruttore indipendentemente dal numero di parametri. |
Richiede che il costruttore sia visibile. | Può anche chiamare costruttori privati in determinate circostanze. |
Genera qualsiasi eccezione (controllata o meno) dichiarata dal costruttore. | Fa sempre il wrapping di un'eccezione generata con InvocationTargetException . |
Per questi motivi, Constructor.newInstance() è preferito a Class.newInstance() , ed è il metodo utilizzato da vari framework e API come Spring, Guava, Zookeeper, Jackson, Servlet, ecc.
GO TO FULL VERSION