javaclassoopdictionaryfirst-class

Store classes in a Map so that they can be instantiated


I want to be able to create instances of classes, based on HashMap entries.

E.g. this is what I'd try writing off the top of my head:

public class One implements Interface {
  public void sayName() {
    System.out.println("One");
  }
}

public class Two implements Interface {
  public void sayName() {
    System.out.println("Two");
  }
}

Map<String, Interface> associations = new HashMap<String, Interface>();

associations.put("first", One);
associations.put("second", Two);

Interface instance = new associations.get("first")();

instance.sayName(); // outputs "One"

But I strongly suspect this will not work in Java.


My situation: I want to create a way to associate String names with classes.

A user can create instances of a class by using its "name".

I feel like trying: making a map of names to classes (I don't know how to store classes in a map), and getting the item from the map that matches "name" and then instantiating it.

That won't work.


How can I associate classes with String names and instantiate those classes using the 'name' I've given it?


Solution

  • You can use the Supplier functional interface and a method reference to the default constructor:

    Map<String, Supplier<Interface>> associations = new HashMap<>();
    
    associations.put("first", One::new);
    associations.put("second", Two::new);
    

    To instantiate a new object, call Supplier.get:

    Interface foo = associations.get("first").get();
    

    If your constructors require arguments, you'll need to use another functional interface. For one- and two-argument constructors, you can use Function and BiFunction respectively. Any more and you'll need to define your own functional interface. Supposing the constructors both take a string, you could do this:

    class One implements Interface
    {
        One(String foo){ }
    
        public void sayName() {
            System.out.println("One");
        }
    }
    
    Map<String, Function<String, Interface>> associations = new HashMap<>();
    associations.put("first", One::new);
    

    and then use Function.apply to get the instance:

    Interface a = associations.get("first").apply("some string");
    

    If your constructors take different number of arguments then you're out of luck.