Voorbeeld van het maken van een object met behulp van Class.newInstance()

Stel je voor dat je de opdracht krijgt om een ​​object te maken met behulp van reflectie. Zullen we aan de slag gaan?

We beginnen met het schrijven van de code voor de klasse die we willen instantiëren:

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 +
                '}';
    }
}

Dit wordt onze klasse — met verschillende velden, een constructor met parameters, getters en setters, een toString()- methode en een initialisatieblok. Laten we nu verder gaan met het tweede deel: een object maken met behulp van reflectie. De eerste benadering die we zullen bekijken, is 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());
    }
}

Uitstekend! Laten we onze code uitvoeren en kijken of de leeftijd wordt weergegeven. Maar we krijgen een foutmelding over een ontbrekende standaardconstructor. Het blijkt dat we met deze methode alleen een object kunnen maken dat is gemaakt met een standaardconstructor. Laten we een standaardconstructor voor onze klasse toevoegen en de code opnieuw testen.

Foutmelding:

Code van de nieuwe constructor

public Employee() { }

Na het toevoegen van de constructor is hier de uitvoer:

leeftijd is 1

Geweldig! We hebben uitgezocht hoe deze methode werkt. Laten we nu eens onder de motorkap kijken. Als we de documentatie openbreken, zien we dat onze methode al verouderd is :

Het kan ook InstantiationException en IllegalAccessException genereren . Dienovereenkomstig suggereert de documentatie dat we de andere manier gebruiken om een ​​object te maken, namelijk Constructor.newInstance() . Laten we in detail analyseren hoe de klasse Constructor werkt.

methoden getConstructors en getDeclaredConstructors

Om met de klasse Constructor te kunnen werken , moeten we eerst een instantie verkrijgen. We hebben hiervoor twee methoden: getConstructors en getDeclaredConstructors .

De eerste retourneert een array van openbare constructors en de tweede retourneert een array van alle klassenconstructors.

Laten we onze klas wat privacy geven, of liever, laten we een aantal privé-constructors maken om te helpen demonstreren hoe deze methoden werken.

Laten we enkele privé-constructors toevoegen:

private Employee(String name, String surname) {
    this.name = name;
    this.lastName = lastName;
}

Kijkend naar de code, merk op dat een van de constructors privé is:

Laten we onze methoden testen:

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);
	      }
	  }
}

En we krijgen dit resultaat:

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

Oké, zo krijgen we toegang tot het Constructor- object. Nu kunnen we praten over wat het kan doen.

De klasse java.lang.reflect.Constructor en zijn belangrijkste methoden

Laten we eens kijken naar de belangrijkste methoden en hoe ze werken:

Methode Beschrijving
getNaam() Retourneert de naam van deze constructor als een tekenreeks.
getModifiers() Retourneert de Java-toegangsmodificatoren gecodeerd als een getal.
getExceptionTypes() Retourneert een array van Class-objecten die de typen uitzonderingen vertegenwoordigen die door de constructor zijn gedeclareerd.
getParameters() Retourneert een array van parameterobjecten die alle parameters vertegenwoordigen. Retourneert een array met lengte 0 als de constructor geen parameters heeft.
getParameterTypes() Retourneert een array van Class-objecten die de formele parametertypen in declaratievolgorde vertegenwoordigen.
getGenericParameterTypes() Retourneert een array van Type-objecten die de formele parametertypen in declaratievolgorde vertegenwoordigen.

getName() & getModifiers()

Laten we onze array in een lijst verpakken om het gemakkelijk te maken om mee te werken. We schrijven ook de methoden getName en 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;
}

En onze belangrijkste methode, waar we alles zullen noemen:

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);
}

En nu zien we alle informatie die we willen:

Werknemersklasse:
Constructors:
[private com.codegym.Employee(java.lang.String), public
com.codegym.Employee(java.lang.String,java.lang.String,int), public com.codegym.Employee() ]
Modifiers:
[privé, openbaar, openbaar]

getExceptionTypes()

Met deze methode kunnen we een reeks uitzonderingen krijgen die onze constructor kan genereren. Laten we een van onze constructors wijzigen en een nieuwe methode schrijven.

Hier veranderen we onze huidige constructor enigszins:

private Employee(String name, String surname) throws Exception {
    this.name = name;
    this.lastName = lastName;
}

En hier hebben we een methode om uitzonderingstypen te krijgen en toe te voegen aan 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);

Hierboven hebben we toegang gekregen tot de eerste constructor in onze lijst. We zullen later bespreken hoe je een specifieke constructor kunt krijgen.

En kijk naar de uitvoer nadat we worpen hebben toegevoegd Uitzondering :

Uitzonderingstypes:
[class java.lang.Exception]

En voordat u de uitzondering toevoegt:

