Exempel på att skapa ett objekt med Class.newInstance()

Föreställ dig att du får i uppdrag att skapa ett objekt med hjälp av reflektion. Ska vi börja?

Vi börjar med att skriva koden för klassen vi vill instansiera:


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

Det här kommer att vara vår klass — med flera fält, en konstruktor med parametrar, getters och sättare, en toString()- metod och ett initieringsblock. Låt oss nu gå vidare till den andra delen: skapa ett objekt med hjälp av reflektion. Det första tillvägagångssättet vi ska titta på kommer att använda 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());
    }
}

Excellent! Låt oss köra vår kod och se efter vilken ålder som ska visas. Men vi får ett felmeddelande om en saknad standardkonstruktor. Det visar sig att den här metoden bara låter oss få ett objekt skapat med en standardkonstruktor. Låt oss lägga till en standardkonstruktor för vår klass och testa koden igen.

Felmeddelande:

Koden för den nya konstruktören


public Employee() { }

Efter att ha lagt till konstruktorn, här är resultatet:

åldern är 1

Bra! Vi kom på hur den här metoden fungerar. Låt oss nu ta en titt under huven. När vi öppnar dokumentationen ser vi att vår metod redan är utfasad :

Det kan också kasta InstantiationException och IllegalAccessException . Följaktligen föreslår dokumentationen att vi använder det andra sättet att skapa ett objekt, nämligen Constructor.newInstance() . Låt oss analysera i detalj hur Constructor -klassen fungerar.

getConstructors och getDeclaredConstructors metoder

För att arbeta med Constructor -klassen måste vi först skaffa en instans. Vi har två metoder för detta: getConstructors och getDeclaredConstructors .

Den första returnerar en array av offentliga konstruktörer, och den andra returnerar en array av alla klasskonstruktörer.

Låt oss ge vår klass lite integritet, eller snarare, låt oss skapa några privata konstruktörer för att visa hur dessa metoder fungerar.

Låt oss lägga till några privata konstruktörer:


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

Titta på koden, notera att en av konstruktörerna är privat:

Låt oss testa våra 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);
	      }
	  }
}

Och vi får detta resultat:

getConstructors:
public com.codegym.Employee(java.lang.String,java.lang.String,int)
public.com.codegym.Employee()

getDeclaredConstructors:
privat com.codegym.Employee(java.lang.String,java.lang .String)
public com.codegym.Employee(java.lang.String,java.lang.String,int)
public com.codegym.Employee()

Okej, det är så här vi får tillgång till Constructor- objektet. Nu kan vi prata om vad den kan göra.

Klassen java.lang.reflect.Constructor och dess viktigaste metoder

Låt oss ta en titt på de viktigaste metoderna och hur de fungerar:

Metod Beskrivning
hämta namn() Returnerar namnet på denna konstruktor som en sträng.
getModifiers() Returnerar Java-åtkomstmodifierarna kodade som ett nummer.
getExceptionTypes() Returnerar en array av klassobjekt som representerar de typer av undantag som deklareras av konstruktorn.
getParameters() Returnerar en matris med parameterobjekt som representerar alla parametrar. Returnerar en matris med längden 0 om konstruktorn inte har några parametrar.
getParameterTypes() Returnerar en array av klassobjekt som representerar de formella parametertyperna i deklarationsordning.
getGenericParameterTypes() Returnerar en matris med Type-objekt som representerar de formella parametertyperna i deklarationsordning.

getName() & getModifiers()

Låt oss slå in vår array i en lista för att göra den bekväm att arbeta med. Vi kommer också att skriva getName och 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;
}

Och vår huvudmetod , där vi kommer att kalla allt:


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

Och nu ser vi all information vi vill ha:

Anställd klass:
Konstruktörer:
[privat com.codegym.Employee(java.lang.String), public
com.codegym.Employee(java.lang.String,java.lang.String,int), public com.codegym.Employee() ]
Modifierare:
[privat, offentlig, offentlig]

getExceptionTypes()

