Nested fields

When a packed class contains an instance field that is a packed type, the data for that field is packed directly into the containing class. The field is known as a nested field. When reading from a nested field, a small object is created as a pointer to the data. When writing to a nested field, you must use an explicit copy operation.

A nested packed array is a type of nested field. For more information, see Nested packed arrays.

Consider a class that represents a simple two dimensional box that you might use in a graphical user interface or drawing program. The class consists of two points: the start point and the dimensions of the box. The class also has two fields that represent the colors to use for the frame of the box, and the interior fill.

Here are some examples:

public final class Point extends PackedObject
        public int x;
        public int y;

public final class Box extends PackedObject 
        public Point origin;
        public Point extent;
        public RGBColour frameColour;
        public RGBColour fillColour;
Note: is described in The packed object data model.
If Box is a standard Java™ type, the class would consist of four object pointers, with all the data stored in those objects. The following diagram represents the memory layout:
A Box object consists of a header, followed by four pointers to the following objects: origin, extent, frame and fill. Each of these four objects also consists of a header and fields. The origin and extent objects have two fields: x and y. The frame and fill objects have three fields: red, green, and blue. Overall, the memory layout has 5 objects, containing 4 pointers and 10 fields. Each field occupies 4 bytes. The total space occupied by the data is therefore 40 bytes.
As a packed type, however, the representation is far more compact. The only object is the Box, which is represented in the following diagram. These four fields are all of packed types, so their data is directly embedded into the object:
In the packed version of the Box type, the fields of the referenced objects are embedded in the Box object. There is therefore only one object, Box, which consists of a header and the following 10 fields: originX, originY (embedded from the origin object), extentX, extentY (embedded from the extent object), R, G, B (embedded from the frame object), and R, G, B (embedded from the fill object). The first four fields occupy 4 bytes each. The last 6 fields, which are of type int, occupy 1 byte each. The total space occupied by the data is therefore 22 bytes.
Note: Fields of primitive data types, such as int, are not considered nested because they are not object types. Similarly, fields of non-packed object types, such as String are also not nested. Instead of their data being embedded in the packed object, such fields are referenced from within the packed object, in the same way that they would be from a standard Java object.

You do not need to allocate and initialize nested fields. Rather than being initially null, all the data is available and initialized to the default value. See Initialization and construction.

Reading nested fields

When accessing a nested field in a packed object, the JVM must return an object for further operations to interact with. Because the data for the field is contained within the object, a small object must be generated to describe the data. This object is known as a derived object. This object is not a copy of the data, but a pointer to the data.

The following diagram illustrates accessing the box.extent derived object:
The memory layout of the packed Box object is the same as in the previous diagram. There is a separate, derived, object, box.extent, which consists of a header which references the beginning of the extent fields within the Box object. Use this box.extent object to access the nested extent fields.
Derived objects tend to be short lived, and in many cases can be removed entirely by the just-in-time compiler (JIT). For example:
The derived object for the box.extent fields can be removed by compiler optimization because the appropriate offset into the object data can be calculated directly. In the example, this offset is offset 12 into the data.

Writing to nested fields

Because nested fields consist of data and not object pointers, you cannot assign an object into a nested field. The data can be copied in, but the reference to the object cannot be preserved. Likewise, further changes to the stored object are not reflected in the field, as these changes would be if storing a reference. Therefore, direct assignment into nested fields is not allowed. Instead, you must use an explicit copy operation with the PackedObject.copyFrom(PackedObject) method. This method copies the data from the argument into the receiver.

The following example is not valid when implementing a method like setFillColour(RGBColour):
 void setFillColour(RGBColour value) 
         fillColour = value;  // Invalid
Use the following example instead:
 void setFillColour(RGBColour value) 

For more information about assignment to nested fields, see Assignment Semantics.

An additional consequence of assignment is that final nested fields are not allowed. This rule exists because of a conflict between the compiler and the JVM:
  • The compiler expects the field to be initialized, that is, assigned to, before the constructor completes.
  • The JVM has implicitly initialized the field along with the containing object, and prevents assignment to the field.
The final modifier has no effect. Because nested fields cannot be assigned to, the fields are implicitly final. However, the contents of the nested field can be changed, which is standard Java behavior. Consider a final field of type Object[]. The reference to the array cannot be changed after it is assigned, but the contents of the array can be freely modified.