Friday, 28 September 2007

JSR-275 - Suitable for Java 7?

I've been evaluating JSR-275 - Units and Quantities tonight in relation to JSR-310. Its not been pretty.

JSR-275 - Units and Quantities

JSR-275 is a follow on from JSR-108, and aims to become part of the standard Java library in the JDK. It provides a small API for quantities and units, and a larger implementation of physical units, conversions and formatting. The goal is to avoid using ints for quantities, with their potential for mixing miles and metres.

So where's the problem?

Well, it starts with naming. There is a class called Measure which is the key class. But its what I would call a quantity - a number of units, such as 6 metres or 32 kilograms.

Measure is generified by two parameters. The first is the value type. You'd expect this to be a Number, but weirdly it is any class you like.

You'd expect the second generic parameter to be the unit type, right? Er, no. It's the Quantity. So, not only is the quantity class not called Quantity, there is another class which doesn't have much to do with a quantity that is called Quantity. Confused yet?

The unit class is at least called Unit. Unit also has a generic parameter, and again its Quantity.

So, what is this mysterious Quantity class in JSR-275?

Well, its the concept of length or temperature or mass. Now actually that is a very useful thing for JSR-275 to have in the API. By having it in the API, it allows the generic system to prevent you from mixing a length with a mass. Which must be a Good Thing, right?

 Measure<Integer, Length> distance = Measure.valueOf(6, KILO(METRES));
 int miles = distance.intValue(MILES);

The first thing to notice is that the variable is held using a Length generic parameter. There is no support in JSR-275 to specify the variable to only accept metres or miles. Personally, I think thats a problem.

The second thing to notice is how easy conversion is. It just happened when it was requested. But is that what you necessarily want? We just converted from kilometres to miles without a thought. What about lost accuracy? Are those units really compatible?

Well now consider dates and times:

 Measure<Integer, Duration> duration = Measure.valueOf(3, MONTHS);
 int days = duration.intValue(DAYS);

Er, how exactly did we manage to convert between months and days? What does that really mean? Well, assuming I've understood everything, what it does is use a definition of a year as 365 days, 5 hours, 49 minutes, and 12 seconds, and a month being one twelth of that. It then goes ahead and does the conversion from months to days using that data.

I doubt that the result will be what you want.

In fact, perhaps what has happened here is that we have substituted the unsafe int with a supposedly safe class. With JSR-275 we relax and assume all these exciting conversions will just work and nothing will go wrong. In short, we have a false sense of security. And thats perhaps one of the worst things an API can do.

