Monday 8 December 2014

What might a Beans v2.0 spec contain?

What features should be in scope and out of scope for a Beans specification v2.0?

Note: I am an independent blogger, not an Oracle employee, and as such this blog is my opinion. Oracle owns the JavaBeans trademark and controls what actually happens to the specification.

Update 2015-01-03: New home page for the Beans v2.0 effort now available!

JavaBeans v1.0

My last blog examined the contents of the current JavaBeans (TM) specification. The key feature elements can be summarized as follows:

  • Properties - accessed using getter and setter methods
  • Events - change events for properties
  • Methods - all public methods
  • BeanInfo - permits customization of properties, events and methods
  • Beans - a utility class to access the features
  • Property editor - a mechanism to edit properties, including conversion to and from a string format
  • GUI support - allows a bean to be manipulated visually

These features were a good fit for 1997, given that the original goal was to be a component system, interoperating with other similar systems like COM/DCOM. These features are no longer what we need, so any Beans v2.0 is naturally going to look a lot different.

Beans v2.0 - Data abstraction

The key goal of a Beans v2.0 spec is to define an abstraction over the data in an object.

When such an abstraction exists, frameworks and libraries become far simpler to write and interact with. For example, a serialization framework at its most basic level simply needs to iterate over all the beans and properties in an object graph in order to perform its task.

In addition to frameworks, many ad-hoc coding tasks become simpler because it is much easier to walk over the object graph of beans and properties. For example, to find all instances of a particular type within an object graph starting from an arbitrary bean.

The data abstraction needs to be fully round-trip. The spec must allow for data to be read from a bean into another format. But it must also provide the tools to allow the original bean to be recreated from the data previously read.

Beans v2.0 - Mutable vs Immutable

The JavaBean v1.0 specification implicitly defines mutable objects. It requires all beans to have a no-args constructor, thus the only way for the properties to get populated is through the use of setters. This mutability is one of the key complaints that people have about JavaBeans, and often Java itself by association.

Any Beans v2.0 specification must support immutable objects.

Immutable classes are now a common design approach to systems and are increasingly popular. Supporting them is thus vital to encompass the range of data objects in use today.

Any Beans v2.0 specification must also still support mutable objects. While there are those that turn their nose up at any mutability, it is still a perfectly valid choice for an application. The decision between mutable and immutable data objects is a trade-off. The Beans v2.0 spec needs to support both options.

The implications of supporting immutable is that an alternative to a no-args constructor and public setters will be needed to support construction. This is likely to be a "bean builder" of some kind.

Beans v2.0 - Beans and Properties

The term "bean" is often associated with a specific Java class, such as Person. The term "property" is often associated with a single field on the class, such as surname or forename.

Any Beans v2.0 spec must not have a reflection-based view of beans and properties.

Firstly, a property is not the same as a field (instance variable). Fields hold the state of an object, but not necessarily in the form that the object wishes to expose (it might be stored in an optimised format for example). As such, it must be possible for a bean to control the set of properties that it publishes.

Secondly, it can be desirable to a bean with a dynamic set of properties. This might consist of a bean with a fixed set of base properties and an extensible map of additional ones, or a bean which is entirely dynamic, much closer to the data model of many dynamic languages.

The implications of this is that there is not a one-to-one mapping between a bean and a Class, nor is there a one-to-one mapping between a property and a java.lang.reflect.Field.

The JavaBeans v1.0 spec treats indexed properties in a special way. I can see no reason to do that in Beans v2.0, particularly given the range of collection types now in use.

Beans v2.0 - Getters and Setters

The current JavaBeans v1.0 spec requires there to be a physical java.lang.reflect.Method instance for both the getter and setter. What Beans v2.0 needs to provide is an abstraction away from this, as it provides many more options.

Any Beans v2.0 spec must not require data access via a java.lang.reflect.Method.

Moving away from direct exposure of reflection raises the level of abstraction. It permits alternative object designs to be exposed as beans.

