Accessing Fields


All the native methods that you saw so far were static methods with number and string parameters. We next consider native methods that operate on objects. As an exercise, we implement a method of the Employee class that was introduced in Volume 1, Chapter 4, using a native method. Again, this is not something you would normally want to do, but it does illustrate how to access fields from a native method when you need to do so.

Accessing Instance Fields

In order to see how to access instance fields from a native method, we will reimplement the raiseSalary method. In the Java programming language, the code was simple.

 public void raiseSalary(double byPercent) {    salary *= 1 + byPercent / 100; } 

Let us rewrite this as a native method. Unlike the previous examples of native methods, this is not a static method. Running javah gives the following prototype:

 JNIEXPORT void JNICALL Java_Employee_raiseSalary(JNIEnv *, jobject, jdouble); 

Note the second argument. It is no longer of type jclass but of type jobject. In fact, it is the equivalent of the this reference. Static methods obtain a reference to the class, whereas non-static methods obtain a reference to the implicit this argument object.

Now we access the salary field of the implicit argument. In the "raw" Java-to-C binding of Java 1.0, this was easya programmer could directly access object data fields. However, direct access requires all virtual machines to expose their internal data layout. For that reason, the JNI requires programmers to get and set the values of data fields by calling special JNI functions.

In our case, we need to use the GetdoubleField and SetDoubleField functions because the type of salary is a double. There are other functionsGetIntField/SetIntField, GetObjectField/SetObjectField, and so onfor other field types. The general syntax is:


x = (*env)->GetXxxField(env, this_obj, fieldID);
(*env)->SetXxxField(env, this_obj, fieldID, x);

Here, class is a value that represents a Java object of type Class, fieldID is a value of a special type, jfieldID, that identifies a field in a structure, and Xxx represents a Java data type (Object, Boolean, Byte, and so on). There are two ways to obtain the class object. The GetObjectClass function returns the class of any object. For example:

 jclass class_Employee = (*env)->GetObjectClass(env, this_obj); 

The FindClass function lets you specify the class name as a string (curiously, with / instead of periods as package name separators).

 jclass class_String = (*env)->FindClass(env, "java/lang/String"); 

Use the GetFieldID function to obtain the fieldID. You must supply the name of the field and its signature, an encoding of its type. For example, here is the code to obtain the field ID of the salary field.

 jfieldID id_salary = (*env)->GetFieldID(env, class_Employee, "salary", "D"); 

The string "D" denotes the type double. You learn the complete rules for encoding signatures in the next section.

You may be thinking that accessing a data field seems quite convoluted. The designers of the JNI did not want to expose the data fields directly, so they had to supply functions for getting and setting field values. To minimize the cost of these functions, computing the field ID from the field namewhich is the most expensive stepis factored out into a separate step. That is, if you repeatedly get and set the value of a particular field, you incur only once the cost of computing the field identifier.

Let us put all the pieces together. The following code reimplements the raiseSalary method as a native method.

[View full width]

JNIEXPORT void JNICALL Java_Employee_raiseSalary(JNIEnv* env, jobject this_obj, jdouble byPercent) { /* get the class */ jclass class_Employee = (*env)->GetObjectClass(env, this_obj); /* get the field ID */ jfieldID id_salary = (*env)->GetFieldID(env, class_Employee, "salary", "D"); /* get the field value */ jdouble salary = (*env)->GetDoubleField(env, this_obj, id_salary); salary *= 1 + byPercent / 100; /* set the field value */ (*env)->SetDoubleField(env, this_obj, id_salary, salary); }

CAUTION

Class references are only valid until the native method returns. Thus, you cannot cache the return values of GetObjectClass in your code. Do not store away a class reference for reuse in a later method call. You must call GetObjectClass every time the native method executes. If this is intolerable, you can lock the reference with a call to NewGlobalRef:

 static jclass class_X = 0; static jfieldID id_a; . . . if (class_X == 0) {    jclass cx = (*env)->GetObjectClass(env, obj);    class_X = (*env)->NewGlobalRef(env, cx);    id_a = (*env)->GetFieldID(env, cls, "a", ". . ."); } 

Now you can use the class reference and field IDs in subsequent calls. When you are done using the class, make sure to call

 (*env)->DeleteGlobalRef(env, class_X); 


Examples 11-11 and 11-12 show the Java code for a test program and the Employee class. Example 11-13 contains the C code for the native raiseSalary method.

Example 11-11. EmployeeTest.java
  1. public class EmployeeTest  2. {  3.    public static void main(String[] args)  4.    {  5.       Employee[] staff = new Employee[3];  6.  7.       staff[0] = new Employee("Harry Hacker", 35000);  8.       staff[1] = new Employee("Carl Cracker", 75000);  9.       staff[2] = new Employee("Tony Tester", 38000); 10. 11.       int i; 12.       for (Employee e : staff) e.raiseSalary(5); 13.       for (Employee e : staff) e.print(); 14.    } 15. } 

