CodeGym /Corsi /All lectures for IT purposes /Creare oggetti con la riflessione

Creare oggetti con la riflessione

All lectures for IT purposes
Livello 1 , Lezione 81
Disponibile

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:

l'età è 1

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:

getConstructors:
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:

Classe dipendente:
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 :

Tipi di eccezione:
[classe java.lang.Exception]

E prima di aggiungere l'eccezione:

Tipi di 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:

Parametri del costruttore:
[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);
}
public com.codegym.Employee()

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:

public com.codegym.Employee(java.lang.String,java.lang.String,int)
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

privato com.codegym.Employee(java.lang.String,java.lang.String,java.util.List)
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.

Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION