Question

I hate the JavaBeans pattern with a passion that burns like the fire of a thousand suns. Why?

  • Verbose. It's 2009. I shouldn't have to write 7 LOC for a property. If they have event listeners then hold on to your hat.
  • No type-safe references. There is no type-safe way to reference a property. The whole point of Java is that it is type safe, and its most popular pattern is not at all typesafe.

What I would like is something like:

class Customer {
    public Property<String> name = new Property();
}

I am a web developer mostly, so it needs JPA and Wicket support.

Help me off the javabean train!

Was it helpful?

Solution

I think you're pretty close with the declaration you have there (see below for a sketch). However, by using a non-beans approach, you'll probably lose support provided by most tools that assume the JavaBeans protocol is in effect. Please be kind. The code below is off the top of my head...

public class Property<T> {
    public final String name;
    T value;
    private final PropertyChangeSupport support;

    public static <T> Property<T> newInstance(String name, T value, 
                                              PropertyChangeSupport support) {
        return new Property<T>(name, value, support);
    }

    public static <T> Property<T> newInstance(String name, T value) {
        return newInstance(name, value, null);
    }

    public Property(String name, T value, PropertyChangeSupport support) {
        this.name = name;
        this.value = value;
        this.support = support;
    }

    public T getValue() { return value; }

    public void setValue(T value) {
        T old = this.value;
        this.value = value;
        if(support != null)
            support.firePropertyChange(name, old, this.value);
    }

    public String toString() { return value.toString(); }
}

and then go ahead and use it:

public class Customer {
    private final PropertyChangeSupport support = new PropertyChangeSupport();

    public final Property<String> name = Property.newInstance("name", "", support);
    public final Property<Integer> age = Property.newInstance("age", 0, support);

    ... declare add/remove listenener ...
}


Customer c = new Customer();
c.name.setValue("Hyrum");
c.age.setValue(49);
System.out.println("%s : %s", c.name, c.age);

So, now declaring a property is a single line of code and property change support is included. I called the methods setValue() and getValue() so it would still look like a bean to code like Rhino and stuff, but for succinctness, you could add just get() and set(). The rest is left as an exercise for the reader:

  • Properly handle serialization
  • Handle null value checking
  • Maybe add a specializations for atomic types if you care about autoboxing overhead.
  • ?? I'm sure there are more gotchas

Also note that you can subclass (usually as an anonymous class) and override setValue() to provide additional parameter checking.

I don't think you can really get away from "String references" since that's pretty much what reflection's all about.

Sadly though, in this day and age, this is still kind of like programming in assembly... Groovy, C#, etc, etc may still be a better choice, if you have a choice.

OTHER TIPS

Check out my Bean annotations at

http://code.google.com/p/javadude/wiki/Annotations

Basically you do things like:

@Bean(
  properties={
    @Property(name="name"),
    @Property(name="phone", bound=true),
    @Property(name="friend", type=Person.class, kind=PropertyKind.LIST)
  }
)
public class Person extends PersonGen {}

rather than defining all those extra get/set etc methods yourself.

There are other attributes to define equals/hashCode, observers, delegates, mixins, etc.

It's a set of annotations and an annotation processor that runs in eclipse or in a command-line build (in ant for example). The processor generates a superclass to contain all the generated code (annotation processors cannot change the class containing the annotations, btw)

You may want to check out Groovy - a dynamically typed, JVM-based (and fully Java-compatible) language with "real" properties.

Use the Spring Framework. Its purpose is to simplify Java development by abstracting a lot of the groundwork that you are complaining about.

You can use it with Hibernate which will ease interation with data sources for you.

Useful sites:

www.springsource.org/download

www.hibernate.org/

For the web, I would suggest JSON (JavaScript Object Notation),

a lightweight data-interchange format

. Here is a reference to a JSON?Bean translator.

When I used C# the first times, I liked the properties, but now, after some time using them with VS 2008 I have to say that I prefer the set-/get-methods.

The main point is my personal way of working. When I have a new class and I would like to know what I can do with it, I just type classname.set and Eclipse shows me what "Properties" I can change. Same goes for get. Maybe its just the poor VS way but there the I have to scoll through a long list of this itelisense (where everything is mixed, instead of showing properties first) just to find out after I compiled that the property I wanted to set is readonly...doh!

Yes, in Java you need a lot of lines, but I just write my attributes and tell the IDE "please create getters and setters for me". But those methods tend to use a lot of space, thats why I would like to have regions in Java as well that I can fold in the IDE.

You could also build a code generator which creates your .java classes from a DSL that you write. You could have some kind of markup that describes the name of your class, the properties you want, and their types. Then, process that file with a program that generates your javabeans. Or you could use annotation, and post-process the class files using something like ASM to inject the accessors & mutators. I believe also that Spring provides some of these kind of features, but I've not used them.

Try SEAM framework from JBoss, you should like it.

