Reputation: 1
I'm pretty new to Java so please be thorough. I'm creating a program that uses a library of methods to search text files with various regular expressions. The list of checks may change based on the type of text file (the text files are network device configuration files). Some checks apply to some devices and don't apply to others. Each check is executed by a call to its method. The problem I have is since all of the checks don't apply and I don't know which do apply until run time. So I need to be able to call methods using a String from a list created at runtime when the user chooses which device they are inspecting. For example: I think I need to be able to call methods using a String Contents of my list that determine which checks are performed
String[] checklist = {"check100", "check101", "check105"}; // list of checks to perform
String chk = checklist[0]; // String containing a check to be performed (Function Call)
chk(config); // Where it all goes wrong... I want to make a call to the check100 method using the value of chk
...
public void check100 (String[] configFile){ // performs the configFile check100
...
...
}
public void check101 (String[] configFile){ // performs the configFile check101
...
...
}
public void check103 (String[] configFile){ // check not applicable to this device
...
...
} `
I've tried looking at reflection but cant seem to figure it out. If there is a better way to do this without using reflection please let me know.
Upvotes: 0
Views: 2248
Reputation: 2088
Solution 1:
This can be done using Java's Reflection API
public static void checkAll( String[] checks, String[] configFile ) {
Class< ? > cl = Check.class; // Class object where all check methods are.
for( String check : checks ) {
Method m = cl.getMethod( check );
m.invoke( null, configFile );
}
}
There are a couple of warnings you should read before considering this approach. I did not add any error handling and this code will not compile. The Reflection API can throw a lot of potential exceptions that I did not cover. You should read up on each method used and consider how you would handle each potential error.
Another warning is reflection is slow, it is much slower than directly invoking each method statically. If you are worried about performance or type safety, consider using Solution 2 instead.
Solution 2:
A better solution is to use a common interface for each check such as.
public interface Check {
void check( String[] configFile );
}
You then create a bunch of classes that implement the interface and add them into say a Map where you can use a name or id to lookup each check that you wish to run. This is much faster than the reflection based approach and is type safe.
Example:
public class Check1 implements Check {
public void check( String[] configFile ) {
// Do check stuff
}
}
public class Check2 implements Check {
public void check( String[] configFile ) {
// More check stuff
}
}
public class ConfigCheck {
public static Map< String, Check > nameToCheck = new HashMap< String, Check >();
public static void invokeChecks( String[] checks, String[] configFile ) {
for( String check : checks ) {
nameToCheck.get( check ).check( configFile );
}
}
}
Solution 3:
Solution 3 is similar to Solution 2 but instead of abstracting each check, you could instead abstract away each type of config you plan on checking. Each type would then be responsible for calling all required checks on the file.
public interface ConfigType {
void check( String[] configFile );
}
public class TxtConfig implements ConfigType {
public void check( String[] configFile ) {
// Place calls to all required checks.
// check100( configFile );
// check101( configFile );
}
}
public class XMLConfig implements ConfigType {
public void check( String[] configFile ) {
// Place calls to all required checks for XML configs
// check101( configFile );
// check102( configFile );
}
}
IMO this is a very clean solution and the interface could be expanded where each ConfigType subclass can be used to determine which config files it can check. It also allows for a finer degree of flexibility the other two solutions don't provide. Like only doing certain checks if previous ones "failed". It also reduces the amount of subclasses that are required as in solution 2. You can still share check methods between different config types with no extra work required.
Upvotes: 5
Reputation: 274
Warning: not the best approach.
A trivial way to do it is using a foreach loop and a switch statement:
for (String check: checklist) {
switch (check) {
case "check100": check100();
break;
case "check101": check101();
break;
default: defaultCheck();
break;
}
}
Upvotes: 0
Reputation: 6580
You can use Reflection like this
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
class ReflectionExample {
public static void main(String[] args){
String[] checklist = { "check100", "check101", "check105" };
ReflectionExample example = new ReflectionExample();
for (String methodName : checklist) {
try {
Method method = ReflectionExample.class.getMethod(methodName, String[].class);
String[] configFile = new String[1];
method.invoke(example,configFile);
}catch(Exception e) {
e.printStackTrace();
}
}
}
// ...
public void check100(String[] configFile) { // performs the configFile
// check100
System.out.println("check100");
}
public void check101(String[] configFile) { // performs the configFile
// check101
System.out.println("check101");
}
public void check103(String[] configFile) { // check not applicable to this
// device
System.out.println("check103");
}
}
of course, an exception will be thrown for check105
Upvotes: 0
Reputation: 13342
You can use Reflection
.
Something along these lines:
String chk = checklist[0];
Method checkMethod = Class.getDeclaredMethod(chk);
checkMethod.invoke();
Upvotes: 0
Reputation: 83577
One solution is to create an interface:
public interface Checkable {
void check(String[] configFile);
}
Then create several classes that implement this interface. Unfortunately, this doesn't scale well if you really need over 100 different checks.
Upvotes: 0