I then looked at the proposal for integrating JSR-275 and JSR-310. One idea was for the JSR-310 duration field classes (Seconds, Minutes, Days, Months, etc) to subclass Measure. But that isn't viable as Measure defines static methods that are inappropriate for the subclasses. Measure also defines too many regular methods, including some that don't make sense in the context of JSR-310 (or at least the JSR-310 I've been building).

Looking deeper, what I see is that JSR-275 is focussed on conversions between different units. There is quite a complex API for expressing the relationships between different units (multipliers, powers, roots, etc). This then feeds right up to the main public API, where a key 'service' provided by the API is instant conversion.

But while conversion is important, for me it has come to dominate the API at the expense of what I actually wanted and expected. My use case is a simple model to express a quantity - a Number plus a Unit.

Alternative

So, here is my mini alternative to JSR-275:

 public abstract class Quantity<A extends Number, U extends Unit> {
  public abstract A amount();
  public abstract U unit();
 }
 public abstract class Unit {    // grams or celcius
  public abstract String name();
  public abstract Scale scale();
 }
 public abstract class Scale {   // mass or temperature
  public abstract String name();
 }
 
 Quantity<Integer, MilesUnit> distance = ...

So, what have we lost? Well, all the conversion for a start. In fact, all we actually have is a simple class, similar to Number, but for quantities. And the unit is very bare-bones too.

We've also lost some safety. There is now no representation of the distance vs duration vs temperature concept. And that could lead us to mix mass and distance. Except that the API will catch it at runtime, so its not too bad.

And what have we gained? Well its a much smaller and simpler API. But less functional. However, by being simpler, its easier for other teams like JSR-310 to come along and add the extra classes and conversions we want. For example, implementations of Quantity called Days and Months that don't provide easy conversion.

In essence, this is just the quantity equivalent of Number - thus its a class which the JDK lacks.

I'm sure there are many holes in this mini-proposal (I did code it and try to improve on it, but ran into generics hell again). The main thing is to emphasise simplicity, and to separate the whole reams of code to do with conversion and formatting of units. I'm highly unconvinced at the moment that the conversion/formatting code from JSR-275 belongs in the JDK.

Summary

Having spent time looking at JSR-275 I'm just not feeling the love. Its too focussed on conversion for my taste, and prevents the simple subclassing I actually need.

Opinions welcome on JSR-275!

Thursday, 27 September 2007

Java 7 - Update on Properties

Whilst I was on holiday there was a bit of discussion about properties in Java.

Shannon Hickey announced Beans Binding version 1.0. This is the JSR that intends to develop an API to connect together swing components in a simple and easy to use manner. The idea is that you call a simple API to setup a binding between a GUI field and your data model.

During development, a lot of discussion on the JSR was about the interaction with the possible language proposal for properties. As a result, the v1.0 API includes a Property abstract class, with a subclasses for beans and the EL language from J2EE.

Per-class or per-instance

One key issue that has to be addressed with a property proposal is whether to go per-class or per-instance. The per-class approach results in usage like Method or Field, where the bean must be passed in to obtain the value:

 Person mother = ...;
 Property<Person, String> surnameProperty = BeanProperty.create("surname");
 String surname = surnameProperty.getValue(mother);

Here, the surname property refers to the surname on all Person beans. The caller must pass in a specific person instance to extract the value of the surname.

On the other hand, the per-instance approach results in usage where the property itself knows what bean it is attached to:

 Person mother = ...;
 Property<Person, String> surnameProperty = BeanProperty.create("surname", mother);
 String surname = surnameProperty.getValue();

Here, the surname property refers to the specific surname property on the mother instance. There is thus no need to pass in any object to extract the value of the surname, as the property knows that it is attached to mother.

Which is better? And which did Beans Binding adopt?

Well, its actually the case that for the swing use case, the per-class approach is really the only option. Consider a table where each row is the representation of a bean in a list. The columns of this table are clearly going to be properties, but which type? Well, when defining the binding, we want to do this without any reference to the beans in the list - after all, the beans may change and we don't want to have to redefine the binding if they do.

So, we naturally need the per-class rather than per-instance properties. And, that is what the Beans Binding project defines.

Is there ever a need for the per-instance approach? In my opinion yes, but that can be added to the per-class based property functionality easily.

Adding language support

Having seen the Beans Binding release, Remi Forax released a version of his property language compiler that integrates:

 Person mother = ...;
 Property<Person, String> surnameProperty = LangProperty.create(Person#surname);
 String surname = surnameProperty.getValue(mother);

As can be seen, the main advantage is that we now have a type-safe, compile-time checked, refactorable property Person#surname. This is a tremendous leap forward for Java, and one that shouldn't be underestimated. And the # syntax can easily be extended to fields and methods (FCM).

But, why do we see this new class LangProperty? Well, Remi's implementation returns an object of type java.lang.Property, not a Beans Binding org.jdesktop.Property. This fustrating difference means that two classes named Property exist, and conversion has to take place.

What this really demonstrates is the need for Sun to declare if Java 7 will or won't contain a language change for properties, and for a real effort to occur to unify the various different JSRs currently running in the beans/properties arena. It would be a real disaster for Java to end up with two similar solutions to the same problem (although I should at least say I'm very happy that the two parts are compatible :-).

Summary

Properties support in Java is progressing through a combination of JSR and individual work. Yet Sun is still silent on Java 7 language changes. Why is that?

Opinions welcomed as always!