Назад Оглавление Индекс Вперёд |
Ядро JavaScript 1.5. Руководство по Использованию. |
В этой главе рассматривается использование технологии LiveConnect, обеспечивающей совместную работу кодов, написанных на Java и JavaScript. Предполагается, что Вы уже знакомы с программированием на Java.
В главе имеются следующие разделы:
Дополнительную информацию об использовании LiveConnect см.
в технических
замечаниях по JavaScript на сайте DevEdge.
В JavaScript wrapper\оболочка это объект целевого типа данных языка, содержащий объект исходного языка. При программировании на JavaScript Вы можете использовать объект-оболочку для доступа к методам и полям Java-объекта; вызов методов или доступ к свойству в wrapper вызывает Java-объект. На стороне Java объекты JavaScript "обёртываются" в экземпляры класса netscape.javascript.JSObject и передаются в Java.
Если JavaScript-объект посылается в Java, машина времени
выполнения создаёт Java-оболочку типа JSObject; когда JSObject посылается из Java
в JavaScript, машина времени выполнения разворачивает его в оригинальный тип
объекта JavaScript. Класс JSObject предоставляет интерфейс для вызова методов JavaScript
и проверки JavaScript-свойств.
Если Вы ссылаетесь на пакет или класс Java или работаете с объектом или массивом Java, Вы используете один из специальных объектов LiveConnect. Весь доступ JavaScript к Java осуществляется через эти объекты, резюме по которым - в следующей таблице.
Объект | Описание |
---|---|
Обёрнутый массив Java, к которому имеется доступ из кода JavaScript. |
|
Обёрнутый объект Java, к которому имеется доступ из кода JavaScript. |
|
Поскольку Java является строго типизированным языком, а JavaScript типизирован слабо, машина выполнения JavaScript конвертирует значения аргументов в типы данных, подходящие для других языков, если Вы используете LiveConnect. См. Конвертация Типов Данных.
Иногда существование объектов LiveConnect достаточно
прозрачно, поскольку Вы взаимодействуете с Java в довольно интуитивной манере.
Например, Вы можете создать объект Java String и присвоить его в JavaScript
переменной myString путём использования оператора new с конструктором Java таким
вот образом:
var myString = new java.lang.String("Hello world")
Здесь переменная myString является JavaObject, поскольку
она содержит экземпляр Java-объекта String. Как JavaObject, myString имеет
доступ к public-методам экземпляра java.lang.String и его суперкласса, java.lang.Object.
Эти Java-методы доступны в JavaScript как методы из JavaObject, и Вы можете
вызывать их так:
myString.length() // возвращает 11
Если Java-класс не является частью пакетов java, sun или netscape, Вы можете получить к нему доступ через объект Packages. Например, предположим, корпорация Redwood использует Java-пакет под названием redwood как контейнер различных Java-классов. Чтобы создать экземпляр класса HelloWorld из redwood, Вы осуществляете доступ к конструктору класса так:
var red = new Packages.redwood.HelloWorld()
Вы можете также иметь доступ к классам в пакете по
умолчанию (то есть к классам, не указывающим пакет явно). Например, если класс HelloWorld
находится непосредственно в пути CLASSPATH, а не в пакете, Вы можете получить к
нему доступ так:
var red = new Packages.HelloWorld()
Объекты LiveConnect java, sun и netscape являются
аббревиатурами для наиболее часто употребляющихся Java-пакетов. Например:
var myString = new java.lang.String("Hello world")
var myString = new Packages.java.lang.String("Hello world")
Если любой Java-метод создаёт массив и Вы ссылаетесь на этот массив в JavaScript, Вы работаете с JavaArray. Например, следующий код создаёт JavaArray x из 10 элементов типа int:
x = java.lang.reflect.Array.newInstance(java.lang.Integer, 10)
Как и объект Array из JavaScript, JavaArray имеет свойство length,
которое возвращает количество элементов массива. В отличие от Array.length, JavaArray.length,
это свойство только для чтения, поскольку количество элементов Java-массива
фиксируется в момент создания.
Простые ссылки из JavaScript на пакеты и классы Java создают объекты JavaPackage и JavaClass. В предыдущем примере о компании Redwood, к примеру, ссылка Packages.redwood это JavaPackage-объект. Аналогично ссылка java.lang.String является JavaClass-объектом.
В большинстве случаев Вам не нужно беспокоиться об
объектах
JavaPackage и JavaClass - Вы просто работаете с пакетами и классами Java,
а LiveConnect создаёт эти объекты прозрачно.
В JavaScript 1.3 и ранее JavaClass-объекты не
конвертируются автоматически в экземпляры java.lang.Class. Если Вы передаёте их
в качестве параметров Java-методам - Вы обязаны создавать wrapper/оболочку
вокруг экземпляра
java.lang.Class. В следующем примере метод forName создаёт объект-оболочку theClass,
который затем передаётся методу newInstance для создания массива.
// JavaScript 1.3
theClass = java.lang.Class.forName("java.lang.String")
theArray = java.lang.reflect.Array.newInstance(theClass, 5)
В JavaScript 1.4 и позднее Вы можете передавать JavaClass-объект
непосредственно в метод, как в следующем примере:
// JavaScript 1.4
theArray = java.lang.reflect.Array.newInstance(java.lang.String, 5)
В JavaScript 1.4 и позднее Вы можете передавать строку из одного символа в Java-метод, который требует аргумент типа char. Например, Вы можете передать строку "H" в конструктор Character:
c = new java.lang.Character("H")
В JavaScript 1.3 и ранее Вы обязаны передавать в такие
методы целое число, соответствующее Unicode-значению символа. Например,
следующий код также присваивает значение "H" переменной c:
c = new java.lang.Character(72)
Если код Java прерывается на этапе прогона программы, он вызывает исключение. Если Ваш JavaScript-код имеет доступ к члену данных Java или методу и терпит неудачу, исключение Java передаётся в JavaScript для обработки. Начиная с JavaScript 1.4, Вы можете отловить это исключение в операторе try...catch.
Например, предположим, Вы используете Java-метод forName
для присвоения имени Java-класа переменной theClass. Метод forName вызывает
исключение, если передаваемое Вами значение не вычисляется в имя Java-класса.
Поместите оператор присвоения forName в блок try для обработки исключения:
function getClass(javaClassName) {
try {
var theClass = java.lang.Class.forName(javaClassName);
} catch (e) {
return ("The Java exception is " + e);
}
return theClass
}
В этом примере, если javaClassName вычисляется в верное
имя класса, такое как "java.lang.String", присвоение проходит успешно. Если javaClassName
вычисляется в неверное имя класса, такое как "String", функция getClass
отлавливает исключение и возвращает нечто подобное:
The Java exception is java.lang.ClassNotFoundException: String
См. в разделе "Операторы
Обработки Исключений" информацию об исключениях JavaScript.
Если Вы хотите использовать объекты JavaScript в Java, Вы
обязаны импортировать пакет netscape.javascript в Ваш Java-файл. Этот пакет
определяет следующие классы:
Начиная с JavaScript 1.2, эти классы поставляются в .jar
файле; в предыдущих версиях JavaScript они поставлялись в .zip файле. См. в
книге
Ядро JavaScript. Справочник. информацию об этих классах.
Для доступа к классам LiveConnect поместите .jar или .zip
файл в CLASSPATH компилятора JDK одним из следующих способов:
Например, в Navigator 4. 0 для Windows NT классы
расположены в файле java40.jar в директории Program\Java\Classes ниже директории Navigator'а.
Вы можете специфицировать переменную окружения в Windows NT, дважды щёлкнув
иконку System в Control Panel и создав пользовательскую переменную окружения под
названием CLASSPATH со значением, типа такого:
D:\Navigator\Program\Java\Classes\java40.jar
См. в документации Sun JDK информацию о CLASSPATH.
Поскольку Java является строго типизированным языком, а JavaScript -слабо типизированным, машина выполнения JavaScript конвертирует значения аргументов в типы данных, подходящие для других языков, если Вы используете LiveConnect. См. полную информацию в разделе Конвертация Типов Данных.
Все объекты JavaScript появляются в коде Java как экземпляры netscape.javascript.JSObject. Если Вы вызываете метод в Java-коде, Вы можете передать в него JavaScript-объект как один из аргументов. Чтобы выполнить это, Вы обязаны определить соответствующий формальный параметр этого метода как имеющий тип JSObject.
Итак, при использовании JavaScript-объектов в Java-коде Вы
всегда должны помещать вызов JavaScript-объекта внутри оператора try...catch,
который обрабатывает исключения типа netscape.javascript.JSException. Это даёт
Вашему Java-коду возможность обрабатывать ошибки при выполнении JavaScript-кода,
которые появляются в Java как исключения типа JSException.
Например, предположим, Вы работаете с Java-классом JavaDog. Как показано в следующем коде, конструктор JavaDog принимает в качестве аргумента JavaScript-объект jsDog, который определён как имеющий тип JSObject:
import netscape.javascript.*;
public class JavaDog
{
public String dogBreed;
public String dogColor;
public String dogSex;
// определяется конструктор класса
public JavaDog(JSObject jsDog)
{
// здесь try...catch
используется для обработки JSExceptions
this.dogBreed = (String)jsDog.getMember("breed");
this.dogColor = (String)jsDog.getMember("color");
this.dogSex = (String)jsDog.getMember("sex");
}
}
Обратите внимание, что метод getMember из JSObject
используется для доступа к свойствам JavaScript-объекта. Предыдущий пример
использует getMember для присвоения значения JavaScript-свойства jsDog.breed
члену данных Java JavaDog.dogBreed.
Более жизненный пример - помещение вызова метода getMember внутрь блока try...catch для обработки ошибок типа JSException. См. также Обработка Исключений JavaScript в Java.
Чтобы лучше понять, как работает getMember, посмотрите на
определение специального JavaScript-объекта Dog:
function Dog(breed,color,sex) {
this.breed = breed
this.color = color
this.sex = sex
}
Вы можете создать в JavaScript экземпляр объекта Dog под
названием gabby:
gabby = new Dog("lab","chocolate","female")
Если Вы вычисляете gabby.color, то увидите, что это
свойство имеет значение "chocolate". Теперь предположим, что Вы создаёте
экземпляр JavaDog в Вашем JavaScript-коде, передавая конструктору объект gabby:
javaDog = new Packages.JavaDog(gabby)
Если Вы вычисляете javaDog.dogColor, то увидите, что это
свойство также имеет значение "chocolate", потому что метод getMember в Java-конструкторе
присваивает переменной dogColor значение свойства gabby.color.
Когда JavaScript-код вызываемый из Java, терпит неудачу на этапе прогона программы, он вызывает исключение. Если Вы вызываете JavaScript-код из Java, Вы можете отловить это исключение в операторе try...catch. Исключение JavaScript доступно в Вашем Java-коде как экземпляр netscape.javascript.JSException.
JSException это Java-оболочка вокруг исключения любого
типа, вызываемого в JavaScript, аналогично тому, как экземпляры JSObject
являются оболочками для JavaScript-объектов. Используйте JSException, если Вы
вычисляете JavaScript-код в Java.
Если Вы вычисляете JavaScript-код в Java, следующие
ситуации могут вызвать ошибки времени выполнения:
Например, предположим, что Java-объект eTest вычисляет
строку jsCode, которую Вы ему передали. Вы можете отреагировать на любую ошибку
времени выполнения, возникающую при вычислении, реализуя такой обработчик
исключения:
import netscape.javascript.JSObject;
import netscape.javascript.JSException;
public class eTest {
public static Object doit(JSObject obj, String jsCode) {
try {
obj.eval(jsCode);
} catch (JSException e) {
if (e.getWrappedException()==null)
return e;
return e.getWrappedException();
}
return null;
}
}
В этом примере код блока try пытается вычислить строку jsCode,
переданную ему Вами. Скажем, Вы передали строку "myFunction()" в качестве
значения jsCode. Если myFunction не определена как функция JavaScript,
интерпретатор JavaScript не может вычислить jsCode. Интерпретатор генерирует
сообщение об ошибке, Java-обработчик отлавливает сообщение, а метод doit
возвращает экземпляр объекта netscape.javascript.JSException.
Предположим, однако, что myFunction определена в JavaScript
так:
function myFunction() {
try {
if (theCondition == true) {
return "Everything's ok";
} else {
throw "JavaScript error occurred" ;
}
} catch (e) {
if (canHandle == true) {
handleIt();
} else {
throw e;
}
}
}
Если theCondition - false, функция вызывает исключение.
Это исключение отлавливается в коде JavaScript, и, если canHandle - true, JavaScript
обрабатывает исключение. Если canHandle - false, исключение повторно вызывается, Java-обработчик
отлавливает его, а метод doit возвращает Java-строку:
См. в разделе "Операторы
Обработки Исключений" полную информацию об исключениях JavaScript.
В JavaScript 1.3 и ранее, класс JSException имел три public-конструктора, которые по выбору принимали строковой аргумент, специфицирующий детали сообщения или другую информацию для исключения. Метод getWrappedException отсутствовал.
Используйте оператор try...catch для обработки LiveConnect-исключений
в JavaScript 1.3 и более ранних версиях таким образом:
try {
global.eval("foo.bar = 999;");
} catch (Exception e) {
if (e instanceof JSException) {
jsCodeFailed()";
} else {
otherCodeFailed();
}
}
В этом примере оператор eval терпит неудачу, если foo не
определено. Блок catch выполняет метод jsCodeFailed, если оператор eval в блоке try
вызывает JSException; метод otherCodeFailed выполняется в случае, если блок try
вызывает какую-либо иную ошибку.
Поскольку язык Java строго типизирован, а JavaScript
типизирован слабо, машина выполнения JavaScript конвертирует значения аргументов
в типы данных, подходящие для других языков, если Вы используете LiveConnect.
Эта конвертация описана в разделах:
Если Вы вызываете Java-метод и передаёте ему параметры из JavaScript,
типы данных передаваемых параметров конвертируются в соответствии с правилами,
описанными в следующих разделах:
return-значения методов объекта netscape.javascript.JSObject
всегда конвертируются в экземпляры объекта java.lang.Object. Правила конвертации
этих return-значений также описаны в этих разделах.
Например, если JSObject.eval возвращает JavaScript-число,
Вы можете найти правила конвертации этого числа в экземпляр объекта java.lang.Object
в разделе Числа.
Если Вы передаёте числовые типы JavaScript в качестве параметров методам Java, Java конвертирует эти значения в соответствии с правилами, описанными в следующей таблице:
Если число JavaScript передаётся в качестве параметра в Java-метод,
который ожидает экземпляр объекта java.lang.String, это число конвертируется в
строку. Используйте метод equals() для сравнения результата этой конвертации с
другими строковыми значениями.
Если Вы передаёте Булевы типы JavaScript как параметры для Java-методов, Java конвертирует значения в соответствии с правилами, описанными в следующей таблице:
Если Булево значение JavaScript передаётся в качестве
параметра Java-методу, ожидающему экземпляр объекта java.lang.String, Булево
значение конвертируется в строку. Используйте операцию == для сравнения
результата этой конвертации с другими строковыми значениями.
Если Вы передаёте строковые типы JavaScript как параметры для Java-методов, Java конвертирует значения в соответствии с правилами, описанными в следующей таблице:
Тип Java-параметра | Правила Конверсии |
---|---|
Все значения конвертируются в числа так, как описано в ECMA-262. Строковые значения JavaScript конвертируются в числа в соответствии с правилами ECMA-262 |
|
Если Вы передаёте значения undefined JavaScript в качестве параметров методам Java, Java конвертирует значения в соответствии с правилами, описанными в следующей таблице:
Тип Java-параметра | Правила Конверсии |
---|---|
Значение конвертируется в экземпляр объекта java.lang.String, значением которого является строка "undefined". |
|
Конверсия значения undefined возможна только в JavaScript 1.3
и более поздних версиях. Более ранние версии JavaScript не поддерживают значения undefined.
Если значение undefined JavaScript передаётся в качестве
параметра Java-методу, который ожидает экземпляр объекта java.lang.String,
значение undefined конвертируется в строку. Используйте операцию == для
сравнения результата этой конвертации с другими строковыми значениями.
Если Вы передаёте значения null JavaScript в качестве параметра Java-методам, Java конвертирует значения в соответствии с правилами, описанными в следующей таблице:
Тип Java-параметра | Правила Конверсии |
---|---|
В большинстве случаев, если Вы передаёте JavaArray или JavaObject JavaScript в качестве параметра Java-методу, Java просто снимает оболочку с объекта; в некоторых случаях объект приводится к другому типу данных в соответствии с правилами, описанными в следующей таблице:
Интерфейс или класс совместим для присвоения с развёрнутым
объектом, если развёрнутый объект является экземпляром типа Java-параметра. То
есть, следующий оператор должен возвращать true:
развёрнутыйОбъект instanceof параметраТип
Если Вы передаёте объект JavaClass из JavaScript в качестве параметра Java-методу, Java конвертирует значение в соответствии с правилами, описанными в следующей таблице:
Если Вы передаёте любой иной объект JavaScript в качестве параметра Java-методу, Java конвертирует значение в соответствии с правилами, описанными в следующей таблице:
Тип Java-параметра | Правила Конверсии |
---|---|
Объект разворачивается в новый экземпляр объекта java.lang.JSObject. |
|
Объект разворачивается, вызывается метод toString развёрнутого Java-объекта и результат возвращается как новый экземпляр объекта java.lang.String. |
|
Объект конвертируется в значение с использованием логики оператора ToPrimitive, описанного в ECMA-262. Подсказка PreferredType, используемая с этим оператором, является Number. |
|
В JavaScript 1.3 и позднее, объект разворачивается, и возникает одна из
следующих ситуаций:
|
Значения, передаваемые из Java в JavaScript,
конвертируются так:
Заметьте, что экземпляры объектов java.lang.Double и java.lang.Integer
конвертируются в объекты JavaScript, а не в числа JavaScript. Аналогично и
экземпляры java.lang.String также конвертируются в объекты JavaScript, а не в
строки JavaScript.
Java String-объекты также соответствуют JavaScript-оболочкам. Если Вы вызываете метод JavaScript, который требует строки JavaScript, и передаёте его этой оболочке, Вы получите ошибку. Вместо этого конвертируйте оболочку в строку JavaScript путём присоединения к ней пустой строки, как показано здесь:
var JavaString = JavaObj.methodThatReturnsAString();
var JavaScriptString = JavaString + "";