Sunday, January 16, 2011

Anonymous classes

In my day-to-day work using Java, one of the most frequently used and useful features is anonymous inner classes. Here's a code snippet showing a typical use case, in this case an asynchronous load of a resource:

final FormattingLogger logger = getLogger();
resources.loadImageAsync("texture.png", new AsyncCallback<Image>() {
  @Override
  void onSuccess(ImageResource img) {
    // do something with img
  }

  @Override
  void onFailure(Throwable error) {
    logger.logError("Resource load failed: " + error);
  }
}

What this does is create an unnamed subclass of AsyncCallback<Image>. AsyncCallback in this case is a generic interface that has two methods: onSuccess and onFailure.

Since Java doesn't support closure functions (although apparently the next version of Java will), anonymous inner classes are used instead. They also have one obvious advantage over a function pointer, which is the ability to support multiple methods, such as the onSuccess & onFailure case in the example above.

Like a closure, the anonymous inner class has access to the environment in which it is defined, as seen in the use of 'logger' above. Note that Java doesn't support true capturing of variables, however - any variable referenced by the inner class must be declared 'final'. (The reason is that Java makes a copy of the variable, rather than giving shared access - so making it immutable reduces the confusion that would result if you tried to modify the variable.)

Although the example doesn't show it, you can also pass constructor parameters to the anonymous class in the parentheses following the class name. Of course, you would also need to declare a constructor with the appropriate type signature.

Now, there's no reason why you couldn't create a named subclass of AsyncCallback to do exactly the same thing. It's just more typing, and it also requires the subclass to be defined elsewhere, outside of the place where it is used.

It seems logical that we would want a similar feature in Tart. However, the above syntax won't work, because Tart doesn't have the "new" keyword. Simply typing "classname() { ... }" won't work either, because the syntax is ambiguous - as in the case "if classname() { ... }". So we'll need to come up with some other syntax.

Also, I suspect that anonymous classes, while still useful, will be used somewhat less frequently in Tart than in Java, due to the existence of closures. Assuming that both closures and anonymous classes are available, a programmer who is designing a callback API would need to consider the following factors in deciding which to use:
  • If there are multiple methods in the callback, then then an anonymous class would seem a better fit. That being said, it is possible to either use multiple closures (one for each callback method), or to define a single closure whose parameters allow the passing of different meanings (such as an status code + result.) However, this rapidly becomes unwieldy as the number of methods increases.
  • Some would argue that both the class name and the method name of the callback are an important part of the self-documentation of the callback code. If I define an interface that is named "AsyncCallback" with a method "onSuccess", that tells me more than if I use a generic "Function" type.
Still, even with the above caveats, programmers are going to choose closures a large part of the time because of their convenience, especially for interfaces where there's only a single method. So the need to provide anonymous classes isn't quite as crucial as it would be in Java.

No comments:

Post a Comment