: code tutorial (v3)

Lesson 10: Casting Between Proxy Types

Introduction

No, we're not talking about the latest theatrical broadway production, this is all about dealing with the type system represented by the proxy classes and interfaces. Let's take a quick look at a common snippet of Java to illustrate the issues:

InitialContext   ictx = new InitialContext();
MyInterface      ifc = (MyInterface)ictx.lookup( "scope/ifc" );
MyOtherInterface ifc2 = null;

// the returned instance might also implement another interface
if( ifc instanceof MyOtherInterface )
    ifc2 = (MyOtherInterface)ifc;

How would you do this with proxy types? Here's a C# snippet that will probably work (we'll go into the reasons for the "probably" a little later):

InitialContext   ictx = new InitialContext();
MyInterface      ifc = (MyInterface)ictx.lookup( "scope/ifc" );
MyOtherInterface ifc2 = null;

if( ifc is MyOtherInterface )
    ifc2 = (MyOtherInterface)ifc;

This looks very nice and easy to use, but why did we use this disconcerting word "probably"? The reason is that whether or not this works depends on the set of proxy types that you have. The lookup() method is declared to return Object, so it doesn't provide us a lot of help in figuring out what is the best proxy type to return from an invocation. Consequently, we inspect the Java type of the returned object and go looking for the "best" .NET proxy type that is available.

To that effect, the JuggerNET managed runtime maintains a mapping between Java and available .NET proxy types. If we find an exactly matching proxy type, we will use it and everything works as expected because that proxy type will implement both proxy interfaces. But what if we don't have an exactly matching proxy type? Then we have to continue looking for a "best possible" match and this is where we get into very ambiguous territory.

In the above example, we have some out-of-band expert knowledge about the fact that the returned object implements two different interfaces and we use that knowledge in the code we are writing. The proxy classes and the runtime library don't have that expert knowledge. If no exact proxy type is available, the runtime has to pick a "decent" match and go with it. It might for example decide to return a proxy interface Impl type for the MyInterface type or it might decide to return a proxy interface Impl type for the MyOtherInterface type. The key word here is "or". It will pick one of the two choices and as a result of that choice one of the above casts or type tests will fail. So how do you avoid this problem?

The From() Method

Every proxy type declares a special static casting method called From() that is guaranteed to work because it inspects the Java side's type information and allows you to provide your expert knowledge, just like in the Java snippet. Here's what the guaranteed-to-work snippet looks like:

InitialContext   ictx = new InitialContext();
MyInterface      ifc = MyInterfaceImpl.From( ictx.lookup( "scope/ifc" ) );
MyOtherInterface ifc2 = null;

ifc2 = MyOtherInterfaceImpl.From( ifc );

You might have noticed the Impl types in the above snippet. Interface types are not allowed to contain static members in .NET. That's why every proxy interface type has a corresponding Impl class type which declares its static members, including the From() method.

Essentially, in the above snippet you're saying that you want the lookup() result to be treated as a MyInterface instance. A few lines later, you want it to be treated as a MyOtherInterface instance. In either case, the From() returns a properly typed proxy instance that wraps around the same Java object that was provided as input (via a duplicated JNI object reference).

The From() method allows you to perform safe type casts independent of the set of proxy types that you have available.

The .class Problem

The one additional thing that you might run into has to deal with the class keyword. In Java, you can gain access to a class instance by simply using the <classname>.class notation. class is a reserved word in C# that cannot be used in the same way, so we cannot generate a property called class that would allow you the same usage. The workaround is simple: use Class.ForName(). The only thing you need to remember is to use the fully qualified classname, for example:

Class   clsJavaString = Class.ForName( "java.lang.String" );

With this information, you should be able to tackle any casting and class-related problem from .NET.