Java Mapping for Parameters and Return Values
In Parameters
An in parameter is mapped to a Java parameter with the same name; its type is the mapped Java type.
For example, a Slice parameter string name is mapped to a Java parameter String name.
Out Parameters and Return Values
The return value of a mapped method depends on how many values the corresponding Slice operation returns, including out parameters and a non-void return value:
Zero values
The corresponding Java method returnsvoid. For the purposes of this discussion, we're not interested in these operations.One value
The corresponding Java method returns the mapped type, regardless of whether the Slice definition of the operation declared it as a return value or as an out parameter. Consider this example:SLICEinterface I { string op1(); void op2(out string name); }The mapping generates corresponding methods with identical signatures:
JAVAinterface IPrx extends ObjectPrx { String op1(); String op2(); }Two or more values
The Slice compiler generates an extra nested class to hold the results of an operation that returns multiple values. The class is nested in the mapped interface (not the proxy interface) and has the nameOpResult, whereOprepresents the name of the operation. The leading character of the class name for a "result class" is always capitalized. The values of out parameters are provided in corresponding public fields of the same names. If the operation declares a return value, its value is provided in the field namedreturnValue.The result class defines an empty constructor as well as a primary constructor that accepts and assigns a value for each of its fields. The corresponding Java method returns the result class type.
Consider this example:
interface Example
{
double op(int inp1, string inp2, out bool outp1, out long outp2);
}
The generated code looks like this:
// Server-side skeleton
public interface Example extends com.zeroc.Ice.Object {
public static class OpResult {
public double returnValue;
public boolean outp1;
public long outp2;
...
}
Example.OpResult op(int inp1, String inp2, com.zeroc.Ice.Current current);
...
}
// Client-side proxy
public interface ExamplePrx extends com.zeroc.Ice.ObjectPrx {
default Example.OpResult op(int inp1, String inp2) {
...
}
default Example.OpResult op(
int inp1, String inp2, java.util.Map<String, String> context) {
...
}
default CompletableFuture<Example.OpResult> opAsync(int inp1, String inp2) {
...
}
default CompletableFuture<Example.OpResult> opAsync(
int inp1, String inp2, java.util.Map<String, String> context) {
...
}
}
Null Parameters
Some Slice types naturally have "empty" or "not there" semantics. Specifically, sequences, dictionaries, and strings all can be null, but the corresponding Slice types do not have the concept of a null value. To make life with these types easier, whenever you pass null as a parameter or return value of type sequence, dictionary, or string, the Ice run time automatically sends an empty sequence, dictionary, or string to the receiver.
This behavior is useful as a convenience feature: especially for deeply-nested data types, fields that are sequences, dictionaries, or strings automatically arrive as an empty value at the receiving end. This saves you having to explicitly initialize, for example, every string element in a large sequence before sending the sequence in order to avoid NullPointerException. Note that using null parameters in this way does not create null semantics for Slice sequences, dictionaries, or strings. As far as the object model is concerned, these do not exist (only empty sequences, dictionaries, and strings do). For example, whether you send a string as null or as an empty string makes no difference to the receiver: either way, the receiver sees an empty string.
Optional Parameters
The mapping uses standard Java types to encapsulate optional parameters:
java.util.OptionalDouble
The mapped type for an optionaldouble.java.util.OptionalInt
The mapped type for an optionalint.java.util.OptionalLong
The mapped type for an optionallong.java.util.Optional<T>
The mapped type for all other Slice types.
Optional return values and output parameters are mapped to instances of the above classes, depending on their types. For operations with optional in parameters, the proxy provides a set of overloaded methods that accept them as optional values, and another set of methods that accept them as required values. Consider the following operation:
optional(1) int execute(optional(2) string parameters);
The mapping for this operation is shown below:
// With String in-parameter
java.util.OptionalInt execute(String parameters);
java.util.OptionalInt execute(
String parameters, java.util.Map<String, String> context);
// With Optional<String> in-parameter
java.util.OptionalInt execute(java.util.Optional<String> parameters);
java.util.OptionalInt execute(
java.util.Optional<String> parameters, java.util.Map<String, String> context);
// Async with String in-parameter
CompletableFuture<java.util.OptionalInt> executeAsync(String parameters);
CompletableFuture<java.util.OptionalInt> executeAsync(
String parameters, java.util.Map<String, String> context);
// Async with Optional<String> in-parameter
CompletableFuture<java.util.OptionalInt> executeAsync(
java.util.Optional<String> parameters);
CompletableFuture<java.util.OptionalInt> executeAsync(
java.util.Optional<String> parameters, java.util.Map<String, String> context);
For cases where you are passing values for all of the optional in parameters, it is more efficient to use the required mapping and avoid creating temporary optional values.
A client can invoke execute as shown below:
java.util.OptionalInt i;
i = proxy.execute("--file log.txt"); // required mapping
i = proxy.execute(java.util.Optional.of("--file log.txt")); // optional mapping
i = proxy.execute(java.util.Optional.empty()); // params is unset
if (i.isPresent()) {
System.out.println("value = " + i.get());
}
Passing null where an optional value is expected is equivalent to passing an instance whose value is unset.
Java's optional classes do not consider null to be a legal value. Consider this example:
Slice
interface Widget
{
...
}
interface Repository
{
void addOptional(optional(1) Widget* widget);
void addRequired(Widget* widget);
}
The Ice encoding supports null proxies, so you can pass null to addRequired and the server will receive it as null. However, there's no way to pass an "optional proxy set to null" in the Java mapping. Passing null to addOptional is equivalent to passing the value of java.util.Optional.ofNullable((T)null), which is equivalent to passing the value of java.util.Optional.empty(). In either case, the server will receive it as an optional whose value is not present.