2

I have code that looks like follows:

public class Person{
    private Configuration configuration;
    public void act(){
        final Action action = new Factory().createAction(configuration);
        ....
    }
}

public class Factory{
    public Action createAction(Configuration configuration){
        Constructor<? extends Action> constructor =
                  configuration.getActionClass.
                  getConstructor(configuration.getClass());
        return constructor.newInstance(configuration);
    }
}

public class Configuration{
    private Class<? extends Action> actionClass;

    public void setActionClass(Class<? extend Action> cls){
        this.actionClass = cls;
    }

    public Class<? extends Action> getActionClass(){
        return this.actionClass;
    }
}

Each time act() is called, a new Action instance is created for some reason. I need to subclass Action and pass it to act().

So I use reflection on a factory method to achieve that. But it seems overkill and not type safe.

Is it possible to repalce reflection with a type safe method such like generics?

bresai
  • 369
  • 5
  • 18
  • If you add some type (ie. `Configuration` and `Class actionClass`) to your configuration, this can become type-safe –  Dec 21 '16 at 14:47
  • see also http://stackoverflow.com/search?q=generic+factory+%5Bjava%5D –  Dec 21 '16 at 14:48

2 Answers2

2

Due to type erasure (see #1) you can't avoid reflection and Class<?> arguments if you want to instantiate a type at runtime, so to answer your question it may be an overkill but new T(); just isn't possible.

You could make it type safe however, by adding a generic type parameter to your Configuration class.

public class Configuration<T extends Action> {
    private Class<T> actionClass;

    public void setActionClass(Class<T> cls){
        this.actionClass = cls;
    }

    public Class<T> getActionClass(){
        return this.actionClass;
    }
}

And get rid of any raw references to it. (ie. switch Configuration to Configuration<T> wherever it's being used)

Community
  • 1
  • 1
ᴘᴀɴᴀʏɪᴏᴛɪs
  • 7,169
  • 9
  • 50
  • 81
  • 2
    This also needs to be changed `public Action createAction(Configuration configuration)` to `public T createAction(Configuration config)`, so that it returns the parametric type and not just `Action`. – biziclop Dec 21 '16 at 14:57
  • My intention is not to change the action at runtime, rather a fixed subclass of `Action`. – bresai Dec 21 '16 at 15:03
  • 1
    @bresai This solution is fine for that. – biziclop Dec 21 '16 at 15:04
  • @biziclop sure, this is a good solution. But since no need to change action at runtime, why cannot I get rid of the reflection? – bresai Dec 21 '16 at 15:08
  • @bresai Because that's how type parameters have been implemented in Java. It was a known and planned shortcoming that offered what looked at the time like a good compromise between the new functionality and compatibility with pre-generics code. – biziclop Dec 21 '16 at 15:16
0

I got a very neat solution using new Function feature of Java 8 from a friend.

public class Person{
    private Configuration configuration;
    public void act(){
        final Action action = configuration.getActionFactory().apply(configuration);
        ....
    }
}

public class Configuration{
    private Function<Configuration, Action> actionFactory = Action::new;

    public void setActionFactory(Function<Configuration, Action> actionFactory){
        this.actionFactory = actionFactory;
    }

    public Function<Configuration, Action> actionFactory getActionFactory(){
        return this.actionFactory;
    }
}
bresai
  • 369
  • 5
  • 18