Wednesday, July 21, 2010

Lambdas in Java Preview - Part 5: Apache Wicket

This is the fifth part in a series of blog posts (read the previous part) giving some practical examples of lambdas, how functional programming in Java could look like and how lambdas could affect some of the well known libraries in Java land. This time I'll have a look at how the addition of lambdas could possibly change the API of the Apache Wicket web framework.

If you are not familiar with Wicket, I can't say how much sense it makes for you to read this article. But Wicket is a component oriented web framework and its programming model in some ways is similar to that of the Swing framework. Some of the ways lambdas will change Wicket could also apply to the Swing framework, so maybe this article actually is still useful for you.

The reason, why Wicket is a good example of how the introduction of lambdas into the Java language might change an API, is that it makes heavy use of anonymous inner classes. Now, anonymous inner classes in Java are often painful to look at and one of the primary reasons to add lambdas to Java is to reduce the need for anonymous inner classes, or better to simplify their usage. This is done mainly through SAM conversion, which means that a lambda expression or anonymous function can be converted to a SAM type, i.e. an interface or abstract class with a single abstract method, when it appears in a position where a SAM type is expected (see previous parts for examples). And as Wicket makes heavy use of implementing abstract classes anonymously (and some of these classes actually are simple callback classes that in fact are SAM types) it seems that there could be a lot of places where we can replace these anonymous inner classes with lambda expressions.

AbstractReadOnlyModels

One of the classes that are mostly implemented anonymously is AbstractReadOnlyModel. It has a single abstract method getObject(). In other words, it's a SAM type.
IModel<String> nameModel = 
   new AbstractReadOnlyModel<String>() {
     @Override
     public String getObject() {
       Person person = personModel.getObject();
       return person.getFirstName() + ", " 
                + person.getLastName();
     }
  };

TextField<String> name = new TextField<String>("name", nameModel);
The important part of this is just the body of the getObject method, but about the same amount of code is just noise. With lambdas we could remove most of this noise.
AbstractReadOnlyModel<String> nameModel = #() {
  Person person = personModel.getObject();
  return person.getFirstName() + ", " 
           + person.getLastName();
};

TextField<String> name = new TextField<String>("name", nameModel);
Here we basically replaced the anonymous inner class with a lambda expression, which has the same body as the getObject() method before. This lambda expression will automatically be converted to an AbstractReadOnlyModel.

One thing to note is, that the compiler must have a chance to infer the target SAM type. In the example above we explicitly declared the variable to be of type AbstractReadOnlyModel, but for example, if you call the constructor TextField(String wicketId, IModel model), you cannot pass a lambda expression inline like this

TextField<String> name = new TextField<String>("name", #() {
  Person person = personModel.getObject();
  return person.getFirstName() + ", " 
           + person.getLastName();
});
because IModel is not a SAM type and the compiler cannot infer that you want the lambda to be converted to an AbstractReadOnlyModel.


LoadableDetachableModels

Another commonly used model class is LoadableDetachableModel, which has the load() method as its single abstract method.
final Long personId = ...
IModel<Person> personModel = 
  new LoadableDetachableModel<Person>() {
    @Override
    protected Person load() {
      return getPersonDao().get(personId);
    }
};
So, same as with the AbstractReadOnlyModel, we can replace it with a lambda expression.
final Long personId = ...
LoadableDetachableModel<Person> personModel = #() (getPersonDao().get(personId));

Components

Components are another essential type of classes in Wicket. There are some Wicket components that are SAM types, especially those that handle some kind of event. Often such components are implemented as anonymous inner classes. Take for example an AjaxButton
Button submit = new Link("submit", form) {

 @Override 
 protected void onSubmit(AjaxRequestTarget target,
   Form<?> form) {
  System.out.println("form submitted");
 }

};
Its single abstract method is onSubmit(), but there is an important difference to the SAM types shown before. Components don't have a no-arg constructor, they all take at least a wicketId as a constructor argument. The proposals don't give an answer if to permit an argument list for other than the no-arg constructor for SAM conversion. If it would be allowed, it would possibly look like this
AjaxButton submit = 
    AjaxButton("submit", form)#(
      AjaxRequestTarget target, Form<?> form){
        System.out.println("form submitted")
      };
