Casting and runtime type checking (using instanceof)


In this topic


About casting a run time type checking (using instanceof)

ArcObjects follow an interface based programming style. Many methods use interface types as parameters and have interfaces as return values. When the return value of a method is an interface type, the method returns an object implementing that interface. When a method takes an interface type as a parameter, it can take any object implementing that interface. This style of programming has the advantage that the same method can work with many different underlying objects that implement the required interface. Thus, client code is implementation agnostic.
In the Java programming language, the casting and coercing operation is used to convert between types and the instanceof operator is used to check for type information at run time. From ArcGIS 9.2 onwards, Java products provide a greater support for Java style casting and instanceof operations when compared to prior releases.
The usage of these operations in the context of ArcObjects Java programming is explained as follows:

Old casting method

The following are the types of casts you can use with the ArcGIS Java application programming interface (API):
You may want to know which of the specific concrete class types were returned by the IFeature.getShape() method for which the instanceof operator can be used. These concepts are best described by code examples that compare the two styles (old and new) of casting and performing run time checks while programming ArcObjects Java.
See the following code example of the old style of casting:
[Java]
IFeature feature = featureClass.getFeature(i);
//Assume that the underlying feature was a Point or a Polygon.
IGeometry geom = feature.getShape();
try{
    Point p = new Point(geom);
    // One reason for the following method's failure would be if geom is
    // not a Point.
    p.setX(value);
}

catch (Exception e){
    try{
        // p.setX(value) failed because geom is not of type Point.
        // Try Polygon.
        Polygon poly = new Polygon(geom);
        poly.getArea(); // Successful, if the geom is a Polygon.
    }
    catch (Exception e){
        e.printStackTrace();
    }
}
The old style of casting and run time checks using the single valued constructors of IxxxProxy() type classes is still supported for backward compatibility.

New casting method and run time checks

In the new style of casting, a full-fledged Point object is precast and returned as the result of the feature.getShape(). ESRI's interoperability bridge—which facilitates communication between the native ArcObjects runtime and the Java runtime environment—contains the logic for performing this precasting operation on the return value to hand back a concrete object that implements the returned interface, in addition to other relevant interfaces on that object (IPoint, IZAware, and so on) on a Point object.
See the following code example of the new style of casting:
[Java]
IFeature feature = featureClass.getFeature(i);
//Assume that the underlying feature was a Point or a Polygon.
IGeometry geom = feature.getShape();
if (geom instanceof Polygon)
    Polygon polygon = (Polygon)geom;
else if (geom instanceof Point)
    Point point = (Point)geom;

Working with workspaces

The concept of a workspace is central to any application that works with geographic data. The ArcGIS Java API provides support for working with approximately 16 different types of workspaces (ShapefileWorkspace, RasterWorkspace, and so on).
The workspace implementation in native ArcObjects follows the abstract factory pattern. The result is that a user opening a workspace through a workspace factory gets an interface (IWorkspace) on that underlying workspace that was created.
In prior releases, what followed was a proxy style cast to an interface on the Workspace class or the creation of the Workspace class by passing a reference to the IWorkspace interface, then casting to the required interface (for example, IDataset).

The Workspace class exposes a collection of generic interfaces and operations that are supported by all the typed workspace implementations. At the ArcGIS Java 9.2 release, the return value of each method used to open a workspace—IWorkspaceFactory.open(), IWorkspaceFactory.openFromFile(), or IWorkspaceFactory2.openFromString()—will be wrapped in an instance of the Workspace class so that switching between interfaces on the Workspace class (IWorkspace, IDataset, and so on) can be performed using Java's language support for casting.
See the following code example of usage patterns that can be used:
[Java]
ShapefileWorkspaceFactory shpWksFactory = new ShapefileWorkspaceFactory();
// Usage 1:
IFeatureWorkspace ws = (IFeatureWorkspace)shpWksFactory.openFromFile(
    "\path\to\workspace", 0);
