Eksempel på oprettelse af et objekt ved hjælp af Class.newInstance()

Forestil dig, at du får tildelt at skabe et objekt ved hjælp af refleksion. Skal vi i gang?

Vi starter med at skrive koden til den klasse, vi vil instansiere:


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

Dette vil være vores klasse - med flere felter, en konstruktør med parametre, gettere og sættere, en toString()- metode og en initialiseringsblok. Lad os nu gå videre til den anden del: skabe et objekt ved hjælp af refleksion. Den første tilgang, vi vil se på, vil bruge 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());
    }
}

Fremragende! Lad os køre vores kode og se efter alderen, der skal vises. Men vi får en fejl om en manglende standardkonstruktør. Det viser sig, at denne metode kun lader os få et objekt oprettet ved hjælp af en standardkonstruktør. Lad os tilføje en standardkonstruktør til vores klasse og teste koden igen.

Fejl besked:

Kode for den nye konstruktør


public Employee() { }

Efter tilføjelse af konstruktøren, her er outputtet:

alderen er 1

Store! Vi fandt ud af, hvordan denne metode fungerer. Lad os nu tage et kig under motorhjelmen. Når vi åbner dokumentationen, ser vi, at vores metode allerede er forældet :

Det kan også kaste InstantiationException og IllegalAccessException . Følgelig foreslår dokumentationen, at vi bruger den anden måde at skabe et objekt på, nemlig Constructor.newInstance() . Lad os analysere i detaljer, hvordan Constructor- klassen fungerer.

getConstructors og getDeclaredConstructors metoder

For at arbejde med Constructor- klassen skal vi først have en instans. Vi har to metoder til dette: getConstructors og getDeclaredConstructors .

Den første returnerer en matrix af offentlige konstruktører, og den anden returnerer en matrix af alle klassekonstruktører.

Lad os give vores klasse noget privatliv, eller rettere, lad os skabe nogle private konstruktører for at hjælpe med at demonstrere, hvordan disse metoder fungerer.

Lad os tilføje nogle private konstruktører:


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

Når du ser på koden, skal du bemærke, at en af ​​konstruktørerne er privat:

Lad os teste vores metoder:


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

Og vi får dette resultat:

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

Okay, det er sådan, vi får adgang til Constructor- objektet. Nu kan vi tale om, hvad det kan.

Java.lang.reflect.Constructor-klassen og dens vigtigste metoder

Lad os tage et kig på de vigtigste metoder, og hvordan de virker:

Metode Beskrivelse
getName() Returnerer navnet på denne konstruktør som en streng.
getModifiers() Returnerer Java-adgangsmodifikatorerne kodet som et tal.
getExceptionTypes() Returnerer en matrix af klasseobjekter, der repræsenterer de typer undtagelser, der er erklæret af konstruktøren.
getParameters() Returnerer en matrix af parameterobjekter , der repræsenterer alle parametrene. Returnerer en matrix med længden 0, hvis konstruktøren ikke har nogen parametre.
getParameterTypes() Returnerer en matrix af klasseobjekter, der repræsenterer de formelle parametertyper i deklarationsrækkefølge.
getGenericParameterTypes() Returnerer en matrix af Type-objekter, der repræsenterer de formelle parametertyper i deklarationsrækkefølge.

getName() & getModifiers()

Lad os samle vores array i en liste for at gøre det praktisk at arbejde med. Vi vil også skrive getName og getModifiers metoder:


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

Og vores hovedmetode , hvor vi vil kalde alt:


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

Og nu ser vi alle de oplysninger, vi ønsker:

Medarbejderklasse:
Konstruktører:
[privat com.codegym.Employee(java.lang.String), public
com.codegym.Employee(java.lang.String,java.lang.String,int), public com.codegym.Employee() ]
Modifikatorer :
[privat, offentlig, offentlig]

getExceptionTypes()

Denne metode giver os mulighed for at få en række undtagelser, som vores konstruktør kan kaste. Lad os ændre en af ​​vores konstruktører og skrive en ny metode.

Her ændrer vi vores nuværende konstruktør lidt:


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

Og her har vi en metode til at få undtagelsestyper og tilføje den til 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);

Ovenfor fik vi adgang til den første konstruktør på vores liste. Vi vil diskutere, hvordan man får en bestemt konstruktør lidt senere.