Once I tried this:

interface IListenable {
    void addPropertyChangeListener( PropertyChangeListener listener );
    void removePropertyChangeListener( PropertyChangeListener listener );
}

abstract class MyBean extends IListenable {
    public abstract void setName(String name);
    public abstract String getName();

    // more things
}

public class JavaBeanFactory {

   public <T> Class<T> generate(Class<T> clazz) {
      // I used here CGLIB to generate dynamically a class that implements the methods:
      // getters
      // setters
      // addPropertyChangeListener
      // removePropertyChangeListener
   }
}

I used that as this (it is just an example):

public class Foo {
    @Inject
    public Provider<MyBean> myBeanProvider;

    public MyBean createHook(MyBean a) {
        final MyBean b  = myBeanProvider.get();
        a.addPropertyChangeListener(new PropertyChangeListener() {
             public void propertyChange(PropertyChangeEvent evt) {
                 b.setName((String) evt.getNewValue());
             }
        });
        return b;
    }
}

I use JavaBean properties a lot on my model entities (annotated with JPA) to be able to databind them to a UI (with JFace).

I unfortunately don't have a solution for the second problem (perhaps except for defining constants containing the property names).

What I do to generate the listener boilerplate is by having my model entities extend from an AbstractJavaBean superclass that handles it using reflection. Then I can use the default way of creating getters/setters except that the setters need to be rewritten like:

public void setRemarks(String remarks) {
    set("remarks", remarks);
}

AbstractJavaBean.set then uses reflection (through Apache commons beanutils) to read the old value of the property "remarks" through its getter, sets the new value to a field called "remarks" and fires a property change event using the old and new values. In fact this idea can be extended to enable dependent "derived" properties to automatically fire property changes when one of the properties it is based on changes such as "age" being changed when the property "birthDate" is changed. All of this logic can be coded in one place inside AbstractJavaBean and used by any model object.

I was looking for the same thing and I was really surprised I wasn't able to find a fitting library. Since I felt the pain regularly I started a small project addressing these issues 2 years ago: Propertly

It's available at: https://github.com/aditosoftware/propertly. We are using it in our product now.

It gives static typing with generics, listeners on different levels, navigation through the tree, dynamic models and more. The main advantages are that descriptions for the models are done statically so the information is always available and it is designed with extensibility in mind. So you can i.e. use your own annotations for verification or define where data is read from and stored to.

Example of usage:

A simple IPropertyPitProvider. Properties are first name, last name and age.

// Generics describe parent, self and children
public class StudentPropertyPitProvider 
    extends AbstractPPP<IPropertyPitProvider, StudentPropertyPitProvider, Object>
{
  // IPropertyDescription gives static access to an IProperty's meta data like name and type.
  public static final IPropertyDescription<StudentPropertyPitProvider, String> FIRST_NAME =
      PD.create(StudentPropertyPitProvider.class);

  public static final IPropertyDescription<StudentPropertyPitProvider, String> LAST_NAME =
      PD.create(StudentPropertyPitProvider.class);

  public static final IPropertyDescription<StudentPropertyPitProvider, Integer> AGE =
      PD.create(StudentPropertyPitProvider.class);


  // Getters and setters can of course still be used for easier access. 
  public String getFirstName()
  {
    // getValue and setValue is available at AbstractPPP. That class is used for easier access. 
    // Propertly can be used without inheriting from that class, too.
    return getValue(FIRST_NAME);
  }

  public void setFirstName(String pFirstName)
  {
    setValue(FIRST_NAME, pFirstName);
  }

  public String getLastName()
  {
    return getValue(LAST_NAME);
  }

  public void setLastName(String pLastName)
  {
    setValue(LAST_NAME, pLastName);
  }

  public Integer getAge()
  {
    return getValue(AGE);
  }

  public void setAge(Integer pAge)
  {
    setValue(AGE, pAge);
  }

}

Using the defined provider:

public class Sample
{

  public static void main(String[] args)
  {
    // Hierarchy is necessary to initialize the IPropertyPitProviders and for advanced features.
    Hierarchy<StudentPropertyPitProvider> hierarchy =
        new Hierarchy<>("student1", new StudentPropertyPitProvider());
    // The created student can be accessed from the hierarchy.
    StudentPropertyPitProvider student = hierarchy.getValue();
    // Listeners can be added.
    student.addPropertyEventListener(new PropertyPitEventAdapter()
    {
      @Override
      public void propertyChanged(IProperty pProperty, Object pOldValue, Object pNewValue)
      {
        System.out.println(pProperty.getName() + "=" + pNewValue);
      }
    });

    // The following calls will cause
    //  FIRST_NAME=Nils
    //  LAST_NAME=Holgersson
    //  AGE=32
    // to be printed on console through the listener.
    student.setFirstName("Nils");
    student.setLastName("Holgersson");
    student.setAge(32);
  }

}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top