Lesson 8: Calling Methods
Introduction
Calling methods is as easy as accessing fields, which we learned about in the previous lesson.
The proxy function names are as close to the Java names as possible. It is not always possible to have exactly the
same name because certain names clash with C# keywords like unchecked
or implicit
.
Java also allows the declaration of methods with the same name
as the class, even though they are not constructors. .NET does not allow that. In all these cases the proxy methods will (most likely) have an
underscore appended to their Java name (though the exact details are configurable in the JuggerNET code generator).
Passing Arguments
The proxy types closely mirror the inheritance hierarchy of their underlying Java types. This means that you can pass
a proxy type instance as a method argument in any place where the underlying Java type would be acceptable. The runtime
will also make sure that C# string literals (System.String
instances) can be used in place of Java Object
instances.
The java.lang.String
and java.lang.Object
types are treated differently. Arguments of these types
map to their .NET counterparts System.String
and System.Object
.
Java.Util.Hashtable ht = new Java.Util.Hashtable(); string key = "foo"; Java.Lang.Object value = new Java.Lang.Object(); // put a key/value pair in the hashtable // the key (.NET String) is converted to a Java string on the fly ht.Put( key, value ); // put another key/value pair in the hashtable // both key and value are converted on the fly ht.Put( "FOO", "BAR" );
Returning Results
In Java, what you regard as an object is actually always a reference to an object.
All non-primitive method results are therefore implicitly returned by reference. When a Java method is declared
to return a java.lang.Object
it actually rarely means that
you will get back an instance of type java.lang.Object
. That declaration just establishes that you
will get back an object of a type that is assignable to java.lang.Object
.
Please see the following Java code snippet
where we use our expert knowledge about the type of the stored value even though the get()
function is
declared to return Object
:
Hashtable ht = new Hashtable();
ht.put( "key", "value" );
String result = (String)ht.get("key");
This works because the result is returned by reference, allowing a String
to be passed in place of
an Object
.
.NET proxy methods are declared to return the proxy type for the Java return type. The question is: What .NET type will the returned object have? In an ideal world, the returned proxy object would have the proxy type of the returned Java object, but this is not always possible. You might not have the proxy type for the Java type that is returned by the method call.
It would be a poor product if it could not properly deal with this issue. When a proxy method has to return an object,
it returns a proxy of the type that is the most derived proxy type in the Java type's class
inheritance chain. Again, in a perfect
world that will be the exact proxy type for the returned Java type. If that proxy type is not available, the runtime checks whether
we have a proxy type for the Java type's super class. If yes, that is the proxy type that is returned. If no, the search continues
up the super class chain.
In a worst-case scenario, none of the proxy types for the concrete Java classes are available. If the method was declared
to return an interface type, the runtime picks the interface proxy type's Impl
type as the best possible return type.
This allows you to use the interface contract on the returned instance without having to do anything special.
There is also a special case for java.lang.String
instances. Java strings are always converted to
.NET System.String
instances.
null
return values map to null
as you would expect.
The key take-away from this discussion should be that you should get back the best available proxy type but not neceessarily the perfect proxy type.
Exceptions
Most of the operations you can perform in Java can result in an exception or an error being thrown. We're introducing the topic of exceptions here even though it also technically applies to accessing fields and invoking constructors. We're discussing the topic in greater detail in its own lesson but we quickly want to touch on it here.
The runtime library will check all Java calls to see whether an exception was thrown on the Java side. If so, it will look for the .NET proxy exception type that matches the Java exception type most closely and throw an instance of that type into .NET. This type mapping follows the same rules as outlined above in the discussion on return types.
Proxy exceptions are all derived from System.Exception
so you can catch them via that type if you don't care
about the particular exception type.