Og se på outputtet efter vi har tilføjet kast Undtagelse :

Undtagelsestyper :
[class java.lang.Exception]

Og før du tilføjer undtagelsen:

Undtagelsestyper:
[]

Alt er vidunderligt, men hvordan kan vi se, hvilke parametre der kræves af vores konstruktører? Lad os også finde ud af dette.

getParameters() & getParameterTypes() & getGenericParameterTypes()

Lad os starte igen med at forfine vores private konstruktør. Nu kommer det til at se sådan ud:


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

Og vi har tre yderligere metoder: getParameters til at få rækkefølgen af ​​parametre og deres typer, getParameterTypes til at få parametertyperne og getGenericParameterTypes til at få typer pakket ind i generiske artikler .


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

Og vi tilføjer nogle flere oplysninger til vores i forvejen ikke så lille hovedmetode :


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

Når vi ser på outputtet, ser vi meget detaljerede oplysninger om parametrene for vores konstruktører:

Konstruktorparametre :
[java.lang.String arg0, java.lang.String arg1, java.util.List<java.lang.String> arg2]
Parametertyper :
[class java.lang.String, class java.lang.String, interface java.util.List]
Konstruktør-parametertyper:
[class java.lang.String, class java.lang.String, java.util.List<java.lang.String>]

Dette viser tydeligt forskellen mellem hver metode. Vi ser, at vi har separate muligheder for at få information om parametertyper, indpakkede typer og alt generelt. Super! Nu hvor vi er bekendt med Constructor- klassen, kan vi vende tilbage til hovedemnet i vores artikel - at skabe objekter.

Oprettelse af et objekt ved hjælp af Constructor.newInstance()

Den anden måde at oprette objekter på er at kalde metoden newInstance på konstruktøren. Lad os tage et kig på et fungerende eksempel og se, hvordan vi kan få en bestemt konstruktør.

Hvis du ønsker at få en enkelt konstruktør, skal du bruge getConstructor metoden (ikke at forveksle med getConstructors , som returnerer en matrix af alle konstruktørerne). GetConstructor - metoden returnerer standardkonstruktøren.


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

Og hvis vi vil have en bestemt konstruktør, skal vi videregive konstruktørens parametertyper til denne metode.

Glem ikke, at vi kun kan få vores private konstruktør ved hjælp af getDeclaredConstructor -metoden.


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

Sådan kan vi få en bestemt konstruktør. Lad os nu prøve at skabe objekter ved hjælp af private og offentlige konstruktører.

Offentlig konstruktør:


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

Resultatet er et objekt, som vi kan arbejde med:

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

Alt fungerer fantastisk! Nu prøver vi med en privat konstruktør:


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

Resultatet er en fejl om privatlivets fred for vores konstruktør:

Java kunne ikke oprette et objekt ved hjælp af denne konstruktør, men der er faktisk noget magisk, vi kan gøre i hovedmetoden . Vi kan vores konstruktørs adgangsniveau, hvilket gør det muligt at oprette objekter fra vores klasse:


declaredConstructor.setAccessible(true);

Resultat af oprettelse af et objekt

privat com.codegym.Employee(java.lang.String,java.lang.String,java.util.List)
Medarbejder{name='Rob', surname='Stark', age=-1}

Vi indstiller ikke alderen i vores konstruktør, så den forbliver den samme, som da den blev initialiseret.

Vidunderligt, lad os opsummere!

Fordele ved at oprette objekter ved hjælp af Constructor.newInstance()

Begge metoder har samme navn, men der er forskelle mellem dem:

Class.newInstance() Constructor.newInstance()
Kan kun kalde en no-arg konstruktør. Kan kalde enhver konstruktør uanset antallet af parametre.
Kræver, at konstruktøren er synlig. Kan også ringe til private konstruktører under visse omstændigheder.
Kaster enhver undtagelse (markeret eller ej), der er erklæret af konstruktøren. Omslutter altid en smidt undtagelse med InvocationTargetException .

Af disse grunde foretrækkes Constructor.newInstance() frem for Class.newInstance() , og er den metode, der bruges af forskellige frameworks og API'er såsom Spring, Guava, Zookeeper, Jackson, Servlet osv.