Java Mapping for Classes

On this page:

Basic Java Mapping for Classes

A Slice class is mapped to a Java class with the same name. The generated class contains a public field for each Slice data member (just as for structures and exceptions). Consider the following class definition:

Slice
class TimeOfDay
{
    short hour;         // 0 - 23
    short minute;       // 0 - 59
    short second;       // 0 - 59
    string tz;          // e.g. GMT, PST, EDT...
}

The Slice compiler generates the following code for this definition:

Java
public class TimeOfDay extends com.zeroc.Ice.Value
{
    public TimeOfDay();
    public TimeOfDay(short hour, short minute, short second, String tz);
 
    public short hour;
    public short minute;
    public short second;
    public String tz;
 
    public TimeOfDay clone();

    public static final String ice_staticId = "::...::TimeOfDay";

    public static String ice_staticId();

    @Override
    public String ice_id();

    ...
}

There are a several things to note about the generated code:

  1. The generated class TimeOfDay inherits from com.zeroc.Ice.Value. This means that all classes implicitly inherit from Value, which is the ultimate ancestor of all classes.
  2. The generated class contains a public field for each Slice data member.
  3. The generated class has a constructor that takes one argument for each field, as well as a default constructor.

There is quite a bit to discuss here, so we will look at each item in turn.

Inheritance from Ice::Value in Java

Classes implicitly inherit from a common base class, Value, which is mapped to com.zeroc.Ice.ValueValue is a very simple base class with just a few methods:

Java
public abstract class Value implements java.lang.Cloneable, java.io.Serializable
{
    public Value clone();
    public void ice_preMarshal();
    public void ice_postUnmarshal();
    public String ice_id();
    public SlicedData ice_getSlicedData();
    ...
}

The Value methods behave as follows:

  • ice_preMarshal
    The Ice run time invokes this method prior to marshaling the value's state, providing the opportunity for a subclass to validate its declared data members.
  • ice_postUnmarshal
    The Ice run time invokes this method after unmarshaling a value's state. A subclass typically overrides this method when it needs to perform additional initialization using the values of its declared data members.
  • clone
    This method returns a shallow member-wise copy of the value.
  • ice_id
    This method returns the actual run-time type ID for a class instance. If you call ice_id through a reference to a base instance, the returned type id is the actual (possibly more derived) type ID of the instance.
  • ice_getSlicedData
    This functions returns the SlicedData object if the value has been sliced during un-marshaling or null otherwise.

Class Data Members in Java

By default, data members of classes are mapped exactly as for structures and exceptions: for each data member in the Slice definition, the generated class contains a corresponding public field. A JavaBean-style API is used for optional data members, and you can customize the mapping to force required members to use this same API.

If you wish to restrict access to a field you can modify its visibility using the protected metadata directive. The presence of this directive causes the Slice compiler to generate the field with protected visibility. As a result, the field can be accessed only by the class itself or by one of its subclasses. For example, the TimeOfDay class shown below has the protected metadata directive applied to each of its data members:

Slice
class TimeOfDay
{
    ["protected"] short hour;   // 0 - 23
    ["protected"] short minute; // 0 - 59
    ["protected"] short second; // 0 - 59
    ["protected"] string tz;    // e.g. GMT, PST, EDT...
}

The Slice compiler produces the following generated code for this definition:

Java
public class TimeOfDay extends ...
{
    protected short hour;
    protected short minute;
    protected short second;
    protected String tz;

    public TimeOfDay();
    public TimeOfDay(short hour, short minute, short second);
    // ...
}

For a class in which all of the data members are protected, the metadata directive can be applied to the class itself rather than to each member individually. For example, we can rewrite the TimeOfDay class as follows:

Slice
["protected"] class TimeOfDay
{
    short hour;         // 0 - 23
    short minute;       // 0 - 59
    short second;       // 0 - 59
    string tz;          // e.g. GMT, PST, EDT...
}

