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

これが私たちのクラスになります。いくつかのフィールド、パラメーターを持つコンストラクター、ゲッターとセッター、toString()メソッド、および初期化ブロックがあります。次に、2 番目の部分、つまりリフレクションを使用したオブジェクトの作成に進みましょう。最初に説明するアプローチでは、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です

素晴らしい!私たちはこの方法がどのように機能するかを理解しました。ボンネットの下を見てみましょう。ドキュメントを開いてみると、私たちのメソッドはすでに非推奨になっていることがわかります。

また、 InstantiationExceptionおよびIllegalAccessExceptionをスローすることもあります。したがって、ドキュメントでは、オブジェクトを作成する別の方法、つまりConstructor.newInstance()を使用することを提案しています。Constructorクラスがどのように機能するかを詳しく分析してみましょう。

getConstructors メソッドと getDeclaredConstructors メソッド

Constructorクラスを操作するには、まずインスタンスを取得する必要があります。これには、 getConstructorsgetDeclaredConstructors という2 つのメソッドがあります。

1 つ目はパブリック コンストラクターの配列を返し、2 つ目はすべてのクラス コンストラクターの配列を返します。

クラスにプライバシーを与えましょう。むしろ、これらのメソッドがどのように機能するかを示すためにいくつかのプライベート コンストラクターを作成しましょう。

いくつかのプライベート コンストラクターを追加しましょう。


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

コードを見ると、コンストラクターの 1 つがプライベートであることに注意してください。

メソッドをテストしてみましょう。


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

さて、これがConstructorオブジェクトにアクセスする方法です。ここで、それが何ができるかについて話しましょう。

java.lang.reflect.Constructor クラスとその最も重要なメソッド

最も重要なメソッドとその仕組みを見てみましょう。

方法 説明
getName() このコンストラクターの名前を文字列として返します。
getModifiers() 数値としてエンコードされた Java アクセス修飾子を返します。
getExceptionTypes() コンストラクターによって宣言された例外のタイプを表す Class オブジェクトの配列を返します。
getパラメータ() すべてのパラメータを表すParameterオブジェクトの配列を返します。コンストラクターにパラメーターがない場合は、長さ 0 の配列を返します。
getParameterTypes() 仮パラメータの型を宣言順に表す Class オブジェクトの配列を返します。
getGenericParameterTypes() 仮パラメータの型を宣言順に表す Type オブジェクトの配列を返します。

getName() と getModifiers()

操作しやすいように、配列をListでラップしましょう。getNameメソッドと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;
}

そして、すべてを呼び出すメインメソッド:


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

このメソッドを使用すると、コンストラクターがスローする可能性のある例外の配列を取得できます。コンストラクターの 1 つを変更して、新しいメソッドを作成しましょう。

ここで、現在のコンストラクターを少し変更します。


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 を追加した後の出力を見てください。

例外の種類:
[クラス java.lang.Exception]

例外を追加する前に、次のようにします。

例外の種類:
[]

すべてが素晴らしいですが、コンストラクターに必要なパラメーターをどのように確認すればよいでしょうか? これも解いてみましょう。

getParameters() & getParameterTypes() & getGenericParameterTypes()

プライベート コンストラクターを改良することからもう一度始めましょう。これで、次のようになります。


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

さらに、パラメーターの順序とその型を取得するgetParameters 、パラメーターの型を取得するgetParameterTypes 、およびジェネリックスでラップされた型を取得するgetGenericParameterTypesの 3 つの追加メソッドがあります。


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

そして、すでにそれほど小さくないmainメソッドにさらに情報を追加します。


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,インタフェース java.util.List]
コンストラクタパラメータの型:
[クラス java.lang.String, クラス java.lang.String, java.util.List<java.lang.String>]

これは、各方法の違いを明確に示しています。パラメーターの型、ラップされた型、その他一般的なすべてに関する情報を取得するための個別のオプションがあることがわかります。素晴らしい!Constructorクラスについて理解したので、記事のメイントピックであるオブジェクトの作成に戻りましょう。

Constructor.newInstance() を使用したオブジェクトの作成

オブジェクトを作成する 2 番目の方法は、コンストラクターで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);

結果として、操作できるオブジェクトが得られます。

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

オブジェクトの作成結果

private com.codegym.Employee(java.lang.String,java.lang.String,java.util.List)
従業員{名前='ロブ'、姓='スターク'、年齢=-1}

コンストラクターでは年齢を設定しないため、初期化されたときと同じままになります。

素晴らしいですね、まとめましょう!

Constructor.newInstance() を使用してオブジェクトを作成する利点

どちらのメソッドも同じ名前を共有しますが、それらの間には次のような違いがあります。

Class.newInstance() Constructor.newInstance()
引数なしのコンストラクターのみを呼び出すことができます。 パラメーターの数に関係なく、任意のコンストラクターを呼び出すことができます。
コンストラクターが表示される必要があります。 特定の状況下ではプライベート コンストラクターを呼び出すこともできます。
コンストラクターによって宣言された例外 (チェックされているかどうかにかかわらず) をスローします。 スローされた例外は常にInvocationTargetExceptionでラップされます。

これらの理由から、Constructor.newInstance()はClass.newInstance()よりも好まれており、Spring、Guava、Zookeeper、Jackson、Servlet などのさまざまなフレームワークや API で使用されるメソッドです。