For example, consider a bean that has no public getters and setters. With the Beans v2.0 abstraction, this could still expose its data as properties, providing interoperation with frameworks, but encapsulating the data for most programmatic uses. This tackles another big criticism of JavaBeans, which is that they cause state to be exposed rather than hidden.

Another example is the HashMap based bean. In this case a Method does not exist for each property, just a single get by name. Being able to expose this as a bean provides the desired dynamic behaviour.

The implications of this are simply that the Beans v2.0 spec abstraction will simply be long the lines of the Map interface get and put.

Beans v2.0 - Annotations

The current JavaBeans v1.0 spec predates annotations in Java. But annotations are now key to many programming practices in Java.

Any Beans v2.0 spec must provide access to property and bean annotations.

Annotations are represented by implementations of the Annotation interface. It so happens that the Annotation interface is not part of reflection. It is possible to implement an annotation yourself (apart from a compiler warning).

What is needed is for the Beans v2.0 spec abstraction to provide access to the set of annotations on the bean, and the set of annotations on each property without going via reflection. With this step, and the ones above, many direct uses of reflection will completely disappear, with the Beans v2.0 abstraction being good enough.

Beans v2.0 - Events

The current JavaBeans v1.0 spec considers events to be first class parts of the abstraction.

There is no good reason to include events in the Beans v2.0 spec.

The current JavaBeans v1.0 spec is GUI focussed, and as such events were an essential element. But the Beans v2.0 spec outlined here focuses on a different goal, abstracting over the data in the object. Round-tripping data from a bean to another format and back to a bean does not require events or a GUI.

There will be those reaching for the comments box right now, demanding that events must be included. But think carefully and you should realise that you don't need to.

None of what is proposed here for Beans v2.0 takes away what works today with JavaBeans v1.0. GUI developers can still carry on using the same getters, setters and events that they do today, including bound and constrained properties. The java.beans package will continue to exist and continue to be available. In addition, JavaFX is the way forward for Java GUIs, and it has its own approach to properties.

Beans v2.0 - Conversion to/from String

The current JavaBeans v1.0 spec includes PropertyEditor. One of the use cases of the class, notably in Spring framework, is to convert a simple object (typically a VALJO) to and from a String. For example, this includes Integer, Enum, Class and Currency.

Any Beans v2.0 spec must tackle conversion to/from a String for simple types.

The most common use case for the Beans v2.0 spec will be to iterate over the object graph and process it in some way, such as to write out a JSON or XML message. In order for this to be practical, all objects in the object graph need to be either a bean, or a simple type convertible to a string. As such, simple type string conversion is very much needed to round out the spec.

The Joda-Convert project contains a simple set of interfaces and classes to tackle this problem, and I imagine any Beans v2.0 spec solution would be similar.

Beans v2.0 - But what does a bean actually look like?

Part of the goal of Beans v2.0 is to allow for different kinds of bean. Immutable as well as mutable. Private getters and setters as well as public. Dynamic map-based structures as well as field-based ones.

Any Beans v2.0 spec should not overly restrict how beans are implemented.

We know that Oracle has no current plans for properties at the language level. As such, it also makes little sense for Beans v2.0 spec to specify a single approach for how classes should be designed. Instead, the spec aims to allow classes that are designed in different ways to all agree on a common way to interact with frameworks and libraries that does not require getters, setters and a no-args constructor.

The Joda-Beans, Lombok, Immutables and Auto projects (and many others) all provide different ways to create beans without writing manual code. All could be adapted to work with the proposed Beans v2.0 spec, as could frameworks like Jackson, Hibernate, Dozer and Groovy (to name but a few).

The simple answer is that the bean could look just like it does today with getters and setters. The real answer is that the bean may look any way it likes so long as it can expose its data via the Beans v2.0 API.

Summary

