1

I'm looking for a way to call any given method in my class without having to do the whole try-catch statement.

EXAMPLE:

public void Worker(String handle) throws Exception
{
    if(PROTOCOL.contains(handle)) 
    {
        System.out.println("good");
        Worker.handle;
    }
    else { 
            throw new Exception("Don't understand <" + handle + ">");
         }
}

PROTOCOL is a list of allowed commands.

I know I can say if(input = ....){do....} but I want to be able to do the above; call my class with the input value.

Is this possible?

Simo
  • 955
  • 8
  • 18
1nn15
  • 13
  • 1
  • 3
  • 1
    Have a look at Reflection: https://docs.oracle.com/javase/tutorial/reflect/member/methodInvocation.html – Steve Smith Sep 06 '18 at 15:41
  • Throwing generic Exception is a design problem. Consider throwing an unchecked exception - `IllegalStateException` seems reasonable for the posted code. If `handle` could be changed to an enum then the only problem would be if it's null. – Andrew S Sep 06 '18 at 15:49
  • haha yeah I thought that it was to general. Ill change it to IllegalStateException for sure – 1nn15 Sep 06 '18 at 16:03
  • 1
    `IllegalArgumentException` will be better here as `handle` is passed as argument – rkosegi Sep 06 '18 at 16:08
  • What would the difference be with state and argument? I get the obvious implication, but ist state more general vs argument is like inputting int when requiring a string? – 1nn15 Sep 06 '18 at 16:19

3 Answers3

1

Depending on what your commands would look like you could use a Map<String, Command> which you'd then use like this:

Map<String, Command> PROTOCOL = ... //you build that map somehow

Command c = PROTOCOL.get(handle);
if( c != null ) {
    System.out.println("good");
    c.execute();
} else { 
    throw new Exception("Don't understand <" + handle + ">");
}

Command could then be a class or function interface:

interface Command {
  void execute();
}

Used as a class interface

class MyCommand implements Command {
  //this can have private data

  void execute() {
    //do whatever is needed
  }
}

PROTOCOL.put("mycommand", new MyCommand(/*you could pass parameters here*/));

Advantages:

  • The interface can have more than 1 method, e.g. it could have a String getName() as well.
  • Commands can have parameters, e.g. you could provide one implementation and use different names that are bound to the same command with different parameters (e.g. a "increment" and "decrement" could be bound to AddCommand(1) and AddCommand(-1)).
  • You could use some dependency inversion mechanism (e.g. via CDI) to have commands register themselves to PROTOCOL. That way you could even add commands via some plugin mechanism.
  • Using interfaces for classes might be easier to grasp for others.
  • Easier to structure for larger commands as you can easily extract the classes into separate files.

Used as a function interface (e.g. via lambdas)

PROTOCOL.put("mycommand", () -> { 
  //do whatever is needed
});

Advantages:

  • No classes needed which can be more concise for short commands.
Thomas
  • 87,414
  • 12
  • 119
  • 157
  • So you're saying that I should make my protocol list a hashmap with two prarams, (name, method) then I can call it that way or make an interface. – 1nn15 Sep 06 '18 at 16:02
  • @1nn15 As far as I understand your question (which is quite short) your protocol would basically consist of a list of "keywords" that are _mapped_ to some code. Thus using a map (which could be a hashmap) seems to be a good fit. You'd need the interface as well (i.e. not "or" but "and") as the map's value type. – Thomas Sep 06 '18 at 16:05
  • ahh ok, I get that. – 1nn15 Sep 06 '18 at 16:05
  • I guess trying to be lazy only makes things more complicated XD – 1nn15 Sep 06 '18 at 16:06
  • `Command` interface is unnecessary here, you can use `Runnable` or `Callable` – rkosegi Sep 06 '18 at 16:09
  • @rkosegi Please clarify if you can – 1nn15 Sep 06 '18 at 16:11
  • @1nn15 : here `Command` interface declares essentially same contract as already exists in JRE : `java.lang.Runnable` or `java.util.concurrent.Callable`. – rkosegi Sep 06 '18 at 16:47
  • @rkosegi that depends, the problem is that I just used the minimum for what a command might look like but the OP might actually need more. Besides that, using a specialized interface might help reduce errors and document intent. – Thomas Sep 07 '18 at 07:08
0

If you do not want to use try-catch (handle exceptions yourself) then declare those exceptions on the calling method followed after throws keyword.

https://docs.oracle.com/javase/tutorial/essential/exceptions/declaring.html

Basically you are delegating the exception handling to the caller of your method.

JRG
  • 4,037
  • 3
  • 23
  • 34
0

Here is a simple working example showing some things that you can do using reflection.

Note: I decided to store available methods in a list, but this could be easily modified to use a predefined list of Strings. I also threw an unchecked IllegalArgumentException from the worker method.

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Worker {

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

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

    public void worker(String handle) throws IllegalArgumentException {
        Class<? extends Worker> clazz = this.getClass();
        List<Method> methods = Arrays.asList(clazz.getMethods());

        if (methods.stream().map(m -> m.getName()).collect(Collectors.toList()).contains(handle)) {
            try {
                Method method = clazz.getDeclaredMethod(handle);
                method.invoke(this);
            } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
                e.printStackTrace();
            }
        } else {
            throw new IllegalArgumentException("Don't understand <" + handle + ">");
        }
    }

    public static void main(String[] args) {
        new Worker().worker("method1");
        new Worker().worker("method2");
        new Worker().worker("method3");
    }
}
Josh Sullivan
  • 360
  • 3
  • 10