Ever wondered whether Java lets you play around with method "pointers" as C or C++ do? The Java Reflection API provides the necessary support for handling any high-level Java construct (class, method, field, etc) as a normal Java object that you can inspect and manipulate.
A couple of days ago, David Shay explained how he came to know about this technique and commented on the runtime overhead due to reflection. I won't go into that, I just would like to show one concrete example of how method pointers could be used in practice. Since every problem usually has many solutions, the code example I'm going to discuss below is also implementable without reflection, of course :)
For this example, I have been inspired by Lars Haendel's tutorial on C/C++ function pointers. The code shown in the table below is an equivalent Java implementation of his code with some enhancements that I describe hereafter.
1. import java.io.*;
2. import java.lang.reflect.Method;
3. public class TestMethodPtr {
4.
5. public static float plus(float left, float right) {
6. return left + right;
7. }
8.
9. public static float minus(float left, float right) {
10. return left - right;
11. }
12.
13. private static Method getOperation(char opCode) throws Exception {
14. if (opCode == '+') return getMethod("plus");
15. if (opCode == '-') return getMethod("minus");
16. return null;
17. }
18.
19. private static Method getMethod(String name) throws Exception {
20. Class cl = TestMethodPtr.class;
21. Class[] params = new Class[] {Float.TYPE, Float.TYPE};
22. return cl.getMethod(name, params);
23. }
24.
25. private static char readChar() throws IOException {
26. BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
27. System.out.print("Type in the operation (+/-): ");
28. return (char) in.read();
29. }
30.
31. public static void main(String[] args) throws Exception {
32. // define a method pointer and the arguments
33. Method methodPtr;
34. Object[] arguments = {new Float(2f), new Float(4f)};
35.
36. while (true) {
37. // get the method pointer
38. methodPtr = getOperation(readChar());
39.
40. if (methodPtr == null) {
41. System.out.println("Bye bye!!");
42. break;
43. }
44.
45. // call the method using the pointer
46. System.out.println(" Result: " + methodPtr.invoke(null, arguments));
47. }
48. }
49. }
|
Output:
D:\>run
Type in the operation (+/-): +
Result: 6.0
Type in the operation (+/-): +
Result: 6.0
Type in the operation (+/-): -
Result: -2.0
Type in the operation (+/-): -
Result: -2.0
Type in the operation (+/-): +
Result: 6.0
Type in the operation (+/-): q
Bye bye!!
D:\>
|
The goal of the TestMethodPtr class is to ask the user what kind of operation she would like to perform. If she wants to do an addition, she types in '+' (without the quotes) at the prompt. For a subtraction, she will input '-' instead (see method readChar(), lines 25-29). Any other character will shut down the program. Upon receiving a character, the program will determine which method should be invoked: plus() for '+' and minus() for '-' (see method getOperation(), lines 13-17). Finally, when the appropriate method is returned, it is always invoked with the same fixed argument values, namely 2 and 4. Agreed, this is not really useful, but letting the user specify the arguments would have unnecessarily complicated the example without bringing much more value. This example just aims at demonstrating the parallel between C/C++ function pointers and Java Method references. As I mentioned before, the same result can be achieved without method pointers as depicted in the code below.
1. import java.io.*;
2. import java.lang.reflect.Method;
3. public class TestMethodPtr {
4.
5. public static float plus(float left, float right) {
6. return left + right;
7. }
8.
9. public static float minus(float left, float right) {
10. return left - right;
11. }
12.
13. private static char readChar() throws IOException {
14. BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
15. System.out.print("Type in the operation (+/-): ");
16. return (char) in.read();
17. }
18.
19. public static void main(String[] args) throws Exception {
20. while (true) {
21. char opCode = readChar();
22. if (opCode == '+') {
23. System.out.println(" Result: " + plus(2f, 4f));
24. } else if (opCode == '-') {
25. System.out.println(" Result: " + minus(2f, 4f));
26. } else {
27. System.out.println("Bye bye!!");
28. break;
29. }
30. }
31. }
32. }
|
Output:
D:\>run
Type in the operation (+/-): +
Result: 6.0
Type in the operation (+/-): +
Result: 6.0
Type in the operation (+/-): -
Result: -2.0
Type in the operation (+/-): -
Result: -2.0
Type in the operation (+/-): +
Result: 6.0
Type in the operation (+/-): q
Bye bye!!
D:\>
|
Personally, I have very seldomly encountered the case where I needed to pass references to Method objects around in my applications. As I said in another blog, if you need to do this, you probably have to rethink your design. It's not because a feature is available that you must use it blindly.