On the other hand, a future version of Wicket could be designed differently, in order to better support SAM conversion. Let's build a sub class of AjaxButton that relies more on composition instead of inheritance (Wicket in general is the other way round, though). Our subclass, instead of having to override onSubmit, it could take a callback argument:
public class MyAjaxButton extends AjaxButton {

private IAjaxButtonOnSubmitCallback onSubmitCallback;

public MyAjaxButton(String id, Form<?> form,
  final IAjaxButtonOnSubmitCallback onSubmitCallback) {
 super(id, form);
 this.onSubmitCallback = onSubmitCallback;
}

@Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
 onSubmitCallback.onSubmit(target, form);
}
IAjaxButtonOnSubmitCallback is a SAM type
public interface IAjaxButtonOnSubmitCallback 
    extends IClusterable {

  void onSubmit(AjaxRequestTarget target, Form<?> form);

}
so we can call the constructor of our button with a lambda expression as the last argument (here with the arrow syntax and type inference of the latest proposal)
AjaxButton submit = 
  new MyAjaxButton("submit", form, { target, form -> 
    System.out.println("form submitted");
  });
The same principle (composition over inheritance) would work for a lot of other Wicket components, generally for all components that have some sort of callback mechanism for certain events, e.g. form submit events, form validation error events, link clicked events, dropdown selection changed events and so on. If all these events were executed by callbacks instead of by abstract methods, then SAM conversion could be used efficiently.

In general, there are two cases, where we could think about introducing additional callback classes: First, if a class is a SAM type, but does not have a no-arg constructor, we could replace the single abstract method with a call to a callback we give to that component. And second, if a class isn't a SAM type, but has more than one abstract method instead, we could provide a callback for each of these abstract methods. This would quite change the design of Wicket, but would better support SAM conversion.

Another class of components where this principle applies, is all sorts of populators. Take for example the columns of a DataTable.
IColumn<Person> personColumn = 
  new AbstractColumn<Person>(Model.of("Person")) {

  @Override
  public void populateItem(
    final Item<ICellPopulator<Person>> cellItem,
    final String componentId, final IModel> rowModel) {
     ...
Again, AbstractColumn does not have a no-arg constructor, so we (probably) cannot use SAM conversion directly. But instead we could write our own sub class of AbstractColumn that takes an IColumnPopulator (a SAM type) and calls it from its populateItem method.
IColumn<Person> personColumn = new AbstractColumn<Person>(Model.of("Person"), IColumnPopulator populator) {

 @Override
 public void populateItem(
   final Item<ICellPopulator<Person>> cellItem,
   final String componentId, final IModel> rowModel) {
    populator.populateItem(cellItem, componentId, rowModel);
  }
A column could then be created a bit more concise like this:
IColumn<Person> personColumn = 
    new MyAbstractColumn<Person>(Model.of("Person"), 
     { cellItem, componentId, rowModel ->
 ...
});

In summary, Wicket has lots of opportunities to make use of SAM conversion, which would reduce the need for anonymous inner classes. In cases where these SAM types don't have a no-arg constructor it is not clear yet, if SAM conversion can be applied to them. For these types a different design which prefers composition over inheritance would allow SAM conversion, though. Same is true for classes that have more than one single abstract method, which could use callback classes instead of abstract methods.




3 comments:

Anonymous said...

Definitively nice yeah. Same same but different compared to how e.g. Scala can bring down the noise when using it for Wicket: http://technically.us/code/x/the-escape-hatch/

Magento Templates said...
This comment has been removed by the author.
Magento Templates said...

@Chillenious

For sure would agree as to what you wrote, the reviews mentioned about Java are just great... Well done

Magento Themes