The current JavaBeans v1.0 spec is very old and not that useful for the kinds of applications we build today. This blog has outlined my opinion on what direction a Beans v2.0 spec should take:

  • Focussed on data abstraction
  • Support for Mutable and Immutable beans
  • Fully abstracted from reflection
  • No requirement for getters and setters
  • No requirement for a no-args constructor
  • Support for dynamically extensible properties
  • Access to bean and property annotations
  • Conversion of simple types to and from a String
  • No support for events

Feel free to comment on the high-level scope outlined above. In particular, if you develop a project that uses the JavaBeans spec (directly or indirectly), please comment to say whether the proposed spec is likely to be sufficient to cover your use cases.

I've also started work on a prototype of the code necessary to support the spec. So also feel free to raise pull requests or join the mailing list / group.

13 comments:

  1. Short answer: immutable case classes with automatic lenses.

    case class Person(String name, int age, Address address);
    case class Address(String firstLine, String secondLine = "", String thirdLine = "", String fourthLine = "", String postcode);

    Person person = Person(name="Arthur Putey", age=31, address=Address(firstLine="22 Acacia Avenue", secondLine="Sunderland", postcode="VB6 5UX"));

    assertThat(Person.age.get(person), equalTo(31));
    assertThat(Person.address.postcode.get(person), equalTo("VB6 5UX"));
    assertThat(Person.address.postcode.set(person, "RA8 81T"), equalTo(
    Person(name="Arthur Putey", age=31, address=Address(firstLine="22 Acacia Avenue", secondLine="Sunderland", postcode="RA8 81T")));

    Person updated = person.copy(name = "Agnes Yentob");
    assertThat(updated, equalTo(
    Person(name="Agnes Yentob", age=31, address=Address(firstLine="22 Acacia Avenue", secondLine="Sunderland", postcode="VB6 5UX")));

    ReplyDelete
    Replies
    1. This is a blog post about Java, not Scala or any other JVM language. What would be more helpful would be an assessment of whether the Beans v2.0 spec outlined above would help or hinder interoperability with Scala. I believe it would help as Scala could declare properties however it wants, and yet those properties could be accessed by any JVM framework based on the Beans v2.0 spec.

      Delete
    2. I think we are hamstrung by the language. After the introduction of value types we may be less hamstrung. Meanwhile, in the Java we actually have now...

      Property lookup should start with public get[Name] methods, then [name] methods (getName() and getValue() are not obviously better than name() and value() - the "get" is primarily there to distinguish the method from "set", which needn't be there if the bean's immutable), then public [name] fields, so you could have

      public class Person {
      public final String name;
      public final int age;

      public Person(String name, int age) {
      this.name = name;
      this.age = age;
      }
      }

      as the minimal implementation of an immutable bean. For a mutable bean, I guess:

      public class Person {
      public String name;
      public int age;

      public Person(String name, int age) {
      this.name = name;
      this.age = age;
      }
      }

      If there's no no-arg constructor, then you have to guide XML/JSON/ORM mappers a bit. In Java 8 it's possible to expose parameter names, which could be used to look up field names and thence any field annotations. Otherwise you're stuck with parameter annotations. Either way is a bit brittle, as it depends on parameter names or annotations matching up with field names, in a non-compile-time-checkable way. Alternative you can do truly evil things with bytecode and create an instance without calling its constructor and then set its fields reflectively.

      Delete
    3. Thanks for the response. I would like to see classes with public fields be able to be represented by the new meta-model. I'm currently thinking of a bean builder interface to handle construction, as that can cope with mutable and immutable.

      Delete
  2. (See http://akisaarinen.fi/blog/2012/12/07/boilerplate-free-functional-lenses-for-scala/ for a Scala implementation)

    ReplyDelete
  3. Wow, I never really thought much about that, but if creating beans becomes easier (using only bare fields or a simple Map), then language-level support for getters and setters does become redundant.

    It would be nice to have more utilities to manipulate beans too. EL and many frameworks can access Maps, Lists, Beans and sometimes even bare fields (such as JPA) transparently, but when you need to do that yourself, the code is never clean and is full of reflection and manual conversion.

    ReplyDelete
    Replies
    1. A good beans v2.0 spec would open up the world of open source to innovate to find new ways to represent beans, not just via getters and setters. I suspect we'd have some interesting innovation!

      Delete
  4. Automatic hashcode/equals/toString from fields whould be useful too

    ReplyDelete
    Replies
    1. With the right property abstraction, you can write an abstract superclass which uses the property information from the subclass to produce a general -purpose equals/hashCode/toString. I would say that enabling these sorts of abstract classes should be a goal of the v2.0 spec.

      Delete
  5. I would love to see properties become accessible as type-safe value-holder objects, something like Property<T> with String name() and T value() methods.

    ReplyDelete
  6. Also, it would be great if new "beans" can be defined as interfaces, at least for read-only access (only getters), and to move towards having semantic annotations on those interfaces. I can see great use of this in all the data shoveling contexts like persistence (jpa 3.x entities?), serialization to json/xml/etc, mapping data to ui, etc.

    An interface based approach, with property getters that do not directly return values, but typed value holders would be very powerful.

    ReplyDelete
    Replies
    1. Why do you need typed property boxes? What is your use case? I've tried that approach in the past and found it didn't work well on the server (fine in a client side GUI). That said, I think a truly flexible meta-model should be able to cope with properties that are wrapped in boxes, I just don't think it should be mandatory.

      Delete
  7. Hi, Stephen
    I really like the change Beans specification and it's time to simplify JavaBeans and to stop abusing Beans with expressions like this bean.getProp1().getFor().getBar() == "Smthing"
    But the current proposal (Beans 2.0) in my eyes is just a make-up and the expression like the one above will be simplified and Java as language will be more structural than OO.
    So, I'm proposing this solution to be the base of JavaBeans 2.0 or create new specification and abandon the current JavaBeans 1.0
    http://www.javaworld.com/article/2072302/core-java/more-on-getters-and-setters.html
    Problems with this solution as it's written in the article are:
    1. It's verbose
    2. Doesn't have annotations
    But, these problems can be solved using annotations and code generation at compile time (just like code is generated in Project Lombook).
    Ex.

    Developers view of the class
    @Importer(exclude="prop1")
    @Exporter
    class Bean1{
    int prop1;
    int prop2;
    }

    Compiled class
    class Bean1{
    int prop1;
    int prop2;
    //Code that should be generate by the compiler
    public interface Importer {
    int getProp2();
    }
    public interface Exporter {
    void setProp1(int p1);
    void setProp2(int p2);
    }

    public Bean1(Importer imp){
    this.prop2=imp.getProp2();
    }

    public void export(Exporter exp){
    exp.setProp1(p1);
    exp.setProp2(p2);
    }
    //////////////////////////////////////////
    }

    Client that uses bean classes can be like

    @ConsumeImporters(list of @Importers)
    @ConsumeExporters(list of @Exporters)
    @MappedProperties({ ("Client1.cProp2","Bean1.prop2"), ..... })
    class Client1{
    int prop1;
    String cProp2;

    void createBean1(){
    Bean b1=new Bean(this);
    .....
    }

    void readBean1(){
    Bean b1=//read from db
    b1.export(this);
    }
    }
    Compiled class
    class Client1 implements Bean1.Importer, Bean1.Exporter{
    int prop1;
    int cProp2;

    int getProp2(){
    return cProp2;
    }

    void setProp1(int prop1){
    this.prop1=prop1;
    }

    void setProp2(int prop2){
    this.cProp2=prop2;
    }
    ......

    }
    This is just an example and all use-cases aren't include. But in my opinion is't more developer friendly and good starting point for better JavaBeans 2.0 and more OO approach.

    ReplyDelete

Please be aware that by commenting you provide consent to associate your selected profile with your comment. Long comments or those with excessive links may be deleted by Blogger (not me!). All spam will be deleted.