If you try to use Cocoa-Java, many things will simply not work as you
expect. If you want to start a JVM from Cocoa or use AppKit controls from Java,
you can use the JNI, as described in Technote 2147.

The Java Bridge is the worst documented part of Mac OS X (well, except those 'Description forthcoming' things, but at least they know that these parts are lacking!). The worst part of it is the documentation on how to use Java classes from Objective-C. This also happens to be something I've used extensively, so that is the topic of this article. This is one of the few "Undiscovered Countries" of Objective-C (you should feel like a pioneer now!).

At first glance, Java is much more like C++ than Objective-C, but don't be fooled by the wrapping: Java can be just as dynamic as Objective-C. That's why this Bridge is only possible with these two languages (and Python).

// Why Should I Use Java?

Maybe you read that you shouldn't use Java in Cocoa. That's true (in my opinion), but the Java Bridge enables you to write your application partly in Java and partly in Objective-C. This enables you to do the following (in agreement with the MVC model):

Write the Controller part in Objective-C, for easy integration and high speed.

Write the Model part in Java, which can be kept cross-platform to enable easy porting.

// Implementation

As a rule of thumb, you can treat Java classes just like Objective-C classes. There are just two exceptions:

The initialization is different.

You have to write the header for yourself (if you don't want hundreds of compiler warnings).

// How to Initialize

First, you have to add the key "NSJavaNeeded" to your Application Settings (Command-Opt-E in PB) with the string-value of "YES". This will tell the runtime to load Java when the application launches.

Calling a method with two arguments works this way (note that there's an error in the Apple docs here):

[vector add:0 :@"Inserted at the beginning"];

// Converting Objects

You may have noticed that I sent @"one item!", which is an NSString, to a Java class. How does a Java class handle it?

Simply: it doesn't. All NSStrings are automatically converted to java.lang.Strings, and vice versa (see the second line in the example above).

However, since all Cocoa API classes are properly wrapped to behave like Java classes, you can send them over the bridge. That does not work with your own objects without manual wrapping! But there's no problem with accessing Java objects you got from a Bridge call.

Additionally, Java's booleans are properly converted to BOOLs, and integers work, too (I haven't tried floats yet). There should be no problem. Converting a java.util.Date to NSDate is a bit more complicated. This is the method I use:

// Reference Counting vs. Garbage Collection

No, this isn't a discussion on what's better, but how they are used when mixed. This is a part Apple did really well. In Objective-C, all classes (even Java classes) use reference counting. In Java, all classes (even Objective-C classes) use Garbage Collection. That means you still have to release all Java objects you have allocated. End of discussion. Or is it "Discussion forthcoming?"

// What About Non-Empty Constructors?

NOTE: This feature is broken in 10.0.x. You need 10.1 or later to use it.

Believe it or not, Apple actually forgot to document this. Let's pretend you want to call this Java constructor:

(Note that '[class new]' is the same as '[[class alloc] init]' in terms of reference counting). That first argument (the signature) describes the constructor's parameters, it's documented in the Java specs. For easy accessibility, here's the important part:

BaseType Character

Type

Interpretation

B

byte

signed byte

C

char

Unicode character

D

double

double-precision floating-point value

F

float

single-precision floating-point value

I

int

integer

J

long

long integer

L<classname>;

reference

an instance of class <classname>

S

short

signed short

Z

boolean

true or false

[

reference

one array dimension

For historical reasons the syntax of fully qualified class and interface names that appear in class file structures differs from the familiar syntax of fully qualified names documented in section 2.7.5. In this internal form, the ASCII periods ('.') that normally separate the identifiers that make up the fully qualified name are replaced by ASCII forward slashes ('/'). For example, the normal fully qualified name of class Thread is java.lang.Thread. In the form used in descriptors in the class file format, a reference to the name of class Thread is implemented using a CONSTANT_Utf8_info structure representing the string "java/lang/Thread".

OK, enough copied. (Are you still there? Hello??)

For example, the constructor public Testclass(int a, String b, boolean c); is identified by @"(ILjava/lang/String;Z)".

// Defining the Classpath

If you want to access your Java classes, you usually have to define where the Java runtime should look for them. I won't go into many details here: that's something for the Bare Basics section of this site. You have to add the following keys to your expert application settings:

Key

Type

Comment

NSJavaRoot

String

The '.' directory for the classpath (a relative path from the application wrapper, so it should always begin with "Contents/...")

NSJavaPath

Array of Strings

All classpath entries in an array

// Using The Bridge In Foundation Tools

In tools, additional steps have to be taken in order to load the Java VM.

Add the JavaVM-framework (located in /System/Library/Frameworks) to your project.

Insert "#import <JavaVM/JavaVM.h>" to the top of your main source file.

Before using the Bridge the first time, insert "[NSJavaVirtualMachine defaultVirtualMachine];" to load it (this command may take some time).

If you want to specify the class path, use the following command instead (this example adds the PostgreSQL jar-file):

In regular Cocoa-apps, these steps are done by the Cocoa framework using the keys in the target's build settings.

That's all folks. Go and code some nice Objective-C/Java apps!

// Additional Information

John DeNisi sent me a mail with some additional information he got by "trying things once or twice", on some small test programs, not through any rigorous testing protocol. As always, your mileage may vary...

I found that Java booleans, shorts, ints, longs, floats, doubles and Strings
map nicely across the Java bridge as long as you declare the appropriate
Java classes/methods in Objective-C .h header files.

Java Number objects also auto-morph nicely to Objective-C NSNumber objects
(just like String <-> NSString), as long as you don't try to directly pass
Java Number sub-classes.

However, bytes don't work properly. When negative byte values are returned
from Java to ObjC, they appear to get cast to unsigned values. I also
haven't found any way to directly map Java Unicode (16-bit) char types.

Attempting to directly return Java primitive arrays to Objective-C causes
the Java bridge to blow up! However, if you first cast them to the generic
Java "Object" type on the Java side and return that "Object", you can then
successfully get it as an "id" in Objective-C and use the static methods of
the Java.lang.reflect.Array class to access elements one at a time. It's
probably inefficient, but does at least give you a way to access the
elements of Java primitive arrays from Objective-C.

Finally, your article didn't mention another very nice aspect of the Java
bridge: Uncaught Java Exceptions thrown by Java code called from
Objective-C are nicely auto-morphed to Objective-C NSException objects and
re-thrown up the Objective-C calling chain. I found this critical to
successfully getting my test app to work [andy: This is mentioned in the Apple docs].

Comments

I tried to use some java classes from Objective-C code, refering this page. But I received (null) from

I too am having the problem hiro explained... you seem to have found a way to do this bridging yourself, but for some reason I am not able to reproduce your results, perhaps there is some other project builder setting that needs to be set somewhere that I havn't found, but as it stands, your: well written, easy to follow, and informative description of using the java bridge... is useless.

and changed my build settings (note: not application settings) to NSJavaNeeded=YES. I couldn't find anythign that said application settings, in the latest PB. Got to bring in the framework and import the header, also.

Can someone tell me the difference between running the app out of PB directly, and just double clicking on the .app file built from PB. It seems that my Java calls work great if I am running out of PB, but if I build and the run the .app by double clicking it, it doesn't execute. Thanks! Maybe VM path or something needs to be specified...I missed it. Any ideas?

The Jave Bridge as outlined in this excellent article worked great for me until recently. Now the bridge is not working, but only not working for classes where constructors are required (ie newWithSignature: is used).

Here is some code for a test String using a constructor that takes a String.

Oh also i wouldn't do any
[NSClassFromString(@"java.lang.String") newWithSignature:@"..."]
as there is transparent bridging between java.lang.String and NSMutableString. You might as well just create an NSString the obj-c way and use it as a java.lang.string