Example 11-12. Employee.java
  1. public class Employee  2. {  3.    public Employee(String n, double s)  4.    {  5.       name = n;  6.       salary = s;  7.    }  8.  9.    public native void raiseSalary(double byPercent); 10. 11.    public void print() 12.    { 13.       System.out.println(name + " " + salary); 14.    } 15. 16.    private String name; 17.    private double salary; 18. 19.    static 20.    { 21.       System.loadLibrary("Employee"); 22.    } 23. } 

Example 11-13. Employee.c

[View full width]

  1. #include "Employee.h"  2.  3. #include <stdio.h>  4.  5. JNIEXPORT void JNICALL Java_Employee_raiseSalary(JNIEnv* env, jobject this_obj,  jdouble byPercent)  6. {  7.    /* get the class */  8.    jclass class_Employee = (*env)->GetObjectClass(env, this_obj);  9. 10.    /* get the field ID */ 11.    jfieldID id_salary = (*env)->GetFieldID(env, class_Employee, "salary", "D"); 12. 13.    /* get the field value */ 14.    jdouble salary = (*env)->GetDoubleField(env, this_obj, id_salary); 15. 16.    salary *= 1 + byPercent / 100; 17. 18.    /* set the field value */ 19.    (*env)->SetDoubleField(env, this_obj, id_salary, salary); 20. } 


 Accessing Instance Fields 

  • jfieldID GetFieldID(JNIEnv *env, jclass cl, const char name[], const char sig[])

    returns the identifier of a field in a class.

    Parameters:

    env

    The JNI interface pointer

     

    cl

    The class object

     

    name

    The field name

     

    sig

    The encoded field signature


  • Xxx GetXxxField(JNIEnv *env, jobject obj, jfieldID id)

    returns the value of a field. The field type Xxx is one of Object, Boolean, Byte, Char, Short, Int, Long, Float, or Double.

    Parameters:

    env

    The JNI interface pointer

     

    obj

    The object whose field is being returned

     

    id

    The field identifier


  • void SetXxxField(JNIEnv *env, jobject obj, jfieldID id, Xxx value)

    sets a field to a new value. The field type Xxx is one of Object, Boolean, Byte, Char, Short, Int, Long, Float, or Double.

    Parameters:

    env

    The JNI interface pointer

     

    obj

    The object whose field is being set

     

    id

    The field identifier

     

    value

    The new field value


  • jfieldID GetStaticFieldID(JNIEnv *env, jclass cl, const char name[], const char sig[])

    returns the identifier of a static field in a class.

    Parameters:

    env

    The JNI interface pointer

     

    cl

    The class object

     

    name

    The field name

     

    sig

    The encoded field signature


  • Xxx GetStaticXxxField(JNIEnv *env, jclass cl, jfieldID id)

    returns the value of a static field. The field type Xxx is one of Object, Boolean, Byte, Char, Short, Int, Long, Float, or Double.

    Parameters:

    env

    The JNI interface pointer

     

    cl

    The class object whose static field is being returned

     

    id

    The field identifier


  • void SetStaticXxxField(JNIEnv *env, jclass cl, jfieldID id, Xxx value)

    sets a static field to a new value. The field type Xxx is one of Object, Boolean, Byte, Char, Short, Int, Long, Float, or Double.

    Parameters:

    env

    The JNI interface pointer

     

    cl

    The class object whose static field is being set

     

    id

    The field identifier

     

    value

    The new field value


Accessing Static Fields

Accessing static fields is similar to accessing non-static fields. You use the GetStaticFieldID and GetStaticXxxField/SetStaticXxxField functions. They work almost identically to their non-static counterpart, with two differences:

  • Since you have no object, you must use FindClass instead of GetObjectClass to obtain the class reference.

  • You supply the class, not the instance object, when accessing the field.

For example, here is how you can get a reference to System.out.

 /* get the class */ jclass class_System = (*env)->FindClass(env, "java/lang/System"); /* get the field ID */ jfieldID id_out = (*env)->GetStaticFieldID(env, class_System, "out", "Ljava/io/PrintStream;"); /* get the field value */ jobject obj_out = (*env)->GetStaticObjectField(env, class_System, id_out); 



    Core JavaT 2 Volume II - Advanced Features
    Building an On Demand Computing Environment with IBM: How to Optimize Your Current Infrastructure for Today and Tomorrow (MaxFacts Guidebook series)
    ISBN: 193164411X
    EAN: 2147483647
    Year: 2003
    Pages: 156
    Authors: Jim Hoskins

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net