This week's giveaway is in the Android forum.
We're giving away four copies of Android Security Essentials Live Lessons and have Godfrey Nolan on-line!
See this thread for details.
The moose likes Java in General and the fly likes Dynamic class type declaration Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of Android Security Essentials Live Lessons this week in the Android forum!
JavaRanch » Java Forums » Java » Java in General
Bookmark "Dynamic class type declaration" Watch "Dynamic class type declaration" New topic
Author

Dynamic class type declaration

Kian Peng Yong
Greenhorn

Joined: Aug 29, 2009
Posts: 7
Dear sir/madam,

I have a situation where I find myself adding more codes into a function as the number of components/classes increases.

I am trying to collect the list of classes into a Hashtable (componentClasses) by declaring them one by one according to a configuration file.
.
.
.
if (componentName.equals("FileReader")) {
FileReader component = new FileReader(componentKey);
componentClasses.put(componentKey, component);
}
else if (componentName.equals("TextMapper")) {
TextMapper component = new TextMapper(componentKey);
componentClasses.put(componentKey, component);
}
else if (componentName.equals("InputQueue")) {
InputQueue component = new InputQueue(componentKey);
componentClasses.put(componentKey, component);
}
.
.
.

As shown above, I have put in place "FileReader", "TextMapper" and "InputQueue" declaration.
However, if I create another class, lets say "EmailWriter", I will need to add the following into the existing codes:


else if (componentName.equals("EmailWriter")) {
EmailWriter component = new EmailWriter(componentKey);
componentClasses.put(componentKey, component);
}

Is there a way I can make my code dynamically declare the necessary component?
So that I do not need to keep adding additional codes into this function?

Thank you very much.




Campbell Ritchie
Sheriff

Joined: Oct 13, 2005
Posts: 38045
    
  22
Sounds too difficult a question for us beginners, so I shall move it.

Will Class.forName("foo.bar.MyClass").newInstance() help at all?

And welcome to JavaRanch
Rob Spoor
Sheriff

Joined: Oct 27, 2005
Posts: 19655
    
  18

Not newInstance, but using reflection you could do this:
- use Class.forName to get the Class instance
- get the right Constructor
- create an instance using that Constructor


SCJP 1.4 - SCJP 6 - SCWCD 5 - OCEEJBD 6
How To Ask Questions How To Answer Questions
Kian Peng Yong
Greenhorn

Joined: Aug 29, 2009
Posts: 7
Rob Prime wrote:Not newInstance, but using reflection you could do this:
- use Class.forName to get the Class instance
- get the right Constructor
- create an instance using that Constructor


Could you give me an example please? Thank you very much.
Steve Luke
Bartender

Joined: Jan 28, 2003
Posts: 4167
    
  21

Look at the API for Class. It has methods for getting a particular Constructor, if you know its parameter types, and a method for getting an array of constructors which you can search through to find the best CTor for your task.

Once you have the correct Constructor, you can use one of its methods to create a new instance using the Constructor.

See if you can find the correct methods in the API, and let us know if you run into trouble.


Steve
Kian Peng Yong
Greenhorn

Joined: Aug 29, 2009
Posts: 7
Steve Luke wrote:Look at the API for Class. It has methods for getting a particular Constructor, if you know its parameter types, and a method for getting an array of constructors which you can search through to find the best CTor for your task.

Once you have the correct Constructor, you can use one of its methods to create a new instance using the Constructor.

See if you can find the correct methods in the API, and let us know if you run into trouble.


Hi Steve, thank you for the quick reply. In order to get the correct constructor, I need to do this:

Constructor[] constructors = FileReader.class.getDeclaredConstructors();

for (int j=0; j<constructors.length; j++) {

Constructor constructor = constructors[j];
Class[] parameterTypes = constructor.getParameterTypes();

for (int i=0; i><parameterTypes.length; i++) {

Class c = parameterTypes[i];
System.out.println("Param type name = " + c.getName());
}
}


Now, .... this code is still not flexible enough to automatically go through the list of modules I am creating (FileReader, TextMapper ... etc).

Also, from the output of this code, I get:

Param type name = java.lang.String

So, this shows me the FileReader class expect a single parameter, which is a String, correct?

Now ... how do I access the componentName which is a public String declared inside the FileReader Class if I want to compare it with a String obtained from a Configuration text file? i.e. if I have the following code:

if (componentValueFromConfigFile.equals(FileReader.componentName)) {
FileReader component = new FileReader(componentKey);
componentClasses.put(componentKey, component);
}

Assuming we can somehow replace the FileReader with something from the Constructor[] ?

Sorry ... I am new to this Dynamic coding ..... Am I getting this totally wrong? ....

Thank you very much and hope to hear from you soon.

Best regards,
Yong
Steve Luke
Bartender

Joined: Jan 28, 2003
Posts: 4167
    
  21

Well, you know the parameter that you have to pass in is a String, and what its value is. In the previous code you had:


You would know at this point that componentKey is a String. So you know the Constructor you are looking for is one that uses a single String as a parameter. It would look something like this:


You would need the fully qualified class name for the componentName though. So not just "FileReader" but "luke.steve.file.FileReader" if it were in the luke.steve.file package.

Another option would be the 'enum as factory' pattern. I think it makes code easier to read. Example:

First start with an Enum, one for each expected type. The enum would have a method to generate the expected component with the 'Component Key':


So to create a 'TextMapper' you can use

But you can also take advantage of the enum's valueOf(String) method:

So again you can replace your if statements with this:


Another advantage is, as you have it now you have to pass around hard coded Strings "FileReader", and "TextMapper". But with the Enum you can avoid errors that might cause by using ComponentEnum.FileReader.name() and ComponentEnum.TextMapper.name().

Finally, to add a new value, you just add another entry to the enum list:


Then clients can call ComponentEnum.InputQueue.name() and your component mapping code doesn't have to change.
Kian Peng Yong
Greenhorn

Joined: Aug 29, 2009
Posts: 7
Hi Steve,

Thank you so much for helping

I followed your instructions and managed to get it working the way I wanted it:

try {

Class componentClass = Class.forName(componentFullName);

Class[] classes = new Class[1];
classes[0]=String.class;

Constructor ctor = componentClass.getConstructor(classes);

Object[] objects = new Object[1];
objects[0]=componentKey;

Object component = ctor.newInstance(objects);

componentClasses.put(componentKey, component);

}catch(Exception e) {
System.out.println("Unable to initialise component: "+componentKey+" -> "+componentFullName);
}


Looking at the example code you shown me, I can't help wondering is there a better way to declare the class[] and object[] in the code I attached above (in Red)? Can I cast a class to class[] and object to object[] ... or is there a better way/method to do this??

Thank you very much again.

Best regards,
Yong
 
It is sorta covered in the JavaRanch Style Guide.
 
subject: Dynamic class type declaration
 
Similar Threads
Generic Tag Listener - Request for Comment
FileReader?
About printing and reading
Writing a class file to read text file.
Question about getting arraylist's size