Saturday, July 20, 2013

Controlled reflection and template methods in java

This was in drafts for a long time and since then I have lost the original context. But I thought I'll make it compilable and let you, the reader, decide, whether you find any use for this.

Suppose you have a base class A. Suppose also that you need to instantiate two classes B and C, sub-classes of A, with the same configuration data. One straightforward way to achieve this in java is to use constructors:

ConfigData configData = setConfigData();
B b = new B(configData);
C c = new C(configData);

The question is: is there is a way to keep everything just in one method of the base class, governing setting the config data that would return an instance of a subclass of A (B or C)?

Yes, there is! One way to set this is to implement a ctor in the base class A.

Another method is to use "templated" reflection. By "templated" I here refer to Java generics. In order to make sure we get the proper class instances, we should limit the accepted classes with <T extends A>:

public class A {
// declaration updated thanks to Pitko's comment below
public static <t extends A> t configureA(Class<a> ATemplateClass) {
t a;
 ConfigData configData = setConfigData();
 a = ATemplateClass.getConstructor(ConfigData.class).newInstance(configData);
 return a;
    }

private static ConfigData setConfigData() {
        ConfigData configData = new ConfigData("configParam1Value");
 return configData;
    }
}

public class ConfigData {
public String configParam1;

public ConfigData(String _configParam1) {
configParam1 = _configParam1;
}

/* (non-Javadoc)
 * @see java.lang.Object#toString()
 */
@Override
public String toString() {
   return "ConfigData [configParam1=" + configParam1 + "]";
}
}
The child classes will look alike (in practise they will have differrent implementation logic), illustrating with just B subclass:

public class B extends A {
ConfigData configData;

public B(ConfigData _configData) {
this.configData = _configData; 
}

/**
* @return the configData
*/
public ConfigData getConfigData() {
   return configData;
}
}

Now we can say:

B b = A.configureA(B.class);
C c = A.configureA(C.class);
  
System.out.println("Class B:" + b.getConfigData());
System.out.println("Class C:" + c.getConfigData()); 

// which outputs:
Class B:ConfigData [configParam1=configParam1Value]
Class C:ConfigData [configParam1=configParam1Value]

This post illustrates the usage of reflection and generics in java. We were able to access child classes in the base class using "controlled" reflection, that is we allowed only subclasses of the base class to be passed in the reflecitve method. We use generics to return proper subclass instances from the base class.

3 comments:

Dmitry Kan said...

A comment by Olli Varis, demonstrating proper java polymorphism:

"Nice post, I commented on the blog but the comment disappeared....anyways...I'm sure you wanted to demonstrate reflection & generics but I would just simply:

public abstract class A {

private final ConfigData configData;

public A() {
this.configData = initConfigData();
}

protected ConfigData initConfigData() {
return new ConfigData("A");
}

public final ConfigData getConfigData() {
return configData;
}
}

public class B extends A {
@Override
public ConfigData initConfigData() {
return new ConfigData("B");
}
}
"

For which the main method would be:

public static void main(String[] args) {
B b = new B();
System.out.println("Class B:" + b.getConfigData());
}

outputting:

Class B:ConfigData [configParam1=B]

Great!

Anonymous said...

Hi !
Nice to find another hacker interested in Java Academics in real world.

I found your post interesting but something didn't smell quite right.
After sleeping overnight and returning here again I notice these two lines:

First in class A you have:
public static <t extends A> A configureA(Class<a> ATemplateClass) {

Secondly in bottom (main) there is

B b = (B) A.configureA(B.class);

Bad smell: Why is it necessary to cast the new object down to B??

Trying to satisfy my curiosity I compiled the code and it did not!

My first worry was correct: The formal parameter to configureA has
an unknown <a>!

This led me to the happy trail towards a good (nice smelling!) result:

Using the type template <t extends A> at the beginning of this method
dictates that all occurrences of the desired type should be 't'! Both the
return type from this method and the formal parameter type.

So after modifying it to:
public static <t extends A> t configureA(Class<t> ATemplateClass)

The code compiles and executes ok, and after cleaning up the
main to say:
B b = A.configureA(B.class);
without casting the result, it uses generics cleanly and effectively!

Yours, Pitko.

Dmitry Kan said...

@Pitko.

Thanks for spending your time on this code!

To be honest I'm not sure what exactly happened, when I have copied the code from eclipse IDE, but when I glanced at it now, I see:

public static A configureA(Class ATemplateClass) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
...


And the second thing you have mentioned is downcasting to B. I totally agree that with your fix, it is not required.

So to give the full method implementation for reference:

public static t configureA(Class ATemplateClass) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
t a;
ConfigData configData = setConfigData();
a = ATemplateClass.getConstructor(ConfigData.class).newInstance(configData);
return a;
}


This is cool, thanks for incubating the idea!