А.6 Создание объектов - Кен Арнольд Джеймс Гослинг

^ А.6 Создание объектов
Вы можете создавать объекты Java внутри реализаций родных методов с помощью функции execute_java_constructor:

HObject *execute_java_constructor(ExecEnv *ee, char *className, ClassClass *classObj, char *signature, ...)

Создает новый объект указанного типа, задаваемого одним из двух параметров className или ClassObj (не используемый параметр должен быть равен NULL). Для создания объекта вызывается конструктор, описываемый строкой signature. За параметром signature следуют параметры конструктора.

Например, создание нового объекта типа ^ Simple с помощью безаргументного конструктора класса происходит следующим образом:

execute_java_contructor(NULL, "Simple", NULL, "()")

В данном случае сигнатура конструктора выглядит тривиально. Чтобы воспользоваться конструктором, который получает один или несколько параметров, необходимо включить их типы в сигнатуру, и поместить их значения в нужном порядке после строки сигнатуры. Типы в строке сигнатуры аналогичны тем, которые возвращаются Class.getName, и используют односимвольные сокращения для примитивных типов. Применяются следующие сокращения:

Z

boolean

I

int

C

char

J

long

B

byte

F

float

S

short

D

double

Чтобы избежать конфликтов между именами классов/интерфейсов и этими буквами, типы объектов получают имена вида " ^ Ltype" , где type — полное имя класса или интерфейса, в котором разделители-точки заменяются косой чертой, а в конце ставится точка с запятой. Например, параметр, представляющий собой объект String, будет выглядеть как " Ljava/lang/String;" . Для массивов указывается тип массива с префиксом [; так, массив значений типа long будет иметь тип " [J" . В многомерных типах используются несколько квадратных скобок. Массив String[][] будет выглядеть как " [[Ljava/labg/String;" .

Если вам все же непонятно, как указать конкретный тип в такой строке, вы можете написать класс Java, создающий объект нужного типа, и вызвать для него метод getClass().getName(). Затем, если полученное имя является именем типа, замените все точки на /, поставьте L спереди и ; сзади, если эти символы отсутствуют.

Аргументы, которые представляют собой объекты, передаются в виде указателей на дескрипторы.

Ниже показано, как происходит создание двух объектов Attr; первый из них использует конструктор с одним аргументом, которому передается только имя, а второй — конструктор с двумя аргументами, которому сообщается исходное значение.

oneArgAttr = (struct HAttr *)

execute_java_constructor(EE(), "Attr", NULL,

"(Ljava/lang/String;)", attrStr);


twoArgAttr = (struct HAttr *)

execute_java_constructor(EE(), "Attr", NULL,

"(Ljava/lang/String;Ljava/lang/Object;)",

attrStr, attrStr);

Точка с запятой выполняет функцию терминатора (завершающего символа) типа, а не разделителя параметров. Конструктор, получающий два параметра типа long и два параметра типа double, описывается строкой " (JJDD)" .
^ А.7 Вызов методов Java
Вызов методов Java из программ на C напоминает вызов конструкторов Java. Для этого используются следующие основные функции:

long *execute_java_static_method(ExecEnv *ee, ClassClass *cb, char *method_name, char *signature, ...)

Выполняет статический метод класса, описываемого параметром cb.

long *execute_java_dynamic_method(ExecEnv *ee, HObject *obj, char *method_name, char *signature, ...)

Выполняет нестатический (динамический) метод для заданного объекта.

В обоих функциях, параметр method_name является именем вызываемого метода, а signature описывает передаваемые аргументы. В отличие от конструкторов, вы также должны объявить возвращаемый методом тип после закрывающей скобки в сигнатуре. Возвращаемый тип указывается с использованием тех же сокращений, что и для типа параметров, с дополнительной буквой V для void. Примеры приведены ниже. Вы должны сами привести тип long к возвращаемому типу метода или проигнорировать его для методов типа void.

Для получения структуры класса, используемой при вызове статических методов, применяется одна из двух функций FindClass:

^ ClassClass *FindClass(ExecEnv *ee, char *class_name, bool_t resolve)

Возвращает указатель на структуру ClassClass для заданного класса. Как и прежде, параметр типа ExecEnv следует получить от функции EE. Логическая величина resolve аналогична одноименному параметру, используемому методом ClassLoader.loadClass в разделе 13.2.

^ ClassClass *FindClassFromClass(ExecEnv *ee, char *class_name, bool_t resolve, ClassClass *from)

Возвращает указатель на объект-класс для заданного класса с использованием объекта ClassLoader класса from.

Приведем пример, в котором метод System.out.println() вызывается для вывода сведений о работе родного метода. Статический родной метод grindAway класса Crunch, приведенный ниже, осуществляет некоторые трудоемкие вычисления и возвращает результат типа double:

double

Crunch_grindAway(struct HCrunch *this_h)

{

ClassClass *myClass;

HObject *out;

long i;

double result;

ExecEnv *ee = EE(); /* используется в нескольких местах */


myClass = FindClass(ee, "Crunch", TRUE);

out = (HObject *)execute_java_static_method(ee, myClass,

"outStream", "()Ljava/io/PrintStream;");

if (exceptionOccurred(ee))

return 0.0;

for (i = 0; i << NUM_PASSES; i++) {

execute_java_dynamic_method(ee, out,

"println", "(I)V", i);

if (exceptionOccurred(ee))

return 0.0; // необходимо что-то вернуть

/* .. вычисления ... */

}

return result;

}

Во фрагменте программы, предшествующем циклу, мы получаем дескриптор объекта java.io.PrintStream для System.out. Мы не можем непосредственно использовать значение статического поля System.out, потому что статические поля не имеют своего представления в родных методах; из-за этого приходится прибегать к обходным маневрам. В данном случае мы создаем статический метод, возвращающий нужное значение, и вызываем его из родного метода:

public static java.io.PrintStream outStream() {

return System.out;

}

Родной код должен получить указатель на структуру ClassClass для класса Crunch, поэтому мы вызываем FindClass. После этого можно вызвать функцию execute_java_static_method с указанием имени вызываемого метода и его сигнатуры, включающей тип возвращаемого значения. Мы преобразуем возвращаемое значение long к типу, необходимому для конкретного метода. В данном случае нам требуется общий дескриптор HObject, а не дескриптор для конкретного типа java.io.PrintStream.

После вызова метода проверяется, не было ли возбуждено исключение. Это стоит делать после каждого вызова метода или конструктора Java из родного кода, поскольку в противном случае вы можете пропустить исключение и иметь неприятности в будущем. Если мы выходим из функции по причине возникновения исключения, то возвращается фиктивное значение, которое игнорируется программой.

После того, как получен доступ к выходному потоку, можно начинать циклические вычисления. При проходе каждого цикла его номер выводится методом System.out.println. Для вызова этого метода мы используем функция execute_java_dynamic_method, передавая ей в качестве параметров объект, для которого вызывается метод, имя метода, его сигнатуру и аргументы. Нам нужна версия println, получающая аргумент типа int; этот метод имеет тип void, поэтому мы используем сигнатуру " (I)V" и передаем целое число, которое нужно вывести (i) после аргумента-сигнатуры. И снова при возврате из метода необходимо проверить, не возбуждено ли исключение.

Для многих типов, возвращаемых методами Java, приведение long к нужному типу происходит элементарно. Однако типы double и long в Java являются 64-разрядными, тогда как в большинстве существующих компиляторов C тип long 32-разрядный, и поэтому возвращаемое значение будет иметь только половинную длину. Хотя существуют различные способы получения всех 64 бит возвращаемого значения, о них не говорится в этой книге из-за их машинной зависимости.

1366214004477609.html
1366324313594097.html
1366384392249623.html
1366464992359322.html
1366542135686032.html