Class.newInstance()를 사용하여 객체를 생성하는 예

리플렉션을 사용하여 객체를 생성하도록 할당되었다고 상상해 보십시오. 시작할까요?

인스턴스화하려는 클래스의 코드를 작성하는 것으로 시작하겠습니다.

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

이것은 여러 필드, 매개 변수가 있는 생성자, getter 및 setter, toString() 메서드 및 초기화 블록이 포함된 클래스가 됩니다 . 이제 두 번째 부분인 리플렉션을 사용하여 객체를 생성해 보겠습니다. 우리가 살펴볼 첫 번째 접근 방식은 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());
    }
}

훌륭한! 코드를 실행하고 나이가 표시되는지 살펴보겠습니다. 그러나 누락된 기본 생성자에 대한 오류가 발생합니다. 이 방법을 사용하면 기본 생성자를 사용하여 생성된 개체만 가져올 수 있습니다. 클래스의 기본 생성자를 추가하고 코드를 다시 테스트해 보겠습니다.

에러 메시지:

새 생성자의 코드

public Employee() { }

생성자를 추가한 후 출력은 다음과 같습니다.

나이는 1

엄청난! 우리는 이 방법이 어떻게 작동하는지 알아냈습니다. 이제 후드 아래를 살펴 보겠습니다. 문서를 열면 우리의 방법이 이미 사용되지 않는 것을 볼 수 있습니다 .

InstantiationExceptionIllegalAccessException 을 던질 수도 있습니다 . 따라서 문서에서는 개체를 만드는 다른 방법인 Constructor.newInstance()를 사용할 것을 제안합니다 . Constructor 클래스가 어떻게 작동하는지 자세히 분석해 보겠습니다 .

getConstructors 및 getDeclaredConstructors 메서드

Constructor 클래스 로 작업하려면 먼저 인스턴스를 가져와야 합니다. 이를 위한 두 가지 방법이 있습니다: getConstructorsgetDeclaredConstructors .

첫 번째는 공용 생성자의 배열을 반환하고 두 번째는 모든 클래스 생성자의 배열을 반환합니다.

클래스에 약간의 프라이버시를 부여하거나 이러한 메서드가 어떻게 작동하는지 보여주기 위해 개인 생성자를 만들어 봅시다.

일부 개인 생성자를 추가해 보겠습니다.

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

코드를 보면 생성자 중 하나가 비공개라는 점에 유의하십시오.

방법을 테스트해 보겠습니다.

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

결과는 다음과 같습니다.

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

getDeclaredConstructors:
비공개 com.codegym.Employee(java.lang.String,java.lang .String)
공개 com.codegym.Employee(java.lang.String,java.lang.String,int)
공개 com.codegym.Employee()

좋아요, 이것이 우리가 Constructor 객체 에 접근하는 방법입니다 . 이제 우리는 그것이 무엇을 할 수 있는지에 대해 이야기할 수 있습니다.

java.lang.reflect.Constructor 클래스 및 가장 중요한 메서드

가장 중요한 방법과 작동 방식을 살펴보겠습니다.

방법 설명
getName() 이 생성자의 이름을 문자열로 반환합니다.
getModifiers() 숫자로 인코딩된 Java 액세스 한정자를 반환합니다.
getExceptionTypes() 생성자가 선언한 예외 유형을 나타내는 Class 객체의 배열을 반환합니다.
getParameters() 모든 매개변수를 나타내는 매개변수 객체 의 배열을 반환합니다 . 생성자에 매개변수가 없으면 길이가 0인 배열을 반환합니다.
getParameterTypes() 형식 매개변수 유형을 선언 순서로 나타내는 Class 객체의 배열을 반환합니다.
getGenericParameterTypes() 형식 매개변수 유형을 선언 순서로 나타내는 Type 객체의 배열을 반환합니다.

getName() & getModifiers()

작업하기 편리하도록 배열을 List 로 래핑하겠습니다 . getNamegetModifiers 메소드 도 작성합니다 .

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

그리고 우리 가 모든 것을 부를 주요 방법은 다음과 같습니다.

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

이제 원하는 모든 정보를 볼 수 있습니다.

직원 클래스:
생성자:
[private com.codegym.Employee(java.lang.String), public
com.codegym.Employee(java.lang.String,java.lang.String,int), public com.codegym.Employee() ]
수정자 :
[비공개, 공개, 공개]

getExceptionTypes()

이 메소드를 사용하면 생성자가 던질 수 있는 예외의 배열을 얻을 수 있습니다. 생성자 중 하나를 수정하고 새 메서드를 작성해 보겠습니다.

여기에서 현재 생성자를 약간 변경합니다.

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

여기에 예외 유형을 가져오고 이를 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);

