4

Is it possible to dynamically call a method on a class from java?

E.g, lets say I have the reference to a class, e.g either the string: 'com.foo.Bar', or com.foo.Bar.class, or anything else which is needed..). And I have an array / list of strings, e.g [First, Last, Email].

I want to simply loop through this array, and call the method 'validate' + element on the class that I have a reference to. E.g:

MyInterface item = //instantiate the com.foo.Bar class here somehow, I'm not sure how.

item.validateFirst();
item.validateLast();
item.validateEmail();

I want the above lines of code to happen dynamically, so I can change the reference to a different class, and the names in my string list can change, but it will still call the validate + name method on whichever class it has the reference to.

Is that possible?

Piro
  • 1,367
  • 2
  • 19
  • 40
Ali
  • 261,656
  • 265
  • 575
  • 769

6 Answers6

8

The simplest approach would be to use reflection

Given...

package com.foo;

public class Bar {

    public void validateFirst() {
        System.out.println("validateFirst");
    }

    public void validateLast() {
        System.out.println("validateLast");
    }

    public void validateEmail() {
        System.out.println("validateEmail");
    }

}

You could use something like...

String methodNames[] = new String[]{"First", "Last", "Email"};
String className = "com.foo.Bar";
try {

    Class classRef = Class.forName(className);
    Object instance = classRef.newInstance();

    for (String methodName : methodNames) {

        try {

            Method method = classRef.getDeclaredMethod("validate" + methodName);
            method.invoke(instance);

        } catch (NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException ex) {
            ex.printStackTrace();
        }

    }

} catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
    ex.printStackTrace();
}

To look up the methods and execute them.

You will need to decide the best way to handle errors and what they mean to you, but it wouldn't be a difficult them to expand the idea to a reusable method...

Updated with idea of concept discussed in comments

Given....

public interface Validator {
    public boolean isValid(Properties formProperties);
}

We can create one or more...

public class UserRegistrationValidator implements Validator {
    public boolean isValid(Properties formProperties) {
        boolean isValid = false;
        // Required fields...
        if (formProperties.containsKey("firstName") && formProperties.containsKey("lastName") && formProperties.containsKey("email")) {
            // Further processing, valid each required field...
        }
        if (isValid) {
            // Process optional parameters
        }
        return isValid;
    }
}

Then from our input controller, we can look and valid the required forms

public class FormController ... {

    private Map<String, Validator> validators;

    public void validForm(String formName, Properties formProperties) {
        boolean isValid = false;
        Validator validator = validators.get(formName);
        if (validator != null) {
            isValid = validate.isValid(formProperties);
        }
        return isValid;
    }

}

