I am trying to reduce my code from using a bunch of if statements from getting a specified command and calling a method for it.
Instead, I want to try something that would take that command and call a method name with it
Something like this:
"get" + commandString + "Count"()
Instead of:
if (command == "something") {
callSomeMethod();
}
if (command == "somethingelse") {
callSomeOtherMethod();
}
...
Is there a way to call a method from a specified string? Or a better way to approach this problem.
This is the use-case for a switch case statement.
switch(command){
case "command1": command1(); break;
case "command2": command2(); break;
Using a string javascript style is fortunately impossible in Java. The comments links to answers how to use reflection to accomplish something similar. This is rarely a good solution.
we can use
java.lang.reflect.Method
Something like
java.lang.reflect.Method method;
try {
method = obj.getClass().getMethod(methodName, param1.class, param2.class, ..);
} catch (SecurityException e) { ... }
catch (NoSuchMethodException e) { ... }
The parameters identify the very specific method you need (if there are several overloaded available, if the method has no arguments, only give methodName).
And your possible solution can be like -
package com.test.pkg;
public class MethodClass {
public int getFishCount() {
return 5;
}
public int getRiceCount() {
return 100;
}
public int getVegetableCount() {
return 50;
}
}
package com.test.pkg;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) {
MethodClass classObj = new MethodClass();
Method method;
String commandString = "Fish";
try {
String methodName = "get" + commandString + "Count";
method = classObj.getClass().getMethod(methodName);
System.out.println(method.invoke(classObj)); //equivalent to System.out.println(testObj.getFishCount());
} catch (SecurityException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
Ref: How do I invoke a Java method when given the method name as a string?
Related
So, I have the following classes:
public class MainClass{
public void run(String infoOne, String infoTwo, String infoThree, String infoFour, String infoFive, String infoSix){
SomeClass someClass = new SomeClass();
someClass.runSomeMethod();
someClass.runSomeMethodTwo( infoOne);
someClass.runSomeMethodThree( infoThree, infoOne, infoSix);
someClass.runSomeMethodFour( infoTwo, infoFive);
someClass.runSomeMethodFive(infoThree, infoFive, infoOne, infoSix);
}
}
public class SomeClass{
public boolean runSomeMethod(){
// do something
}
public boolean runSomeMethodTwo(String arg){
// do something
}
public boolean runSomeMethodThree(String argOne, String argTwo, String argThree){
// do something
}
public boolean runSomeMethodFour(String argOne, String argTwo){
// do something
}
public boolean runSomeMethodFive(String argOne, String argTwo, String argThree, String argFour){
// do something
}
}
As you can see it's a bunch of methods taking only Strings as parameters (but a different amount every time). What I want now is to wrap each single method in a try catch block and log some results. To do that I wanted to put a method in between that handles the logging:
log(SomeClass::runSomeMethodFour);
public void log(????? method, String...args){
try{
if(method.execute(args);
System.out.println("Success!");
} else {
System.out.println("Failed to execute!");
}
} catch (Exception e){
e.printStackTrace();
}
}
Is this possible in some way? To pass a dynamic number of arguments to a lambda function? Or could I do something with generics?
There is no need to create a complicated Reflection-based solution. Your problems stem from the unnecessary attempt to separate the method and the parameter arguments, instead of just encapsulating the entire action like
public class MainClass {
public void run(String infoOne, String infoTwo, String infoThree,
String infoFour, String infoFive, String infoSix) {
SomeClass someClass = new SomeClass();
log(() -> someClass.runSomeMethod());
log(() -> someClass.runSomeMethodTwo(infoOne));
log(() -> someClass.runSomeMethodThree(infoThree, infoOne, infoSix));
log(() -> someClass.runSomeMethodFour(infoTwo, infoFive));
log(() -> someClass.runSomeMethodFive(infoThree, infoFive, infoOne, infoSix));
}
public void log(BooleanSupplier method) {
try {
if(method.getAsBoolean()) {
System.out.println("Success!");
} else {
System.out.println("Failed to execute!");
}
} catch (Exception e ){
e.printStackTrace();
}
}
}
For the work of the log method, only the boolean return value is relevant, which matches the functional signature of BooleanSupplier.
JLS described Method Reference Expression:
The compile-time declaration of a method reference is the method to which the expression refers. In special cases, the compile-time declaration does not actually exist, but is a notional method that represents a class instance creation or an array creation. The choice of compile-time declaration depends on a function type targeted by the expression, just as the compile-time declaration of a method invocation depends on the invocation's arguments.
A method reference expression is compatible in an assignment context, invocation context, or casting context with a target type T if T is a functional interface type (§9.8) and the expression is congruent with the function type of the ground target type derived from T.
the method reference expression must be assign an exactly Function Interface at compile time. and Function Interface is a SAM Interface. so you can't bind a method handler dynamically by method reference expression at runtime.
but you can using reflection or invoke api to achieve it.
let see each method expression refer to a Function Interface in your SomeClass results in refers to different Function Interface type:
SomeClass it = new SomeClass();
BooleanSupplier first1 = it::runSomeMethod;//bound
Predicate<SomeClass> first2 = SomeClass::runSomeMethod;//unbound
Predicate<String> second1 = it::runSomeMethodTwo;//bound
BiPredicate<SomeClass, String> second2 = SomeClass::runSomeMethodTwo;//unbound
...
Hearing about "reflection" as a comment by Oliver Charlesworth I came up with the following solution:
public class Test {
static Test testLogger = new Test(); //This should be another class ofcourse, but it doesn't matter for this example
public static void main(String[] args) throws NoSuchMethodException, SecurityException{
Test test = new Test();
run(test, "something", "hi", "hai", "blaa");
}
public static void run(Object pageObjectModel, String methodName, String...arguments){
Class<String>[] args = new Class[arguments.length];
Arrays.fill(args, String.class);
try {
testLogger.log(pageObjectModel, pageObjectModel.getClass().getMethod(methodName, args), arguments);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
}
private void log(Object pageObjectModel, Method method, String...arguments) {
try {
if((Boolean)method.invoke(pageObjectModel, (Object[])arguments)){
System.out.println("Success!");
} else {
System.out.println("Fail!");
}
} catch (Exception e){
e.printStackTrace();
}
}
public boolean something(String one, String two, String three){
System.out.println(one+", "+two+", "+three);
return true;
}
}
This seems to be perfect for what I'm trying to achieve. Although I don't really like reflection due to having bad experiences with it (giving problems with obfuscated code) I think it's fine for this project.
Thanks for helping me in the right direction!
Methods[] method =classname.getClass().getDeclaredMethods();
In the above code, i want to get the value of particular method. Suppose above method will return some getter and setter methods. Can we get the value of any getter methods?
Like PeterMmm said, you can use invoke on Method, passing the object that you want the call to be made on, and any other arguments the method needs, as get methods usually don't have arguments, you can do like this:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MethodsTest {
public int getA() {
return 5;
}
public int getB() {
return 8;
}
public static void main(String[] args) {
MethodsTest obj = new MethodsTest();
Method[] methods = obj.getClass().getDeclaredMethods();
for (Method method: methods) {
if (method.getName().startsWith("get"))
try {
System.out.println(method.getName() + ": " + method.invoke(obj));
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
it will print:
getB: 8
getA: 5
hope it helps
Would I do something like this?
Assert.assertTrue(value = int n);
Also, if I had a method such as:
public int get(){
return count;
}
What kind of a test case would I write for it? Would I write a test case testing if the return is an integer?
Regrading your first question - the return type is an int (so you don't need to verify that).
Regarding your second question, if it's just a getter, there's no need to write test for it.
Generally, unit test helps you to test bussiness logic and that a certain behavior of a building block of your app works.
If we'll take the count for example, you would probably want to build up a test case with actions that affects the count in a specific way and then assert on it's expected value.
You can look here for a nice tutorial on Unit Tests in Java.
Class for testing purpose:
public class Item {
private int number;
public Item(int number) {
this.number = number;
}
public int getNumber() {
return number;
}
}
Your test cases could be:
import static org.junit.Assert.*;
public class Test {
#org.junit.Test
public void testCase1() {
Item item = new Item(1);
assertEquals(1, item.getNumber());
}
#org.junit.Test
public void testCase2() {
Item item = new Item(2);
assertEquals(2, item.getNumber());
}
}
You can verify your business logic via assertEquals method.
How would I write a test case in Java to test that the input is an int?
Assuming your concern is someone could change input type of a method.
public final void testSet() {
try {
MyClass.class.getDeclaredMethod("set", new Class[] { Integer.TYPE });
} catch (NoSuchMethodException e) {
Assert.assertFalse(true);
} catch (SecurityException e) {
e.printStackTrace();
}
}
Would I write a test case testing if the return is an integer?
Assuming your concern is someone could change return type of a method. By the way you are returning int in your example so I have used Integer.TYPE, if you want to check for Integer then use Integer.class
public final void testGet() {
try {
java.lang.reflect.Method m = MyClass.class.getDeclaredMethod("get", null);
Assert.assertTrue(m.getReturnType() == Integer.TYPE);
} catch (NoSuchMethodException | SecurityException e) {
e.printStackTrace();
}
}
I tried using reflection to use a custom List View for an App with target api level 7. The necessary fileds are only available from api level 9 so I attempted to fix that via reflection.
I need to find the protected Method View.overScrollBy( int,int,int,int,int,int,int,int,boolean). When i call
View.getDeclaredMethods()
and iterate over the Method[] array i find it, but when I try
View.class.getDeclaredMethod(String name, Class...< ? > paramTypes)
I get a NoSuchMethodException. I compared the hard coded Method Name and parameterType values with the values extracted from the method (found via iteration) and they are identical...
private boolean initCompatibility()
{
Method[] methods = View.class.getDeclaredMethods();
try {
// The name of the Method i am looking for;
String OVERSCROLL_S = "overScrollBy";
for (Method meth : methods) {
if (meth.getName().equals(OVERSCROLL_S)) {
mListView_overScrollBy = meth;
break;
// method found
}
}
// Params for getDeclaredMethod(…)
String methodName = "overScrollBy";
Class[] methodParams = { Integer.TYPE, Integer.TYPE, Integer.TYPE,
Integer.TYPE, Integer.TYPE, Integer.TYPE, Integer.TYPE,
Integer.TYPE, Boolean.TYPE };
// works
Method test = View.class.getDeclaredMethod(methodName,methodParams);
// fails
View.class.getDeclaredMethod(mListView_overScrollBy.getName(),
mListView_overScrollBy.getParameterTypes());
/*
* I also tried this way around and again the first worked and the second
* failed, so the input arguments are not the problem...
* View.class.getDeclaredMethod( mListView_overScrollBy.getName(),
* mListView_overScrollBy.getParameterTypes() );
* Method test = View.class.getDeclaredMethod(methodName,methodParams);
*/
return true;
} catch (SecurityException e) {
e.printStackTrace();
return false;
} catch (NoSuchMethodException e) {
e.printStackTrace();
return false;
}
}
I do not understand why the call always works the first time and the does not the second time. Interestingly it also fails when i call only once for View.class.getDeclaredMethod(String name, Class...< ? > paramTypes) and it does not make any difference whether i use the hard coded input values or the one extracted from the method I am looking for...
Does anybody know what the problem is? Thanks
This is very interesting, but it is not Android-specific, I think.
I wrote this small test in plain Java:
public class ReflectionTest {
public static void main(String[] args){
Method[] m = ReflectionTest.class.getDeclaredMethods();
for (Method method : m) {
System.out.println(method.getName());
}
try {
Method m1 = ReflectionTest.class.getDeclaredMethod("d0", int.class, boolean.class);
if(m1 != null){
System.out.println("m1 found!");
}
} catch (Exception e) {
e.printStackTrace();
}
try {
Method m2 = ReflectionTest.class.getDeclaredMethod("d0", Integer.TYPE, Boolean.TYPE);
if(m2 != null){
System.out.println("m2 found!");
}
} catch (Exception e) {
e.printStackTrace();
}
try {
Class<?>[] carr = m[1].getParameterTypes();
Method m3 = ReflectionTest.class.getDeclaredMethod("d0", carr);
if(m3 != null){
System.out.println("m3 found!");
}
} catch (Exception e){
e.printStackTrace();
}
}
public void d0(int a, boolean b){
}
}
In eclipse, if I debug it, the three m1,m2 and m3 are printed. However, if I run it, a NoSuchMethodException is thrown when trying to get m3.
UPDATES:
Tested running with jre 7 under linux, and all three m1,m2 and m3 were printed. Perhaps is a problem with jre6? Or is eclipse run configuration?
Changed carr declaration to use method 0 instead of 1: Class<?>[] carr = m[0].getParameterTypes(); as Gray suggested. Now it runs ok but throws exception in debug mode. This means different method order for the returned array m.
Update #2 confirmed, I've included a for loop to print the method names. In run mode the order of the method array is reversed compared to debug mode.
I'm building an admin controller that work like a terminal emulator in Flex 4.5.
The server side is Red5 on a tomcat server using Java programming language.
When a user enter a command in his textinput, the command is sent to the red5, in red5 I check if the command exists and return a proper output or an error if the command or parameters don't match.
so for now i use if (command.equals("..") {} else if (command.equals(...
Is there a way to store the function name or a reference to the function that should be executed in each command and to execute it?
example:
// creating the hasmap
HashMap<String,Object> myfunc = new HashMap<String,Object>();
// adding function reference
myfunc.put("help",executeHelp);
or....
myfunc.put("help", "executeHelp"); // writing the name of the function
and then
void receiveCommand(String command, Object params[]( {
myfunc.get(command).<somehow execute the referrened function or string name ? >
}
any ideas?
thank you!
You could use reflection, but I suggest a easier way.
You can create an abstract class or interface with an abstract method execute. Example:
interface Command {
void execute(Object params[]);
}
class Help implements Command {
void execute(Object params[]) {
// do the stuff
}
}
Now your hashmap can be:
// creating the hasmap
HashMap<String,Command> myfunc = new HashMap<String,Command>();
// adding function reference
myfunc.put("help", new Help());
And then:
void receiveCommand(String command, Object params[]) {
myfunc.get(command).execute(params);
}
You can execute a function by name as follows:
java.lang.reflect.Method method;
try {
method = obj.getClass().getMethod(methodName, param1.class, param2.class, ..);
} catch (SecurityException e) {
// ...
} catch (NoSuchMethodException e) {
// ...
}
In the above snippet, param1.class, param2.class are the class types of the arguments of the method to execute.
Then:
try {
method.invoke(obj, arg1, arg2,...);
}
catch (IllegalArgumentException e) { }
catch (IllegalAccessException e) { }
catch (InvocationTargetException e) { }
There is lots more information about this here: http://java.sun.com/docs/books/tutorial/reflect/index.html
You can define an interface for your functions
interface Function {
public Object invoke(Object[] arguments);
}
and then public your code via this interface
public class Function1 implements Function {
public Object invoke(Object[] arguments) {
...
}
}
and store in the map
map.put("helpCommand", new Function1());
or store a reference using an anonymous class
Function theFunction = new Function() {
public Object invoke(Object[] arguments) {
return theRealMethod(arguments[0], String.valueOf(arguments[1]));
}
}
In the second example I showed how to use the anonymous class as an adaptor if the method you want to call has a different signature than your interface.