// Usage 2:
Workspace ws = shpWksFactory.openFromFile("\path\to\workspace", 0);
IFeatureWorkspace fws = (IFeatureWorkspace)ws;
// Usage 3:
IWorkspace ws = shpWksFactory.openFromFile("\path\to\workspace", 0);
IFeatureWorkspace fws = (IFeatureWorkspace)ws;
[Java]
RasterWorkspaceFactory rstrWksFactory = new RasterWorkspaceFactory();
Workspace ws = rstrWksFactory.open("\path\to\workspace", 0);
// IRasterWorkspace is not implemented by the Workspace class.
IRasterWorkspace rasterWs = new IRasterWorkspaceProxy(ws);

ArcObjects that do not support casting or instanceof operator

In the ArcGIS Java API, the return values of certain methods cannot be cast to a particular category of types. The following are the types and classes that belong to this category: FeatureClass, FeatureCursor, FeatureDataset, RelQueryTable, Table, and so on.
At 9.3, you can easily identify which classes and interfaces support casting. An ArcObjects class supports casting if it's single argument constructor is deprecated. An ArcObjects interface supports casting if it's corresponding proxy is deprecated. Such proxies are intentionally not listed in the ArcObjects javadoc to prevent distracting developers.
If you find that an interface's proxy or a class' single argument constructor is deprecated, you can infer that it supports Java style casting. If not deprecated, you must use the old-style casting methodology described earlier in this document.
Such classes are designed to be abstract classes or concept types of classes in the ArcObjects object model specification. A FeatureClass, for example, can be thought of as a concept that models a finite set of spatial features. Internal to ArcObjects, there can be several different implementations of the notion of a feature class. Hence FeatureClass does not directly map to one particular underlying implementation of a feature class (as opposed to a Point class in the Java API that parallels a Point class in native ArcObjects).
The purpose of exposing the FeatureClass (and other classes in this category) in the Java API is to provide the user with the knowledge of a set of generic interfaces that every concrete implementation of such an abstract class would minimally support.
See the following code example:
[Java]
IWorkspaceFactory wf = new ShapefileWorkspaceFactory();
IFeatureWorkspace fw = new IFeatureWorkspaceProxy(wf.openFromFile("\path\to\data", 0)
    )

// This will not work and throws a java.lang.ClassCastException.
FeatureClass fc = (FeatureClass)fw.openFeatureClass("featureclass name");

// This is the correct way.
IFeatureClass fc = fw.openFeatureClass("featureclass name");
FeatureClass featureClass = new FeatureClass(fc);

// Or,

// This is the correct way using the old proxy style of casting. 
IFeatureClass fc = fw.openFeatureClass("featureclass name");
IGeoDataset gds = new IGeoDatasetProxy(fc);
The preceding code example shows that return values of methods—for example, fw.openFeatureClass()—will not be precast into any of the following candidate classes: FeatureClass, NAClass, RasterCatalog, RelQueryTable, RouteEventSource, or XYEventSource—that implement the returned interface because the internal ArcObjects classes implementing these abstract classes are private and not exposed through the API.
In this case, use the old proxy style casting to switch between interfaces that you expect on a candidate class (for example, from IFeatureClass to IGeoDataset) or create a new full-fledged instance of such a class using its single argument constructor (featureClass = new FeatureClass(fc)), then work with the interfaces on them as demonstrated in the code example.
The following code example summarizes the concepts discussed in this topic. The example opens a ShapefileWorkspace and retrieves a feature from a feature class:
[Java]
ShapefileWorkspaceFactory shpWksFactory = new ShapefileWorkspaceFactory();
IFeatureWorkspace fws = (IFeatureWorkspace)shpWksFactory.openFromFile(
    "\path\to\workspace", 0);
// This prints true.
System.out.println((fws instanceof Workspace));
IFeatureClass featureClass = fws.openFeatureClass("feature class");
// Old style casting needed here.
ITable table = new ITableProxy(featureClass);
int numRows = table.rowCount(null);
// This method returns a concrete object that models the underlying 
// geometry and feature (Point or Polygon).
IGeometry geom = featureClass.getFeature(i);
if (geom instanceof Point)
    doSomethingWithPoint();
else if (geom Instanceof Polygon)
    doSomethingWithPolygon();