Next: 3.4 Advanced Topics Up: 3. Programming GNUstep in Previous: 3.2 Accessing the GNUstep

Subsections

- 3.3.1 Classes
- 3.3.2 Methods
- 3.3.3 Constructors
- 3.3.4 Primitive Types
- 3.3.5 Strings
- 3.3.6 Numbers
  - 3.3.6.1 Details of Number Mapping
- 3.3.7 Exceptions
- 3.3.8 Base Library Structs

# 3.3 How the Wrapping Works

In this section, we review how the various Objective-C entities are exposed to Java.

## 3.3.1 Classes

Objective-C classes are usually exposed to Java with the same name they have in Objective-C. JIGS provides facilities to allow exposing Objective-C classes with a different name to Java; it's better to avoid it if possible.

## 3.3.2 Methods

Not all Objective-C methods are exposed to Java; some methods can not be exposed for obvious reasons (such as methods taking `void *` arguments). In general, methods taking pointer arguments (not pointing to objects) or an arbitrary number of arguments (in the way the `printf` function does) are not exposed to Java.

Arguments and return types are automatically converted by the interface.

Methods without arguments are (usually) exposed as they are: for example, the `NSProcessInfo` method

    - (NSString *) processName;

is exposed to Java as

     public native String processName ();

Sometime, the name is changed. JIGS allows to do this - but the less it is done, the easier for the programmer. Anyway sometimes it is appropriate; for example, the GNUstep method `description` (which can be invoked on any object to return a string describing the object) is exposed to Java with the name `toString ()` (which is the standard method for doing the equivalent job in the Java world).

In Objective-C, methods have their arguments introduced by ``:`', as in the following example:

    - (void) addObject: (id)anObject;

This method is accessible from Java as

    public native void addObject (Object arg0);

The only difference is that in Java the argument is enclosed in brackets.

Things get more complicated if the method has multiple arguments. In Objective-C, each argument is introduced by a little string followed by ``:`', as in the following example:

    - (BOOL) writeToFile: (NSString *)fileName
              atomically: (BOOL)flag;

In this example, `fileName` is the first argument, and `flag` is the second one. The method name is

    writeToFile:atomically:

The method is invoked as follows:

    /* Objective-C example */
    [array writeToFile: @"bar"  atomically: YES];

