22.5 LiveConnect Data ConversionJava 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]
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 valuesFigure 22-3. Data conversions performed when JavaScript reads Java valuesNotice the following points about the data conversions illustrated in Figure 22-2:
Also notice these points about the conversions illustrated in Figure 22-3:
22.5.1 Wrapper ObjectsAnother 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 3In 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. |