Denna metod låter oss få en uppsättning av undantagen som vår konstruktör kan kasta. Låt oss modifiera en av våra konstruktörer och skriva en ny metod.

Här ändrar vi vår nuvarande konstruktör något:


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

Och här har vi en metod för att få undantagstyper och lägga till den i 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);

Ovan fick vi tillgång till den första konstruktören i vår lista. Vi kommer att diskutera hur man skaffar en specifik konstruktör lite senare.

Och titta på utdata efter att vi lagt till kast. Undantag :

Undantagstyper:
[class java.lang.Exception]

Och innan du lägger till undantaget:

Undantagstyper:
[]

Allt är underbart, men hur ser vi vilka parametrar som krävs av våra konstruktörer? Låt oss ta reda på detta också.

getParameters() & getParameterTypes() & getGenericParameterTypes()

Låt oss börja igen med att förfina vår privata konstruktör. Nu kommer det se ut så här:


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

Och vi har ytterligare tre metoder: getParameters för att få ordningen på parametrar och deras typer, getParameterTypes för att få parametertyperna och getGenericParameterTypes för att få typer inslagna i 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()));
}

Och vi lägger till lite mer information till vår redan inte så lilla huvudmetod :


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 tittar på resultatet ser vi mycket detaljerad information om parametrarna för våra konstruktörer:

Konstruktorparametrar :
[java.lang.String arg0, java.lang.String arg1, java.util.List<java.lang.String> arg2]
Parametertyper :
[class java.lang.String, class java.lang.String, gränssnitt java.util.List]
Konstruktörsparametertyper:
[class java.lang.String, class java.lang.String, java.util.List<java.lang.String>]

Detta visar tydligt skillnaden mellan varje metod. Vi ser att vi har separata alternativ för att få information om parametertyper, inslagna typer och allt i allmänhet. Super! Nu när vi är bekanta med Constructor- klassen kan vi återgå till huvudämnet i vår artikel - att skapa objekt.

Skapa ett objekt med Constructor.newInstance()

Det andra sättet att skapa objekt är att anropa metoden newInstance på konstruktorn. Låt oss ta en titt på ett fungerande exempel och se hur vi kan få en viss konstruktör.

Om du vill skaffa en enskild konstruktor bör du använda metoden getConstructor (inte att förväxla med getConstructors , som returnerar en array av alla konstruktörer). Metoden getConstructor returnerar standardkonstruktorn .


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

Och om vi vill få en specifik konstruktor måste vi skicka konstruktorns parametertyper till den här metoden.

Glöm inte att vi bara kan få vår privata konstruktör med metoden getDeclaredConstructor .


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

Så här kan vi få en specifik konstruktör. Låt oss nu försöka skapa objekt med privata och offentliga 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 är ett objekt som vi kan arbeta med:

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

Allt fungerar utmärkt! Nu ska vi försöka 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 är ett fel om vår konstruktörs integritet:

Java kunde inte skapa ett objekt med denna konstruktor, men det finns faktiskt något magiskt vi kan göra i huvudmetoden . Vi kan vår konstruktörs åtkomstnivå, vilket gör det möjligt att skapa objekt i vår klass:


declaredConstructor.setAccessible(true);

Resultat av att skapa ett objekt

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

Vi ställer inte in åldern i vår konstruktor, så den förblir densamma som när den initierades.

Underbart, låt oss summera!

Fördelar med att skapa objekt med Constructor.newInstance()

Båda metoderna har samma namn, men det finns skillnader mellan dem:

Class.newInstance() Constructor.newInstance()
Kan bara anropa en no-arg konstruktor. Kan anropa vilken konstruktör som helst oavsett antalet parametrar.
Kräver att konstruktören är synlig. Kan även ringa privata konstruktörer under vissa omständigheter.
Kastar alla undantag (markerade eller inte) som deklareras av konstruktören. Omsluter alltid ett slängt undantag med InvocationTargetException .

Av dessa skäl är Constructor.newInstance() att föredra framför Class.newInstance() , och är metoden som används av olika ramverk och API:er som Spring, Guava, Zookeeper, Jackson, Servlet, etc.