In Java, these methods are (usually) exposed by keeping only the first part of the name (which is what comes before the first ``:`'). For example, `writeToFile:atomically:` is exposed as:

    public native boolean writeToFile (String arg0, boolean arg1);

and can be then invoked as follows:

    /* Java example */
    array.writeToFile ("bar", true);

It is possible that different Objective-C methods have a name which begins in the same way, but differs in the next parts. For example, imagine that an hypothetical Objective-C class `LocalizedArray` had both the method

    - (BOOL) writeToFile: (NSString *)fileName
              atomically: (BOOL)flag;

and the method

    - (BOOL) writeToFile: (NSString *)fileName
              atomically: (BOOL)flag
              inLanguage: (NSString *)language;

This second method would translate all the entries of the hypothetical array object into `language`, and then save it.

The methods would be exposed to Java as:

    public native boolean writeToFile (String arg0, boolean arg1);
    public native boolean writeToFile (String arg0, boolean arg1, String arg2);

which causes no conflict, because Java allows overloading of methods, and the two methods have a different Java signature. It is worth to remark here that the two methods are different in Objective-C because their names are different, while they are different in Java because their (name + Java signature)s are different. This will be important later in the discussion of selectors.

In rare cases, the conflict can't be escaped - JIGS allows the programmer doing the wrapper to resolve the conflict by exposing one of the two conflicting methods with a different name to Java.

## 3.3.3 Constructors

In GNUstep, you create objects using `alloc`, followed by some variant of `init`. When you expose your class to Java, a corresponding constructor is created for each (`alloc` + `init`) pair.

In short, for each Objective-C `init` method, the Java class has a constructor.

Let us consider for example the `NSArray` class, which is available in Java as the `gnu.gnustep.base.NSArray` class.

In Objective-C, the class has the following `init` methods:

    - (id) init;
    - (id) initWithArray: (NSArray*)array;
    - (id) initWithContentsOfFile: (NSString*)file;
    - (id) initWithObjects: firstObj, ...;
    - (id) initWithObjects: (id*)objects count: (unsigned)count;

The last two methods are not exposed to Java (the first one because it takes an arbitrary number of arguments, which Java does not support; the second one because it takes a pointer argument).

The corresponding Java class has the following constructors:

    public NSArray ();
    public NSArray (NSArray array);
    public NSArray (String fileName);

For example, to create an array from the contents of the `array.debug` file in my home directory, I would do:

    /* Java Example */
    NSArray array = new NSArray ("/home/nicola/array.debug");

When two Objective-C `init` methods have different names but arguments of the same type, it is of course impossible to expose both of them to Java. This case is rare and the only solution is to change or extend the Objective-C API (or expose only one of the two).

## 3.3.4 Primitive Types

Primitive types are mapped automatically by the interface. For example, an Objective-C method returning a `BOOL` (the Objective-C type for boolean variables) will return a `boolean` in Java.

## 3.3.5 Strings

Strings are transparently morphed through the interface. To the end-user programmer, this simply means that Objective-C methods having as argument (or returning) Objective-C `NSString` objects take as argument (or return) in Java Java `String` objects.

Internally, when the method is called, the interface generates on the fly a GNUstep `NSString` object corresponding to the Java `String` object, and passes it to Objective-C. Viceversa, when an Objective-C method returns a `NSString`, a Java `String` object is generated on the fly from the returned string and returned to Java. This design is very comfortable for the programmer, who always uses the string objects of the environment he is programming in, but it is not very efficient. This will only have an effect on your program's performance if you pass very big strings - and (or) a huge number of them - through the interface, while normally it doesn't make any appreciable difference in terms of performance.

## 3.3.6 Numbers

Numbers (instances of `java.lang.Number` and subclasses) are transparently morphed through the interface, in the same way as strings are. Again, this simply means that if an Objective-C method takes as argument (or returns) an Objective-C `NSNumber` object, the method in Java will take as argument (or return) a Java `Number` object; the interface will automatically take care of the conversion.

### 3.3.6.1 Details of Number Mapping

This section explains the internals of the number morphing; it's quite technical and for most people it's not really useful nor interesting, so you may safely skip it at a first reading.

Internally, the situation is more complicated than in the case of strings, because there are many public subclasses of `java.lang.Number`, which hold considerably different types of numbers each; and `NSNumber`, according to its Objective-C type, can hold very different types of numbers as well.

When the interface has to convert a Java `Number` object to an Objective-C `NSNumber` object, it will check if the `Number` object belongs to one of the standard Java number classes (`Byte`, `Short`, `Int`, `Long`, `Float`, `Double`); in this case, it will extract the value of the number in the appropriate precision (for example, by using `byteValue` for a `Byte`, or by using `longValue` for a `Long`), and create a corresponding `NSNumber` object with an Objective-C type which is big enough to hold the value. If the `Number` object instead is of a non-standard class (such as a class you implemented), the interface will extract the value of the number by using `doubleValue`, and create a `NSNumber` object big enough to hold this double value. This can obviously result in loss of precision or conversion problems if the Java number object is holding a value which can't be represented with a double.

When, viceversa, the interface has to convert an Objective-C `NSNumber` object into a Java `Number` object, it will check the Objective-C type of the Objective-C object (only a finite, documented number of number types are possible); it will extract the value using the appropriate method (such as `longValue` is the number is of type `long`), and create a Java `Number` object of a standard class which can hold the value.

Please notice that an Objective-C `NSNumber` object can hold a `BOOL` (boolean) value; this type is morphed into a small Java `Number` object (usually a `Short`) holding `0` for `NO`, and `1` for `YES`. It can't be morphed into a Java `Boolean` object, because in Java `Boolean` objects are not numbers, and the whole point of morphing is that a `NSNumber` is morphed exactly into a Java `Number`.

## 3.3.7 Exceptions

Exception generated during calls to the GNUstep Objective-C code are made available to Java as objects of the `gnu.gnustep.base.NSException` class. This is a subclass of `java.lang.RuntimeException`. GNUstep Objective-C exception usually have a `name` and a `reason`. For example, `NSArray`'s `objectAtIndex ()` method can generate an exception if you ask for an object which lies past the end of the array; in this case the name of the exception is ``NSRangeException`', and the reason is ``Index out of bounds`'. Here is an example:

    /* Java Example */
    NSArray array;
    Object object;

    array = new NSArray ();
    try
    {
      /* Raises an exception because array is empty */
      object = array.objectAtIndex (5);
    }
    catch (NSException e)
    {
      System.out.println ("Name is " + e.name ());
      System.out.println ("Reason is " + e.reason ());
      System.out.println ("Full Description is:" + e);
    }

The example shows how to access the information about the GNUstep exception. On my system, it prints:

    Name is NSRangeException
    Reason is Index out of bounds
    Full Description is gnu.gnustep.base.NSException(NSRangeException):Index out of bounds

Many GNUstep methods can raise exceptions, even if these exceptions are not declared in the Java method declarations (contrary to the Java convention) because in Objective-C the exceptions raised by methods are declared in the documentation, not in the code - so it's important to understand that, when accessing GNUstep from Java, you could need to use an exception handler for delicate parts of your code even if your Java compiler does not force you to do so.

## 3.3.8 Base Library Structs

The fundamental structs defined in the base library

    NSRange, NSPoint, NSSize, NSRect

are exposed as the Java classes

    gnu.gnustep.base.NSRange
    gnu.gnustep.base.NSPoint
    gnu.gnustep.base.NSSize
    gnu.gnustep.base.NSRect

The important thing to understand is that you should think of objects of these classes as C struct - that is, very lightweight objects which are not meant to be subclassed (actually, you can't subclass them at all, since they are `final`), and only act as a container for information which you want to access and pass around as fast as possible. This is actually why they are structs and not objects in the GNUstep Objective-C libraries in the first place.

We discuss here the case of `NSPoint`; the other cases are completely similar.

Whenever an Objective-C method takes a `NSPoint` as argument, the corresponding Java method takes a `gnu.gnustep.base.NSPoint` object as argument. When the method is called, JIGS internally creates a struct (allocated on the stack so it's fast) and initializes it with the `x` and `y` coordinates stored in the Java `NSPoint` object; it passes this struct as argument to the method call3.1.

Whenever an Objective-C method returns a `NSPoint` (C struct), the corresponding Java methods returns a Java `NSPoint` object. When the method is called, JIGS converts the returned struct by creating a corresponding Java `NSPoint` object, and returning this object to Java.

To create an `NSPoint`, you just use the constructor `NSPoint (float x, float y)`, as follows:

    /* Java Example */
    NSPoint point;

    point = new NSPoint (1, 1);

Once you have created a `NSPoint`, you can read and set its `x` and `y` coordinates directly, as in the following example:

    /* Java Example */
    point.x = 5;
    point.y = 4;

What is extremely nice in this approach is that you read and set the coordinates of the point with exactly the same code you would use in Objective-C:

    /* Objective-C Example */
    point.x = 5;
    point.y = 4;

Some functionality which is provided by the GNUstep Base Library in functions has been exposed as methods of the class `gnu.gnustep.base.NSPoint`. This mainly includes:

    public boolean isEqualToPoint (NSPoint aPoint);
    public String toString ();

which provide the equivalent of the GNUstep Base Library functions

    BOOL NSEqualPoints (NSPoint aPoint, NSPoint bPoint);
    NSString *NSStringFromPoint (NSPoint aPoint);

Since `gnu.gnustep.base.NSPoint` is `final` (ie, it can't be subclassed), your Java compiler should be able to inline all the Java equivalent of these little functions, and they should be reasonably fast.

Next: 3.4 Advanced Topics Up: 3. Programming GNUstep in Previous: 3.2 Accessing the GNUstep