You can optionally customize the mapping for data members to use getters and setters instead.

Class Constructors in Java

Classes have a default constructor that initializes fields as follows:

Data Member TypeDefault Value
stringEmpty string
enumFirst enumerator in enumeration
structDefault-constructed value
NumericZero
boolFalse
sequenceNull
dictionaryNull
class/interfaceNull

The constructor won't explicitly initialize a field if the default Java behavior for that type produces the desired results.

If you wish to ensure that fields of primitive and enumerated types are initialized to specific values, you can declare default values in your Slice definition. The default constructor initializes each of these fields to its declared value instead.

The generated class also contains a second constructor that accepts one argument for each field of the class. This allows you to create and initialize a class in a single statement, for example:

Java
TimeOfDay tod = new TimeOfDay(14, 45, 00, "PST"); // 14:45pm PST

For derived classes, the constructor requires an argument for every member of the class, including inherited members. For example, consider the the definition from Class Inheritance once more:

Slice
class TimeOfDay
{
    short hour;         // 0 - 23
    short minute;       // 0 - 59
    short second;       // 0 - 59
}

class DateTime extends TimeOfDay
{
    short day;          // 1 - 31
    short month;        // 1 - 12
    short year;         // 1753 onwards
}

The constructors for the generated classes are as follows:

Java
public class TimeOfDay extends ...
{
    public TimeOfDay() {}

    public TimeOfDay(short hour, short minute, short second)
    {
        this.hour = hour;
        this.minute = minute;
        this.second = second;
    }

    // ...
}

public class DateTime extends TimeOfDay
{
    public DateTime() {}

    public DateTime(short hour, short minute, short second, short day, short month, short year)
    {
        super(hour, minute, second);
        this.day = day;
        this.month = month;
        this.year = year;
    }

    // ...
}

If you want to instantiate and initialize a DateTime instance, you must either use the default constructor or provide values for all the fields of the instance, including fields of any base classes.

Value Factories in Java

Value factories allow you to create classes derived from the Java classes generated by the Slice compiler, and tell the Ice run time to create instances of these classes when unmarshaling. For example, with the following simple interface:

Slice
interface Time
{
    TimeOfDay get();
}

The default behavior of the Ice run time will create and return an instance of the generated TimeOfDay class.

If you wish, you can create your own custom derived class, and tell Ice to create and return these instances instead. For example:

Java
public class CustomTimeOfDay extends TimeOfDay
{
    public String format() { ... prints formatted data members ... }
}

You then create and register a value factory for your custom class with your Ice communicator:

Java
Communicator communicator = ...;
communicator.getValueFactoryManager().add(type -> {
        assert(type.equals(TimeOfDay.ice_staticId()));
        return new CustomTimeOfDay();
    },                
    TimeOfDay.ice_staticId());

Class Operations in Java

Operations on classes are deprecated as of Ice 3.7. Skip this section unless you need to communicate with old applications that rely on this feature.

Operations in classes are not mapped at all into the corresponding Java class. The generated Java class is the same whether the Slice class has operations or not. 

For a class that defines or inherits operations, the Slice-to-Java compiler generates instead a separate _<class-name>Disp class that can be used to implement an Ice object with these operations. Let's change our example to add a class operation:

Slice
class FormattedTimeOfDay
{
    short hour;         // 0 - 23
    short minute;       // 0 - 59
    short second;       // 0 - 59
    string tz;          // e.g. GMT, PST, EDT...
    string format();
}

The Slice compiler generates the following code:

Java
public class FormattedTimeOfDay extends com.zeroc.Ice.Value
{
    // ... operation format() not mapped at all here
}
 
// Disp class for servant implementation
public interface _FormattedTimeOfDayDisp extends com.zeroc.Ice.Object
{
    String format(com.zeroc.Ice.Current current);

    ...
}

This Disp class is the skeleton class for this Slice class. Skeleton classes are described in the Server-Side Java Mapping for Interfaces.

See Also