0

It's difficult to explain what I'm trying to achieve here (so...sorry if I missed a glaringly obvious answer in my searching)...

I'm trying to dynamically instantiate an object based on a Class paired to a value in a HashMap. However, I've been having issues finding an approach that actually works. Currently, I'm doing something like this:

HashMap<String, Flag<?>> flags = new HashMap<String, Flag<?>>();
HashMap<String, Class> keys = new HashMap<String, Class>();

keys.put("test", BooleanFlag.class);
keys.put("thing", StringFlag.class);
keys.put("foo", DoubleFlag.class);

for (Map.Entry<String, Class> key : keys.entrySet()) {
    try {
        Class c = key.getValue();
        Object obj = c.getConstructor(String.class, String.class).newInstance(key.getKey(), "test value that will be checked and coerced by one of the flag classes");
        flags.put(key.getKey(), c.cast(obj));
    } catch (Exception ex) {
        //exception handling
    }
}

In this current incarnation of the method, c.cast(obj) throws a compiler error about an Object being found where Flag is expected. Am I going about this horribly wrong/is this possible?

redwall_hp
  • 1,067
  • 2
  • 10
  • 18
  • Possible duplicate of [Java Dynamically Loading a class](http://stackoverflow.com/questions/3580752/java-dynamically-loading-a-class) – rll Dec 07 '15 at 11:40
  • Why not store a Builder Object instead of BooleanFlag. The BuilderObject can know what type it is supposed to create and that way you don't need to cast. Or have a factory that creates your object and pass in the key as a flag on what to do? – pandaadb Dec 07 '15 at 11:56

2 Answers2

1

You could do

if(obj instanece of Flag) {
  flags.put(key.getKey(), (Flag)obj);
}

That should handle all subclasses/implementations of Flag

Jan
  • 13,738
  • 3
  • 30
  • 55
  • Wouldn't I need to cast it back to a BooleanFlag or a StringFlag to access member methods of the more specific subclasses? That's something I need to avoid, hence why I'm using a Flag> generic instead of just casting to Flag in the first place. – redwall_hp Dec 07 '15 at 11:35
  • Yes of cause - but only if those methods were not part of `Flag`. If you would iterate through the values of your flags map you'd either use valies as Flag or go checkijg for instanceof of some specifics. If you give more detail as to how StringFlag differs from Flag? – Jan Dec 07 '15 at 11:38
  • Flag is an abstract class. StringFlag is the simplest, merely accepting the value given to it. The others, like BooleanFlag, parse the string value given to them and set the canonical value (a boolean value in the case of BooleanFlag, cartesian coordinates in the case of another flag type, etc). – redwall_hp Dec 07 '15 at 11:44
  • But how would you use them different? What additional (public) methods are there you'd want to call if you *knew* this was BooleanFlag? – Jan Dec 07 '15 at 11:47
  • Hm...that's a good point. I think everything that's public should be in the abstract class. And it should still be possible to poke it with instanceof in edge cases... – redwall_hp Dec 07 '15 at 11:54
  • Thanks. I'll give that a try. I guess that's what happens when you try to architect parts of a project when you're tired :P – redwall_hp Dec 07 '15 at 11:54
  • Hm...doesn't play nice with my foo.getFlag("name").getValue(), since it returns an Object instead of a boolean or whatever from public T getValue(). – redwall_hp Dec 07 '15 at 12:07
  • again not the best of designs? not sure if a T getValue(Class asWhat) will help there – Jan Dec 07 '15 at 12:09
  • It would all work if that cast worked. Strangely, c.cast() causes the IDE to spit a compiler error, but if I directly put a "BooleanFlag.class.cast()" instead it accepts it. Not seeing a reason why that should be the case. – redwall_hp Dec 07 '15 at 12:25
  • Dont think so - on access into flags you wouldn't know which kind of Flag you got as well without checking for it. – Jan Dec 07 '15 at 12:28
  • 90% of the time it can be inferred from the code. e.g. "if (foo.getFlag("someboolean").getValue()" since flags are referenced in event handlers in the context they're meant to be used. A boolean flag isn't going to be queried when a string flag is required. That HashMap of key-type pairings is supposed to be a canonical list of allowed flags. (And toString methods are implemented for the few cases where they need to be output visually to the user.) – redwall_hp Dec 07 '15 at 12:35
  • Then I recomment a method T getValue(Class whatToGet) for your Flags that you could invoke as flag.getValue(Boolean.class) -> returns Boolean or flag.getValue(String.class) -> returns String – Jan Dec 07 '15 at 12:41
1

May be the 'Factory pattern' is what you actually need?

public class Flag<T> {}

public interface FlagFactory<T> {
    public Flag<T> newInstance();
}

public class BooleanFlagFactory implements FlagFactory<Boolean> {
    public Flag<Boolean> newInstance() {
        return new Flag<Boolean>();
    }
}
public class StringFlagFactory<T> implements FlagFactory<String> {
    public Flag<String> newInstance() {
        return new Flag<String>();
    }
}

HashMap<String, FlagFactory> factories = new HashMap<String, FlagFactory>();

public void test() {
    factories.put("test", new BooleanFlagFactory());
    factories.put("thing", new StringFlagFactory());

    Flag flag = factories.get("test").newInstance();

}
Eldar Budagov
  • 312
  • 2
  • 11