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.[77]
[77]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.
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.
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.
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.
Copyright © 2003 O'Reilly & Associates. All rights reserved.