위에서 목록의 첫 번째 생성자에 액세스했습니다. 나중에 특정 생성자를 얻는 방법에 대해 논의할 것입니다.

그리고 throws Exception 을 추가한 후의 출력을 살펴보십시오 .

예외 유형:
[class java.lang.Exception]

그리고 예외를 추가하기 전에:

예외 유형:
[]

모든 것이 훌륭하지만 생성자에 어떤 매개변수가 필요한지 어떻게 알 수 있습니까? 이것도 알아봅시다.

getParameters() & getParameterTypes() & getGenericParameterTypes()

개인 생성자를 개선하여 다시 시작하겠습니다. 이제 다음과 같이 표시됩니다.

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

매개 변수의 순서와 유형을 가져오는 getParameters , 매개변수 유형을 가져오는 getParameterTypes , 제네릭 으로 래핑된 유형을 가져오는 getGenericParameterTypes 의 세 가지 추가 메서드가 있습니다 .

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

그리고 우리는 아직 작지 않은 기본 메서드에 정보를 더 추가합니다.

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

출력을 보면 생성자의 매개변수에 대한 매우 자세한 정보를 볼 수 있습니다.

생성자 매개변수:
[java.lang.String arg0, java.lang.String arg1, java.util.List<java.lang.String> arg2]
매개변수 유형:
[class java.lang.String, class java.lang.String, interface java.util.List]
생성자 매개변수 유형:
[class java.lang.String, class java.lang.String, java.util.List<java.lang.String>]

이것은 각 방법의 차이점을 명확하게 보여줍니다. 매개변수 유형, 래핑된 유형 및 일반적으로 모든 것에 대한 정보를 얻을 수 있는 별도의 옵션이 있음을 알 수 있습니다. 감독자! 이제 Constructor 클래스 에 익숙해졌으므로 기사의 주요 주제인 객체 생성으로 돌아갈 수 있습니다.

Constructor.newInstance()를 사용하여 객체 생성

객체를 만드는 두 번째 방법은 생성자에서 newInstance 메서드를 호출하는 것입니다. 작업 예제를 살펴보고 특정 생성자를 얻는 방법을 살펴보겠습니다.

단일 생성자를 얻으려면 getConstructor 메서드를 사용해야 합니다( 모든 생성자의 배열을 반환하는 getConstructors 와 혼동하지 마십시오 ). getConstructor 메서드 는 기본 생성자를 반환합니다.

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

그리고 특정 생성자를 얻으려면 생성자의 매개 변수 유형을 이 메서드에 전달해야 합니다.

getDeclaredConstructor 메서드 를 사용해야만 개인 생성자를 가져올 수 있다는 점을 잊지 마십시오 .

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

이것이 특정 생성자를 얻는 방법입니다. 이제 개인 및 공용 생성자를 사용하여 개체를 만들어 보겠습니다.

공개 생성자:

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

결과는 우리가 작업할 수 있는 개체입니다.

공개 com.codegym.Employee(java.lang.String,java.lang.String,int)
직원{이름='롭' 성='스타크', 나이=10}

모든 것이 훌륭하게 작동합니다! 이제 개인 생성자로 시도해 보겠습니다.

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

결과는 생성자의 프라이버시에 대한 오류입니다.

Java는 이 생성자를 사용하여 객체를 생성할 수 없지만 실제로 main 메서드 에서 수행할 수 있는 마법 같은 작업이 있습니다 . 우리는 생성자의 액세스 수준을 사용하여 클래스의 개체를 만들 수 있습니다.

declaredConstructor.setAccessible(true);

객체 생성 결과

개인 com.codegym.Employee(java.lang.String,java.lang.String,java.util.List)
Employee{name='Rob', 성='Stark', age=-1}

우리는 생성자에서 나이를 설정하지 않으므로 초기화되었을 때와 동일하게 유지됩니다.

훌륭합니다. 요약하자면!

Constructor.newInstance()를 사용하여 객체를 생성할 때의 이점

두 방법 모두 이름이 같지만 차이점이 있습니다.

Class.newInstance() 생성자.newInstance()
인수가 없는 생성자 만 호출할 수 있습니다 . 매개변수 수에 관계없이 모든 생성자를 호출할 수 있습니다.
생성자가 표시되어야 합니다. 특정 상황에서 전용 생성자를 호출할 수도 있습니다.
생성자에 의해 선언된 모든 예외(확인 여부)를 throw합니다. 항상 InvocationTargetException 으로 throw된 예외를 래핑합니다 .

이러한 이유로 Constructor.newInstance() 는 Class.newInstance() 보다 선호되며 Spring, Guava, Zookeeper, Jackson, Servlet 등과 같은 다양한 프레임워크 및 API에서 사용되는 메서드입니다.