Home > Software design >  Is there a way to map a string to a method that returns a different type depending on the string?
Is there a way to map a string to a method that returns a different type depending on the string?

Time:03-01

I want to map a String to a method that builds a certain object, but not necessarily from the same class for every String. Looking around on here a nice solution was to have a Map<String, ObjectBuilder>, ObjectBuilder<T> being an interface with an abstract method T buildObject().

I then have multiple classes, let's say Object1Builder implements ObjectBuilder<Object1>, Object2Builder implements ObjectBuilder<Object2> and so on.

I can then construct my map like so :

stringToBuilder = new HashMap<String, ObjectBuilder>(){{
            put(string1, Object1Builder);
            put(string2, Object2Builder);
            put(string3, Object3Builder);
        }};

And I can then do Object1 myObject1 = stringToBuilder.get(string1).buildObject()

Problem is, I get an error

Raw use of parameterized class 'ObjectBuilder'

in IntelliJ when I instanciate and construct stringToBuilder and I understand it has something to do with not specifying the generic of the interface ObjectBuilder when constructing the map, but I don't see how I could circumvent this. Moreover, I'm not very satisfied with the fact that I'm storing these classes in a map, I wish I could access them through the map without having the whole instance in the map.

You've probably noticed I'm quite new to Java and all this but please be sure I'm trying my best. Thank you in advance :)

CodePudding user response:

What you want will never be possible without explicit casts. The reason is that there is no direct relation between the map keys (strings) and values (ObjectBuilders).

If you can switch from strings to use the T values as map keys, this can be done with a little internal casting.

First, declare your map as Map<Class<?>, ObjectBuilder<?>>. Note the two wild-cards; the compiler cannot help us with enforcing that the keys and the values have the same generic type. That's what we need to do ourselves.

Next, initialize it as necessary. I dislike the anonymous class with initializer you use, so I'll use Map.of:

Map<Class<?>, ObjectBuilder<?>> classToBuilder = Map.of(
        Object1.class, Object1Builder,
        Object2.class, Object2Builder,
        Object3.class, Object3Builder,
);

Finally, we need a method to get the builder:

@SuppressWarnings("unchecked")
private <T> getBuilder(Class<T> type) {
    // Omitted: presence check
    return (ObjectBuilder<T>) classToBuilder.get(type);
}

This can now be used as follows:

Object1 object1 = getBuilder(Object1.class).buildObject();
  •  Tags:  
  • java
  • Related