Of course you need to provide some way to register the Validators and there may be differences based on the backbone framework you are using and the parameters you can use (you don't have to use Properties, but it is basically just a Map<String, String>...)

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • I need this to happen during runtime and as a regular thing. Specifically this would be to do form validation in a web app. Would you still recommend reflection? – Ali Sep 13 '13 at 05:30
  • I'd be concerned about trying to use reflection in this way. Basically, it does not guarantee that the object you are trying to inspect actually has the values you need and it also has a performance hit. It would be better to work with known interfaces. If you validating a form, then I presume that you either have a `Object` with the form data already loaded into it?? – MadProgrammer Sep 13 '13 at 05:32
  • Lets say I have a form for adding or editing a user. To correspond to this, I will have a `com.models.User` class, which will have the fields `string first, string last` etc, which would correspond to data submitted by the form in HTTP post. I was thinking of having a list with the field names that need validation, and then having a `validate` method in the parent Model class (which User will extend from), which will simply loop through the list, and call the corresponding `validate` method on the child model class.. – Ali Sep 13 '13 at 05:38
  • 1
    Or I could build a separate `Validator` class which calls a `getRequiredFields()` method on the model to get the list of field names, then loop through them to call the corresponding `validate` methods. What do you suggest? – Ali Sep 13 '13 at 05:40
  • I'd probably reverse it. I'd probably have some kind controller/factory that had a `validate` method that took a variable number of `String` key/value pairs. I would probably have some kind of lookup which would find the validator for the given form and simply pass the form properties to it. This would either throw an exception or generate the final object, based on your needs. This way, if the form properties change, you can better handle it, same with the validation – MadProgrammer Sep 13 '13 at 05:41
  • The problem that I'm trying to avoid with that approach is, I don't want to write a bunch of boilerplate code which simply does a switch on the field names and calls the corresponding validate method. E.g `case first: return model.validateFirst()`. I want to avoid that and only once have to write `return model.methodName();`. I don't think your approach can do that? – Ali Sep 13 '13 at 05:44
  • If you goal is to generate a `Object` at the end, I would have a `Factory` that could take the forms key/value pair properties and generate the `Object`, validating the values at the same time. If you just want to validate the forms, I would have a controller with a simple `validate` method that took key/value pairs. If validation valied, you would then need to extract the reasons from the `Validator` and pass them back to the client...This way you generalise your functionality and reduce the risks of running into missing methods or changes to the form state - IMHO – MadProgrammer Sep 13 '13 at 05:44
  • *"I don't want to write a bunch of boilerplate code"* - Agreed. Each for should have it's `Validator`. Each `Validator` would accept a vararg of key/value pairs (form properties), which it would then inspect based on what it knows should be there and what it needs those values to be. – MadProgrammer Sep 13 '13 at 05:45
  • Can you throw up some psuedo-code to give me an idea of what you mean? – Ali Sep 13 '13 at 05:47
  • Here's an example of how I'm acomplishing all of this in PHP, I want to replicate this in Java: http://pastebin.com/NqsngB8z – Ali Sep 13 '13 at 05:52
  • This is just my opinion, but when I start look into reflection to solve my problems, I start wondering what is wrong with my design. – MadProgrammer Sep 13 '13 at 06:00
  • Ah, I see what you mean. The problem is, if i just have a one or two line validation, this method will work fine, but often times I need to do thorough validation. For this purpose, I want to have a seperate method for each field, e.g `validateFirst(), validateLast()`, etc rather than just one `validate()` method. Is that possible without reflection? – Ali Sep 13 '13 at 06:02
  • 1
    The `Validator` implementation could then branch out to individual methods as required, that's up to you. Because you couldn't define an `interface` which could possibly meet every need or have any useful way of calling it implementation (you'd need a separate `if` for each possible form which knew about which instance of the `Validator` to use), I would say ... no... – MadProgrammer Sep 13 '13 at 06:04
  • I see. So other than the fact that reflection is an ugly way to achieve this, is there any other downside, e.g performance or security? – Ali Sep 13 '13 at 06:06
  • 1
    Reflection does suffer from a performance hit, also, it's also possible to call `private` and `protected` methods of the objects it's reflecting...which is actually rather neat, but could break parts of the code if not used wisely...don't get me wrong. I use reflection a quite a bit, I just spend a time determining if it's the best solution to the problem. Just because it works, doesn't mean it's right (or wrong) ;) – MadProgrammer Sep 13 '13 at 06:11
  • Approx how big or small of a hit is it? Do most web frameworks use reflection as someone else said? – Ali Sep 13 '13 at 06:12
  • 1
    I can't say for most web frameworks or where they do. I would imagine they might be doing object to database mapping for example. As for performance, take a look at [this](http://stackoverflow.com/questions/435553/java-reflection-performance) and you should take a look at the [offical tutorial](http://docs.oracle.com/javase/tutorial/reflect/) as well – MadProgrammer Sep 13 '13 at 06:19
5

You can write something like this... it takes name of a class as string as an argument, the method name and its arguments

private static String invoke(String aClass, String aMethod, Class<?>[] params,
            Object[] args) throws Exception {
        String resp = "";
        Class<?> c = Class.forName(aClass);
        Method m = c.getDeclaredMethod(aMethod, params);
        Object i = c.newInstance();
        resp = m.invoke(i, args).toString();

        return resp;
}

You can also refer to the oracle tutorial on reflection ... which demonstrates how to call methods http://docs.oracle.com/javase/tutorial/reflect/member/methodInvocation.html

AurA
  • 12,135
  • 7
  • 46
  • 63
2

It's possible using reflection.

  1. First, you create a new class from the FQN (fully qualified name, which is the class name including the package).

  2. Then you iterate through your elements and invoke the "validate" methods on your item.

    Class<?> clazz = Class.forName("com.foo.Bar");
    Object item = clazz.newInstance(); 
    for (String element : elements) {
        Method method = clazz.getDeclaredMethod("validate" + element);
        method.invoke(item);
    }
Moritz Petersen
  • 12,902
  • 3
  • 38
  • 45
  • Fantastic.. is there any downside of doing this while in production? – Ali Sep 13 '13 at 05:31
  • 1
    Reflection has some slight performance disadvantage and also makes the code less maintainable (think about refactoring tools). However, all web frameworks I can think of use reflection more or less, so considering the disadvantages, you can use it also in production. – Moritz Petersen Sep 13 '13 at 05:32
  • 1
    Yes, for example: http://svn.apache.org/repos/asf/struts/struts2/trunk/core/src/main/java/org/apache/struts2/interceptor/validation/AnnotationValidationInterceptor.java – Moritz Petersen Sep 13 '13 at 07:01
1

You can use reflection, but my favorite method is to use beanutils, eg:

Bar b1 = //...
BeanUtils.getProperty(b1, "first");
BeanUtils.getProperty(b1, "last");

Note that your class has to conform to javabean convention. You can read more about beanutils on this blog post (disclaimer I'm the blog author)

gerrytan
  • 40,313
  • 9
  • 84
  • 99
  • That just gives the values of the getters. You need [MethodUtils.invokeMethod](http://commons.apache.org/proper/commons-beanutils//apidocs/org/apache/commons/beanutils/MethodUtils.html#invokeMethod%28java.lang.Object,%20java.lang.String,%20java.lang.Object%29). – Jayamohan Sep 13 '13 at 05:29
  • Why should one use an entire new jar just to invoke dinamically 3 methods? – Silviu Burcea Sep 13 '13 at 05:49
  • @SilviuBurcea because you don't want to 'reinvent the wheel' – gerrytan Sep 13 '13 at 05:58
  • 1
    gerrytan, I don't feel like I'm reinventing the wheel. If I use your excuse often enough, I will end up with 1 class app and 1 GB of jar dependencies. For a one time 3 method invocation it doesn't worth it. @ClickUpvote Yes, BeanUtils is a wrapper around Reflection API(according to their documentation) – Silviu Burcea Sep 13 '13 at 11:15
1

If you know the name of the class beforehand, use Class.forName(yourClassname) That way, you can invoke the class, and then, you can invoke its methods.

VictorCreator
  • 724
  • 1
  • 7
  • 17
0

Yes, using reflection. Using Class.getDeclaredMethod on your object

Object validator = <your object instance>;
final String[] values = {
  "Item1","Item2","Item3"
}
for(final String s : values) {
  Method m = validator.getDeclaredMethod("validate" + s,String.class);
  try {
    Object result = m.invoke(validator, s);
  }
  catch(ex) {}
}
Luca Basso Ricci
  • 17,829
  • 2
  • 47
  • 69