The Source for Java Technology Collaboration
User: Password:



Sergey Malenkov

Sergey Malenkov's Blog

How to use the @ConstructorProperties annotation

Posted by malenkov on March 20, 2007 at 06:00 PM | Comments (5)

The @ConstructorProperties annotation for constructors was introduced in JDK version 6. This annotation shows how the parameters of annotated constructor correspond to object's properties. The following code snippet shows the get methods of the x and y properties:
public class Point {
    private final int x;
    private final int y;

    @ConstructorProperties({"x", "y"})
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return this.x;
    }

    public int getY() {
        return this.y;
    }
}
The first parameter of the constructor corresponds to the x property and the second - to the y property. The simplicity of the conception enables a developer to adopt the @ConstructorProperties annotation easily. In particular this technique is applied to the Long-Term Persistence for JavaBeans (LTP) and Java Management Extensions (JMX). Technically, an annotation is used for copying beans with read-only properties. Let's examine the encoding the beans into XML form.
ByteArrayOutputStream stream = new ByteArrayOutputStream();

XMLEncoder encoder = new XMLEncoder(stream);
encoder.writeObject(new Point(10, 20));
encoder.close();

System.out.println(stream);
In JDK version 5 and earlier, the object Point(10, 20) would not be encoded, because DefaultPersistenceDelegate is applied by default. This delegate creates an object copy by using a constructor without parameters. Then values of properties are set by means of the setter methods. In the given example the Point class is immutable: its properties are read only, and it does not have a constructor without parameters. So you need to register a separate persistence delegate each time you are coding such a class. For example:
ByteArrayOutputStream stream = new ByteArrayOutputStream();

XMLEncoder encoder = new XMLEncoder(stream);
encoder.setPersistenceDelegate(Point.class, new DefaultPersistenceDelegate("x", "y"));
encoder.writeObject(new Point(10, 20));
encoder.close();

System.out.println(stream);
However this approach does not work for library classes. There is a convention to create a BeanInfo class in the library and add the persistenceDelegate attribute to BeanDescriptor. The following code snippet illustrates how to apply the block of static initialization of a library class:
import java.beans.DefaultPersistenceDelegate;
import java.beans.Introspector;

public class Point {
    private final int x;
    private final int y;

    static {
        DefaultPersistenceDelegate dpd = new DefaultPersistenceDelegate("x", "y");
        Introspector.getBeanInfo().getBeanDescriptor().setValue("persistenceDelegate", dpd);
    }

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return this.x;
    }

    public int getY() {
        return this.y;
    }
}
As you see this approach is inconvenient and resource-intensive, because it requires the BeanInfo class to be created. In fact this class may never be needed. Use the @ConstructorProperties annotation to resolve this issue. This annotation enables you to resolve an issue of coding the immutable objects by facilitating the custom persistence delegates registration.

Since the annotation describes the relationship between a constructor parameter and a property, but not an operational logic, it could be applied to other tasks, for example, in JMX technology.


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first)

  • ConstructorProperties clarify the mapping indeed. -- Mikhail Knyazev

    Posted by: knyazevm on March 21, 2007 at 01:55 PM

  • We are using similar one, @FeatureContructor (http://fisheye5.cenqua.com/browse/jax-ws-sources/jaxws-ri/rt/src/com/sun/xml/ws/api/FeatureConstructor.java ) in JAX-WS.
    Good to know that it is part of JDK now.

    Posted by: ramapulavarthi on March 22, 2007 at 12:39 AM

  • I didn't know about this.
    Thanks, I can use this.

    Posted by: petermuys on March 22, 2007 at 04:38 AM

  • I am not sure I like this; this is annotation going overboard. I'd rather see named arguments that can be discovered; and then use a map to pass in values like

    public Foo( int x, String y){ ... };
    Foo f = new Foo( x=1, y="bar" );
    Foo g = new Foo( y="...", x=2 ); // order is irrelevant when named


    Many other languages provides this feature; Java is really lacking here.

    Posted by: beanary on April 25, 2007 at 12:42 PM

  • I don't like the idea to use named assignment, because your code can contain local variables with the same name.
    The best way - ability to retrieve names of method parameters via reflection. See the 5082475 RFE.

    Posted by: malenkov on April 26, 2007 at 01:56 AM





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds