Skip to main content

How to use the @ConstructorProperties annotation

Posted by malenkov on March 20, 2007 at 6:00 PM PDT

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.

Related Topics >>

Comments

@ConstructorProperties now supported by Lombok

Since the latest release of Project Lombok (http://projectlombok.org) version 0.9.3 (Burrowing Whale), lombok will even generate the @ConstructorProperties for you if you use the @Data annotation or one of the new @XxxConstructor annotations. The whole code would then boil down to:

import lombok.Data

@Data
public class Point {
private final int x, y;
}