1

I wish to be able to get a List/ArrayList of N new instances from a static method inherited to subclasses so that I don't have to rewrite this same function in all subclasses.

I want to implement this so that I can build a vector containg some A and B. I've tried several methods but none of them worked for me :

public class Parent {

   public static List<Parent> getNInstances(int n) {
       List<Parent> out = new ArrayList<>();
       for (int i = 0; i < n; i++) {
          Parent newChildInstance = (Parent) MethodHandles
                                               .lookup()
                                               .lookupClass()
                                               .getConstructor()
                                               .newInstance()
          out.add(newChildInstance);
      }
   } 
}

I've got the MethodHandles thing from here since I feel like I need to get the class to be able to call .getConstructor().newInstance() which should, in theory, solve my problem. Nonetheless, this doesn't work, it gives me a NoSuchMethodException since he's not able to find the constructor from the Class given by MethodHandles.lookup().lookupClass(), at least I think that's why.

Here is how I would like the method .getNInstances() to work.

public class Parent {

   public Parent(){  }

   public static List<Parent> getNInstances(int n) {
   List<Parent> out = new ArrayList<>();
   for (int i = 0; i < n; i++) {
      Parent newChildInstance = ...
      out.add(newChildInstance);
      }
   } 
}
public class A extends Parent {
   public A(){ }
}
public class B extends Parent {
   public B(){ }
}
public class Main {
   public static void main(String[] args) {
       List<Parent> total = new ArrayList<>();

       total.addAll(A.getNInstances(3));
       total.addAll(B.getNInstances(4));
   }
}

Here total should therefore be like [a, a, a, b, b, b, b] where a is an instance of A and b an instance of B, but by now, it's just empty.

Michael
  • 41,989
  • 11
  • 82
  • 128

2 Answers2

2

There's no need at all to use reflection here. Use a factory and a method reference for the constructor.

This way you have compile-time assurance that the constructor you're trying to use actually exists.

abstract class ParentFactory
{
    public List<Parent> getNInstances(int n)
    {
        final List<Parent> out = new ArrayList<>();
        for (int i = 0; i < n; i++)
        {
            out.add(constructor().get());
        }
        return out;
    }

    protected abstract Supplier<Parent> constructor();
}

class AFactory extends ParentFactory
{
    @Override
    protected Supplier<Parent> constructor() {
        return A::new;
    }
}

class BFactory extends ParentFactory
{
    @Override
    protected Supplier<Parent> constructor() {
        return B::new;
    }
}

Sample usage:

List<Parent> total = new ArrayList<>();

total.addAll(new AFactory().getNInstances(3));
total.addAll(new BFactory().getNInstances(4));
Michael
  • 41,989
  • 11
  • 82
  • 128
0

I don't manage to reproduce your error. But note some points :

1) Here, you will never lookup a class of the subclasses because lookupClass() returns the class that invoked the method and this is always Parent.getNInstances() :

Parent newChildInstance = (Parent) MethodHandles
        .lookup()
        .lookupClass()
        .getConstructor()
        .newInstance();

Make it an instance method will produce the same result.

2) Generic class should be enough to solve your problem and make it an instance method.

public class Parent<T extends Parent> {

    public List<Parent> getNInstances(int n) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        List<Parent> out = new ArrayList<>();
        for (int i = 0; i < n; i++) {

            Class<T> clazz = (Class<T>) ((ParameterizedType) getClass()
                    .getGenericSuperclass()).getActualTypeArguments()[0];
            Parent newChildInstance =
                    clazz.getConstructor().newInstance();

            out.add(newChildInstance);
        }
        return out;
    }

}

And subclasses :

public class A extends Parent<A> {
    //...
}

public class B extends Parent<B> {
    //...
}

Sample test :

List<Parent> total = new ArrayList<>();    
total.addAll(new A().getNInstances(3));
total.addAll(new B().getNInstances(4));

System.out.println(total);

outputs :

[A@506e6d5e, A@96532d6, A@3796751b, B@67b64c45, B@4411d970, B@6442b0a6, B@60f82f98]

davidxxx
  • 125,838
  • 23
  • 214
  • 215