• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Tim Cooke
  • Devaka Cooray
  • Ron McLeod
  • Jeanne Boyarsky
Sheriffs:
  • Liutauras Vilda
  • paul wheaton
  • Junilu Lacar
Saloon Keepers:
  • Tim Moores
  • Stephan van Hulst
  • Piet Souris
  • Carey Brown
  • Tim Holloway
Bartenders:
  • Martijn Verburg
  • Frits Walraven
  • Himai Minh

Dynamic class type declaration

 
Greenhorn
Posts: 7
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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.




 
Marshal
Posts: 76863
366
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
 
Sheriff
Posts: 22701
129
Eclipse IDE Spring VI Editor Chrome Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
 
Kian Peng Yong
Greenhorn
Posts: 7
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

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.
 
Bartender
Posts: 4179
22
IntelliJ IDE Python Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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.
 
Kian Peng Yong
Greenhorn
Posts: 7
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

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
Posts: 4179
22
IntelliJ IDE Python Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Posts: 7
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
 
That feels good. Thanks. Here's a tiny ad:
the value of filler advertising in 2021
https://coderanch.com/t/730886/filler-advertising
reply
    Bookmark Topic Watch Topic
  • New Topic