Why is there so much casting in Java converted from COM code?


Introduction

When dealing with programs written to the Component Object Model, it is easy to get confused as a Java coder, as generally COM programmers seem to do a lot of unnecessary explicit casting. Remember that explicit casting is when you preceed a variable with the type you want to convert it to in parentheses. This is used to convert between two data types the system won't automatically convert because information is lost or complexity added; for example (here we lose the ".3"):

int a = (int) 2.3;

The root of this apparently hyper-active casting in COM programmers is the COM "QueryInterface" methodology. Essentially, because COM objects could be written in a variety of languages, COM needs an intermediate step that allows an object to say to other objects "tell me about yourself" without actually having to engage with the object itself. The way to ask about an object is to ask it whether it implements a particular interface (remember these are sets of promises about methods in the object). By default, no object gives up anything about itself -- you have to ask whether it matches a specific interface. In addition, you often can't talk to an object until you do ask. The ramifications of this underlying standard is a system that is heavily based around interfaces, rather than objects. Often methods will return objects defined by their interfaces, and be very opaque about what the underlying object is. This means you see quite a lot of this kind of thing:

InterfaceA variableName1 = (InterfaceA)someMethodToGetAnObject();
InterfaceB variableName2 = (InterfaceB)variableName1;

i.e. you have no idea what the underlying object class is -- you just cast to the right interfaces to use it.

If you're interested, you can find out more about this at Microsoft's QueryInterface: Navigating in an Object. In Java, this seems quite unusual, as there is nothing to stop you directly assessing an object's methods without knowing its interfaces (indeed, this is only really common in the core language in Factory classes). The result of this is that Java programmers are often quite confused about how to convert code written by COM programmers to Java. The short answer is that you can't really go wrong by replicating all the casts that other languages with direct access to the COM would use. However, you may see that some can occasionally be dropped. So when is this the case?


Casting in Java, a refresher

By and large, the rule of thumb with primitives in Java is that you only need to cast explicitly if you are losing information (otherwise the JVM can do an automatic 'implicit' cast), so:

double a = 2; // a == 2.0 -- implicit cast as no information lost changing an int to a double.

int b = (int)a // b == 2 -- Explicit cast as ".0" lost, and this might be useful.

In addition, there's casting that requires more complicated methods, where a computer just doesn't understand two data types are related, like converting the character "2" to the number 2. These are less important here:

double a = String.parseDouble("2");

However, with objects, the rule of thumb is "if you need to reveal complexity, you need an explicit cast". This can be seen most clearly when you convert from a complicated thing to a simple one, and back again. For example, when putting a String into a Vector for storage -- remember that Vectors treat objects as the lowest common denominator class that every object invisibly inherits: java.lang.Object:

String h = "Hello World";
Vector v = new Vector();
v.add(h);
// Implicit cast of h to java.lang.Object.
// v.add() is declared as : "public void add (Object a)" -- well, almost. So the label
// attached is simplifying the treatment of the String to a java.lang.Object. h inside v is just
// treated like a very simple java.lang.Object - v only uses its java.lang.Object bits.


String h2 = v.elementAt(0);
// This, however, won't work, as v sees the object at index "0" as a java.lang.Object.
// Instead, we need to cast the complexity back onto the object:
String h2 = (String) v.elementAt(0);
// Obviously this only works if the object was originally a String (or extends String).
// If not, it throws a class conversion exception.


Issue 1: Knowing which interfaces you can apply

Let's look at an example from Arc programming now. Here's an example that uses an object in its underlying form, not cast into any interface (though note we have to cast it to the underlying form, as the method is declared to return the interface):

Map m = (Map) mxDoc.getFocusMap();
m.clearLayers();

This works fine, but you'd never know it, because there's no way other than guesswork to tell that getFocusMap() returns an underlying Map object. Instead the API says it returns an IMap object. As IMap is simpler than a Map (which implements the IMap interface), the API would rather you did this implicit cast:

IMap m = mxDoc.getFocusMap();
m.clearLayers();

Although chances are, a COM coder would actually do this:

IMap m = (IMap) mxDoc.getFocusMap();
m.clearLayers();

Just to match up with the COM style. Such an explicit cast, however, is only really needed where the underlying object is something that has been simplified, and we want to un-simplify it. In COM programming, this might be to attach another interface. For example, if we did this in ArcGlobe:

IMap m = (IMap) gmxDoc.getFocusMap();
m.clearLayers();

It would work fine, but the underlying map would be a Globe, not a Map. We could, therefore, do this

IMap m = (IMap) gmxDoc.getFocusMap();
m.clearLayers();
IScene is = (IScene) m;
is.setDescription("Empty planet");

Here the system has to take a complicated underlying object, which is being treated as an IMap, and forced it to switch the IMap methods for IScene methods. Fortunately, here, this would work. But because we're not just simplifying the object to one of its interfaces (or one of the interfaces IMap implements), we need an explicit cast.

Having seen this, lets go back to our original example:

IMap m = (IMap) mxDoc.getFocusMap();
m.clearLayers();

The problem with this is that the API for getFocusMap() says it just returns an IMap implementing object. We have no idea whether this is a Map object or a Globe object. We just have to quess the interfaces that may be available based on examples and the context. This is a significant issue. There's a lot of guesswork involved in finding out what you can subsequently cast objects into, if all you are told about them is the interface they will match when you are sent them.


Issue 2: Converting code without full documentation

However, what if we saw the above as an example in another language, and didn't even have the API? If we wanted to convert this to Java, there are at least three different definitions of getFocusMap() that might exist:

public IMap getFocusMap ()
public Map getFocusMap ()
public SomethingComplexThatImplementsIMapButHasBeenCastToSomethingElse getFocusMap ()

Under the third of these circumstances, this:

IMap m = mxDoc.getFocusMap();
m.clearLayers();

Would fail, whereas an explicit cast wouldn't. This state of ignorance is not unusual in other languages, which aren't as automatically documented as Java and often rely on detailed examples rather than API documentation. Because of this, explicit casts are commonly used even when not demanded by COM as a safety net. Hopefully, if you have the Java source code you can look at it, or generate the docs, and decide which option is in play for the Java version of the code. However, if (as is surprisingly common) you've only been supplied with the Java bytecode, and no source code or API, you're basically stuck having to follow prexisting examples and hoping for the best.


Summary

As you can see, then, there are two cases where you might want an explicit cast:

1) Where you aren't sure from an example what object you are being sent and you don't have access to the Java API for the packages you're using.
2) When you want to change from one interface to another (and the latter isn't a simpler version of the former).

However, liberal use of explicit casts is not unusual in COM programming. COM programmers feel safer in general with explicit casts in place as a) they are frequently needed to satisfy the COM object-to-object communication system, and b) documentation is often poor, and throws you onto examples where there is no indication of whether the underlying object has already been cast to something or not.