階層の深いインスタンスを自動生成の実装方法をChatGPTに聞いてみた

[自作したプログラム](https://qiita.com/flip-flop/items/e6047d75239b83bb2f23)と比較してみた。10秒、というか質問を送信した直後からずらずらとソースが出力されました。
# はじめに
ChatGPTに以下の質問をした。

* 1回目、階層のあるクラスを渡してランダムな値のインスタンスを生成してください。
* 2回目、階層の深い任意のクラス定義からランダムな値のインスタンスを生成してください。
# 1回目の結果

▼ 渡したクラス定義

```Java
@Data
class Person {
 String name;
 List<Pet> pets;
}

@Data
class Pet {
 List<Cat> cats;
 List<Dog> dogs;
}

@Data
class Cat {
 int id;
 String name;
 double age;
}

@Data
class Dog {
 int id;
 String name;
 double age;
}
```
▼ 出力されたコード
```Java
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class RandomExample {

  private static Random random = new Random();

  public static void main(String[] args) {

    // Person インスタンスの生成
    Person person = new Person();
    person.setName("John");
    person.setPets(generatePets());

    System.out.println(person.toString());
  }

  private static List<Pet> generatePets() {
    List<Pet> pets = new ArrayList<>();

    // Pet インスタンスの生成
    Pet pet = new Pet();
    pet.setCats(generateCats());
    pet.setDogs(generateDogs());

    pets.add(pet);
    return pets;
  }

  private static List<Cat> generateCats() {
    List<Cat> cats = new ArrayList<>();

    // Cat インスタンスの生成
    Cat cat = new Cat();
    cat.setId(random.nextInt(100));
    cat.setName("Cat" + random.nextInt(100));
    cat.setAge(random.nextDouble() * 10);

    cats.add(cat);
    return cats;
  }

  private static List<Dog> generateDogs() {
    List<Dog> dogs = new ArrayList<>();

    // Dog インスタンスの生成
    Dog dog = new Dog();
    dog.setId(random.nextInt(100));
    dog.setName("Dog" + random.nextInt(100));
    dog.setAge(random.nextDouble() * 10);

    dogs.add(dog);
    return dogs;
  }
}


```

# 2回目の結果
今度はクラス定義は渡さずに「階層の深い任意のクラス定義からランダムな値のインスタンスを生成するプログラムを教えて」と質問してみた。

```Java

階層の深いクラス定義からランダムな値のインスタンスを生成するためには、
反射（reflection）を使う方法があります。
以下は、反射を使ってランダムな値のインスタンスを生成するプログラムの例です。
まず、ランダムな値を生成するためのメソッドを定義します。

import java.util.Random;

public class RandomUtil {
    private static final Random random = new Random();

    public static int nextInt(int bound) {
        return random.nextInt(bound);
    }

    public static double nextDouble() {
        return random.nextDouble();
    }

    public static boolean nextBoolean() {
        return random.nextBoolean();
    }

    public static String nextString(int length) {
        StringBuilder sb = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            char c = (char) ('a' + random.nextInt(26));
            sb.append(c);
        }
        return sb.toString();
    }
}
```
```Java
次に、階層の深いクラス定義を作成します。以下は例として、3段階のクラス定義を作成したものです。

public class Example {
    public static void main(String[] args) throws Exception {
        Object instance = createInstance(Root.class);
        System.out.println(instance.toString());
    }

    private static <T> T createInstance(Class<T> clazz) throws Exception {
        T instance = clazz.getDeclaredConstructor().newInstance();
        for (java.lang.reflect.Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true);
            Class<?> fieldType = field.getType();
            if (fieldType.equals(int.class) || fieldType.equals(Integer.class)) {
                field.set(instance, RandomUtil.nextInt(100));
            } else if (fieldType.equals(double.class) || fieldType.equals(Double.class)) {
                field.set(instance, RandomUtil.nextDouble());
            } else if (fieldType.equals(boolean.class) || fieldType.equals(Boolean.class)) {
                field.set(instance, RandomUtil.nextBoolean());
            } else if (fieldType.equals(String.class)) {
                int length = RandomUtil.nextInt(10) + 1;
                field.set(instance, RandomUtil.nextString(length));
            } else {
                Object fieldValue = createInstance(fieldType);
                field.set(instance, fieldValue);
            }
        }
        return instance;
    }
}

class Root {
    private String name;
    private Branch1 branch1;

    public String toString() {
        return String.format("Root{name=%s, branch1=%s}", name, branch1);
    }
}

class Branch1 {
    private int id;
    private Leaf leaf;

    public String toString() {
        return String.format("Branch1{id=%d, leaf=%s}", id, leaf);
    }
}

class Leaf {
    private double value;
    private boolean flag;

    public String toString() {
        return String.format("Leaf{value=%f, flag=%s}", value, flag);
    }
}
```
# 少し手を加えて実行してみた

```java
package com.example.demo.controller;

import java.util.Random;
import lombok.Data;

public class ChatGpt {
    public static void main(String[] args) throws Exception {
        Object instance = createInstance(Root.class);
        System.out.println(instance.toString());
    }

    private static <T> T createInstance(Class<T> clazz) throws Exception {
        T instance = clazz.getDeclaredConstructor().newInstance();
        for (java.lang.reflect.Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true);
            Class<?> fieldType = field.getType();
            if (fieldType.equals(int.class) || fieldType.equals(Integer.class)) {
                field.set(instance, RandomUtil.nextInt(100));
            } else if (fieldType.equals(double.class) || fieldType.equals(Double.class)) {
                field.set(instance, RandomUtil.nextDouble());
            } else if (fieldType.equals(boolean.class) || fieldType.equals(Boolean.class)) {
                field.set(instance, RandomUtil.nextBoolean());
            } else if (fieldType.equals(String.class)) {
                int length = RandomUtil.nextInt(10) + 1;
                field.set(instance, RandomUtil.nextString(length));
            } else {
                Object fieldValue = createInstance(fieldType);
                field.set(instance, fieldValue);
            }
        }
        return instance;
    }

    private static class RandomUtil {
        private static final Random random = new Random();

        public static int nextInt(int bound) {
            return random.nextInt(bound);
        }

        public static double nextDouble() {
            return random.nextDouble();
        }

        public static boolean nextBoolean() {
            return random.nextBoolean();
        }

        public static String nextString(int length) {
            StringBuilder sb = new StringBuilder(length);
            for (int i = 0; i < length; i++) {
                char c = (char) ('a' + random.nextInt(26));
                sb.append(c);
            }
            return sb.toString();
        }
    }

    @Data
    static class  Root {
        private String name;
        private Branch1 branch1;

        public String toString() {
            return String.format("Root{name=%s, branch1=%s}", name, branch1);
        }
    }

    @Data
    static class  Branch1 {
        private int id;
        private Leaf leaf;

        public String toString() {
            return String.format("Branch1{id=%d, leaf=%s}", id, leaf);
        }
    }

    @Data
    static class  Leaf {
        private double value;
        private boolean flag;

        public String toString() {
            return String.format("Leaf{value=%f, flag=%s}", value, flag);
        }
    }
}

```
# 実行結果
```text
Root{name=agxoulflv, 
branch1=Branch1{id=15, 
leaf=Leaf{value=0.321949, flag=false}}}
```



# おわりに
2回目の結果は自作と同じバグはあるがほぼ期待通りでした。検討から実装までそれなりの時間を要したプログラムを数秒で出力されました。
実装に時間を使うより正確な要求を伝える方法の検討に時間をかけたほうがよさそう。
# 参考リンク

* [Java リフレクションを使って階層の深いインスタンスを自動生成する](https://qiita.com/flip-flop/items/e6047d75239b83bb2f23)