3

I'm trying to figure out how to dynamically call a method. I have a string that describes the method name, but I'm not sure how to do it. I thought this could be done with reflection, but haven't had any success. Example

set.add(vehicleConfiguration.getVehicleYear.getName());

set.add(vehicleConfiguration.getVehicleMake().getName());

set.add(vehicleConfiguration.getVehicleModel().getName());

You'll notice all the method calls are the same with the exception of the getVehicleYear, etc

I have a string that describes the method names, just not sure how to use it.

I got as far as this with reflection, but failed.

set.add(Class.forName("VehicleConfiguration").getMethod("vehicleMake", null).getName());

Thanks in advance.

2
  • 1
    What are the return types of getVehicleModel(), getVehicleMake() and getVehicleYear()? If they all have the getName() method it sounds like generics would be a much better solution to your problem than reflection. Or even just an if/else block. Reflection is overkill 99% of the time. Commented Sep 7, 2013 at 19:31
  • @MrLore, I'm open to ideas, please show an example below using a generic. I'd rather stay away from if / else if possible. Thanks Commented Sep 7, 2013 at 19:33

4 Answers 4

4

The class you are looking for is Method. Please read the appropriate javadoc carefully.

You can get a method with, for example

// assumign `getVehicleMake` is the name of the method and it accepts no parameters
Method method = VehicleConfiguration.class.getMethod("getVehicleMake"); 
// VehicleConfiguration.class can be replaced by
// Class.forName("VehicleConfiguration") 
// if VehicleConfiguration is the fully qualified, ie. with packages, name of the class
// other you need Class.forName("com.yourpackage.VehicleConfiguration")

You then need to invoke() this method on an instance of your class.

VehicleConfiguration instance = new VehicleConfiguration();

Object returnObject = method.invoke(instance); // assuming no parameters

To then call getName(), you need to cast the returned object to the type that has the method. Assuming getMake() is a method of the type VehicleMake, call it like this

((VehicleMake)returnObject).getMake();
Sign up to request clarification or add additional context in comments.

2 Comments

When you go to cast the returnObject, can that be dynamic or no?
@George Not exactly, you need to know the type, so that the code compiles. There are some workarounds like using reflection for that too. You can't call the method of a type you don't know.
2

You have to use actual method name: getVehicleMake, not vehicleMake.

Additionally, if you're using this as anything other than an exercise, don't roll your own. Use Commons BeanUtils or Spring's BeanWrapper.

3 Comments

thanks chrylis, I missed that one. Two other small questions, what happens to the null and when I go to call getName() does it actually call the getName() of my method or does it call getName() from getMethod()?
The method name change isn't quite enough. The call to getName() is misplaced.
@George The null is telling the reflection system that you're wanting the method named getVehicleMake that doesn't take any parameters, and yes, as Sotirios noted, you'll be calling getName() on the Method, not on the result you want (you didn't invoke() the Method).
1

Expanding on my comment, As all the methods you showed have a getName() method, let's create a simple class which defines this:

class Nameable
{
    private String name;

    public Nameable(final String name)
    {
        this.name = name;
    }

    public String getName()
    {
        return this.name;
    }
}

Now when you create the object for Make, Model and Year, they can all use this class so they can be used interchangeably, and can then be combined into a Car:

class Car
{
    public final Nameable make;
    public final Nameable model;
    public final Nameable year;

    public Car(Nameable make, Nameable model, Nameable year)
    {
        this.make = make;
        this.model = model;
        this.year = year;
    }

    public Nameable getInfo(final String info)
    {
        switch(info)
        {
            case "make": return this.make;
            case "model": return this.model;
            case "year": return this.year;
        }
        return null;
    }
}

Then a simple implementation would be:

class PaganiZonda2006 extends Car
{
    public PaganiZonda2006()
    {
        super(new Nameable("Pagani"), new Nameable("Zonda"), new Nameable("2006"));
    }
}

And finally, when you want to get the information out, you can read it like so:

public static void main(String[] args)
{
    Car car = new PaganiZonda2006();
    System.out.println(car.getInfo("make").getName()); //Pagani
    System.out.println(car.getInfo("model").getName()); //Zonda
    System.out.println(car.getInfo("year").getName()); //2006
}

1 Comment

thanks, I ended up using your solution. It's not entirely dynamic like I would have hoped do to the switch statement, but I do like it a lot better than the other solutions presented.
0

This ended up being my final solution which is a combination of MrLore and Sotirios Delimanolis solutions. This solution is completely dynamic without the use of any conditions.

This class performs the search for the name by passing in the property name;

String propertyName = "vehicleYear";

vehicleConfiguration.getInfo(propertyName).getName()

propertyName = "vehicleMake";

vehicleConfiguration.getInfo(propertyName).getName()

This class represents the VehicleConfiguration

@Entity
public class VehicleConfiguration extends StatefulEntity {

    @ManyToOne
    @JoinColumn(name = "year_id")
    private VehicleYear vehicleYear;

    @ManyToOne
    @JoinColumn(name = "make_id")
    private VehicleMake vehicleMake;

    public LookupBaseEntity getInfo(final String fieldName) {
        try { 
            String methodName = WordUtils.capitalize(fieldName);
            Method method = VehicleConfiguration.class.getMethod("get" + methodName);  
            return (LookupBaseEntity) method.invoke(this); 
        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
            Logger.getLogger(VehicleConfiguration.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
}

This class represents the VehicleYear

@Entity
public class VehicleYear extends LookupBaseEntity {

}

This class represents the VehicleMake

@Entity
public class VehicleMake extends LookupBaseEntity {

}

Which both extend LookupBaseEntity

public class LookupBaseEntity extends StatefulEntity {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.