Uitzonderingstypen:
[]

Alles is geweldig, maar hoe zien we welke parameters onze constructeurs nodig hebben? Laten we dit ook uitzoeken.

getParameters() & getParameterTypes() & getGenericParameterTypes()

Laten we opnieuw beginnen met het verfijnen van onze privéconstructeur. Nu zal het er zo uitzien:

private Employee(String name, String surname, List<String> list) {
    this.name = name;
    this.lastName = lastName;
}

En we hebben drie extra methoden: getParameters voor het ophalen van de volgorde van parameters en hun typen, getParameterTypes voor het ophalen van de parametertypen en getGenericParameterTypes voor het ophalen van typen verpakt in generieke gegevens .

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()));
}

En we voegen wat meer informatie toe aan onze toch al niet zo kleine hoofdmethode :

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);

Als we naar de uitvoer kijken, zien we zeer gedetailleerde informatie over de parameters van onze constructeurs:

Constructorparameters:
[java.lang.String arg0, java.lang.String arg1, java.util.List<java.lang.String> arg2]
Parametertypen:
[class java.lang.String, class java.lang.String, interface java.util.List]
Constructorparametertypen:
[class java.lang.String, class java.lang.String, java.util.List<java.lang.String>]

Dit toont duidelijk het verschil aan tussen elke methode. We zien dat we afzonderlijke opties hebben om informatie te krijgen over parametertypen, ingepakte typen en alles in het algemeen. Super! Nu we bekend zijn met de klasse Constructor , kunnen we terugkeren naar het hoofdonderwerp van ons artikel: objecten maken.

Een object maken met Constructor.newInstance()

De tweede manier om objecten te maken, is door de methode newInstance op de constructor aan te roepen. Laten we een werkend voorbeeld bekijken en kijken hoe we een bepaalde constructor kunnen krijgen.

Als u een enkele constructor wilt hebben, moet u de getConstructor- methode gebruiken (niet te verwarren met getConstructors , die een array van alle constructors retourneert). De methode getConstructor retourneert de standaardconstructor.

public static void main(String[] args) throws NoSuchMethodException {
    Class employeeClass = Employee.class;
    Constructor<?> employeeConstructor = employeeClass.getConstructor();
    System.out.println(employeeConstructor);
}
openbare com.codegym.Employee()

En als we een specifieke constructor willen krijgen, moeten we de parametertypen van de constructor doorgeven aan deze methode.

Vergeet niet dat we onze private constructor alleen kunnen krijgen met behulp van de methode getDeclaredConstructor .

Constructor<?> employeeConstructor2 = employeeClass.getDeclaredConstructor(String.class, String.class, List.class);
System.out.println(employeeConstructor2);

Dit is hoe we een specifieke constructor kunnen krijgen. Laten we nu proberen objecten te maken met behulp van private en publieke constructors.

Openbare aannemer:

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);

Het resultaat is een object waarmee we kunnen werken:

public com.codegym.Employee(java.lang.String,java.lang.String,int)
Employee{name='Rob' achternaam='Stark', leeftijd=10}

Alles werkt geweldig! Nu zullen we het proberen met een privéconstructeur:

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());

Het resultaat is een fout over de privacy van onze constructor:

Java kon met deze constructor geen object maken, maar er is eigenlijk iets magisch dat we kunnen doen in de hoofdmethode . We kunnen het toegangsniveau van onze constructeur bepalen, waardoor het mogelijk wordt om objecten van onze klasse te maken:

declaredConstructor.setAccessible(true);

Resultaat van het maken van een object

privé com.codegym.Employee(java.lang.String,java.lang.String,java.util.List)
Werknemer{naam='Rob', achternaam='Stark', leeftijd=-1}

We stellen de leeftijd niet in onze constructor in, dus deze blijft hetzelfde als toen deze werd geïnitialiseerd.

Geweldig, laten we het samenvatten!

Voordelen van het maken van objecten met Constructor.newInstance()

Beide methoden hebben dezelfde naam, maar er zijn verschillen:

Klasse.newInstance() Constructor.newInstance()
Kan alleen een no-arg- constructor aanroepen. Kan elke constructor aanroepen, ongeacht het aantal parameters.
Vereist dat de constructor zichtbaar is. Kan onder bepaalde omstandigheden ook particuliere aannemers bellen.
Gooit elke uitzondering (aangevinkt of niet) die door de constructor is gedeclareerd. Wikkelt een gegenereerde uitzondering altijd in met InvocationTargetException .

Om deze redenen heeft Constructor.newInstance() de voorkeur boven Class.newInstance() en is het de methode die wordt gebruikt door verschillende frameworks en API's zoals Spring, Guava, Zookeeper, Jackson, Servlet, enz.