Python's first-class functions make life easier in so many ways. A Java programmer will say, "I can do the same thing with interfaces and anonymous inner classes," but it's just not the same.
The most obvious use is for callbacks of any kind. The simplest example is a function that runs a command. In Python, the command can be passed directly into the runner as a function. For example:
def run(cmd):
cmd()
def myCommand():
print 'myCommand'
run(myCommand)
You can pass an instance method to runner using a bound method:
class RunnerClient:
def useRunner(self):
run(self.command)
def command(self):
print 'RunnerClient.command'
RunnerClient().useRunner()
This can easily be extended to pass any number of arguments to the command given to runner, and to return any number of results:
def run(cmd, *args):
return cmd(*args)
def myCommand(x, y):
print 'x = %d, y = %d' % (x, y)
return x+10, y+10
z, w = run(myCommand, 10, 20)
print z, w
Alternatively, the command arguments can be bound to a function of no arguments using lambda:
def run(cmd):
return cmd()
def myCommand(x, y):
print 'x = %d, y = %d' % (x, y)
return x+10, y+10
z, w = run(lambda : myCommand(10, 20))
That's easy! What does it look like in Java? Let's start with the simplest case above. We need an interface for the command, a Runner class and an instantiation of the command. I made Command and Runner inner classes to keep everything in one module; normally they would be top-level classes. Here is the code:
public class RunnerClient {
private interface Command {
public void execute();
}
private static class Runner {
public static void run(Command cmd) {
cmd.execute();
}
}
public void useRunner() {
Command cmd = new Command() {
public void execute() {
System.out.println("myCommand");
}
};
Runner.run(cmd);
}
public static void main(String[] args) {
new RunnerClient().useRunner();
}
}
Yikes! That's nasty. So much noise! There is the visual noise of the extra punctuation and type declarations, and the conceptual noise of class declarations.
If you want the callback to be a member function, it's not too much different. The anonymous inner class can forward to the method of the main class:
public class RunnerClient {
private interface Command {
public void execute();
}
private static class Runner {
public static void run(Command cmd) {
cmd.execute();
}
}
public void useRunner() {
Command cmd = new Command() {
public void execute() {
command();
}
};
Runner.run(cmd);
}
public void command() {
System.out.println("RunnerClient.command()");
}
public static void main(String[] args) {
new RunnerClient().useRunner();
}
}
To pass arguments to the command...I'm not sure I want to think about that. Probably the simplest solution is to make a specific Command implementation to hold the arguments to the command. You need a different helper class for each command signature. Maybe there is a way to do it with introspection. Any way you cut it, it's going to be painful.
|