22.5 LiveConnect Data Conversion
Java is a strongly typed language
with
a
relatively large number of data
types, while JavaScript is an untyped language with a relatively
small number of types. Because of this major structural difference
between the two languages, one of the central responsibilities of
LiveConnect is data conversion. When JavaScript sets a Java class or
instance field or passes an argument to a Java method, a JavaScript
value must be converted to an equivalent Java value, and when
JavaScript reads a Java class or instance field or obtains the return
value of a Java method, that Java value must be converted into a
compatible JavaScript value.[5]
[5] In addition, data
conversion must happen when Java reads or writes a JavaScript field
or invokes a JavaScript method. These conversions are done
differently, however, and are described later in this chapter, when
we discuss how to use JavaScript from Java. For now, we're
considering only the data conversion that happens when JavaScript
code interacts with Java, not the other way around. Figure 22-2 and Figure 22-3 illustrate how data conversion is performed
when JavaScript writes Java values and when it reads them,
respectively.
Figure 22-2. Data conversions performed when JavaScript writes Java values

Figure 22-3. Data conversions performed when JavaScript reads Java values

Notice the following points about the data conversions illustrated in
Figure 22-2:
- Figure 22-2 does not show all possible conversions
between JavaScript types and Java types. This is because
JavaScript-to-JavaScript type conversions can occur before the
JavaScript-to-Java conversion takes place. For example, if you pass a
JavaScript number to a Java method that expects a
java.lang.String argument, JavaScript first
converts that number to a JavaScript string, which can then be
converted to a Java string. - A JavaScript number can be
converted to any of the primitive Java numeric
types. The actual conversion performed depends, of course, on the
type of the Java field being set or the method argument being passed.
Note that you can lose precision doing this, for example, when you
pass a large number to a Java field of type short
or when you pass a floating-point value to a Java integral type. - A JavaScript number can also be converted to an instance of the Java
class java.lang.Double but not to an instance of
a related class, such as java.lang.Integer or
java.lang.Float . - JavaScript does not have
any representation for character data, so a JavaScript number may
also be converted to the Java primitive char type. - A JavaObject in
JavaScript is "unwrapped" when passed to Java -- that
is, it is converted to the Java object it represents. Note, however,
that JavaClass objects in
JavaScript are not converted to instances of
java.lang.Class , as might be expected. - JavaScript arrays are not converted to Java arrays.
Also notice these points about the conversions illustrated in Figure 22-3:
- Since JavaScript does not have a
type for character data, the Java primitive char type
is converted to a JavaScript number, not a string, as might be
expected. - A Java instance of
java.lang.Double ,
java.lang.Integer , or a similar class is not
converted to a JavaScript number. Like any Java object, it is
converted to a JavaObject object in JavaScript. - A Java string is an
instance of java.lang.String , so like any other
Java object, it is converted to a JavaObject object rather than to an
actual JavaScript string. - Any type of Java array is converted to
a JavaArray
object in JavaScript.
22.5.1 Wrapper Objects
Another important
concept that
you must
grasp in order to fully understand Figure 22-2 and Figure 22-3 is the idea
of wrapper objects. While conversions between most JavaScript and
Java primitive types are possible, conversions between object types
are generally not possible. This is why LiveConnect defines the
JavaObject object in JavaScript -- it represents a Java object
that cannot be directly converted to a JavaScript object. In a sense,
a JavaObject is a JavaScript wrapper around a Java object. When
JavaScript reads a Java value (a field or the return value of a
method), any Java objects are wrapped and JavaScript sees a
JavaObject.
A similar thing happens when JavaScript writes a JavaScript object
into a Java field or passes a JavaScript object to a Java method.
There is no way to convert the JavaScript object to a Java object, so
the object gets wrapped. The Java wrapper for a JavaScript object is
the Java class netscape.javascript.JSObject .
Things get interesting when these wrapper objects are passed back. If
JavaScript writes a JavaObject into a Java field or passes it to a
Java method, LiveConnect first unwraps the object, converting the
JavaObject back into the Java object that it represents. Similarly,
if JavaScript reads a Java field or gets the return value of a Java
method that is an instance of
netscape.javascript.JSObject , that JSObject is
also unwrapped to reveal and return the original JavaScript object.
22.5.2 LiveConnect Data Conversion in Netscape 3
In Netscape 3, there was a bug in
the way that LiveConnect converted Java values to JavaScript values:
the value of a primitive field of a Java object was incorrectly
returned as a JavaScript object, rather than as a JavaScript
primitive value. For example, if JavaScript read the value of a field
of type int, LiveConnect in Netscape 3 converted
that value to a Number object, rather than to a primitive numeric
value. Similarly, LiveConnect converted the value of Java
boolean fields to JavaScript Boolean objects,
rather than primitive JavaScript boolean values. Note that this bug
occurred only when querying the values of Java fields. It did not
occur when LiveConnect converted the return value of a Java method.
Number and Boolean objects in JavaScript behave almost, but not
exactly, the same as primitive number and boolean values. One
important difference is that Number objects, like all JavaScript
objects, use the + operator for string
concatenation rather than for addition. As a result, code like the
following that uses LiveConnect in Netscape 3 can yield unexpected
results:
var r = new java.awt.Rectangle(0,0,5,5);
var w = r.width; // This is a Number object, not a primitive number.
var new_w = w + 1; // Oops! new_w is now "51", not 6, as expected.
To work
around this problem, you can explicitly call the valueOf(
) method to convert a Number object to its corresponding
numeric value. For example:
var r = new java.awt.Rectangle(0,0,5,5);
var w = r.width.valueOf( ); // Now we've got a primitive number.
var new_w = w + 1; // This time, new_w is 6, as desired.