New in JNBridgePro 8.0: Abstract inheritance + Cross-platform overrides = Support for frameworks

JNBridgePro allows you to “bridge any Java with any .NET, anywhere.” Certain aspects of bridging, though, may require more work than others. Two particular aspects of .NET/Java bridging that have required more workarounds than others are inheritance from abstract classes, and cross-platform overrides. In JNBridgePro 8.0, these aspects require no additional workarounds at all; they just work as expected. And now that they’re available, these two new features provide cross-platform support for software frameworks that depend on filling in behaviors by subclassing abstract classes and overriding method behavior.

Software frameworks

A software framework is a set of software classes that help to implement a group of functions. According to the Wikipedia definition, software frameworks have three distinguishing features:

  • Inversion of control: The framework calls the shots. It calls the user-supplied code, rather than the other way around.
  • Non-modifiable framework code: The framework’s code doesn’t change. Rather, the user supplies additional code to specify the functionality for this particular implementation.
  • Extensibility: The framework provides extension points where the user can add application-specific functionality.

There are many software frameworks in use: some well-known ones are Enterprise JavaBeans (EJBs) and Hadoop.

In object-oriented systems, extensibility is generally supported through the ability to override behaviors. The framework might supply abstract classes, and the user must flesh them out by creating concrete classes that inherit from them and supply specific behaviors. In other cases, the framework may supply default behaviors, and the user can override those default behaviors by overriding the methods that contain them.

Prior to JNBridgePro 8.0, abstract class inheritance and method overrides could not be implemented across platforms without somewhat complicated workarounds. In other words, one could not easily use JNBridgePro to write a C# class that inherited from a Java abstract class, and one could not use JNBridgePro to easily write a Java class that inherited from a C# class and overrode methods in the C# class. However, in JNBridgePro 8.0, this is not only possible, but it is easy to do, and it makes it simple to write .NET classes that extend a Java framework, or vice versa.

jnbproframeworksupport

Inheritance and abstract classes

Let’s start with subclassing abstract classes. In Java or C#, for example, it’s not uncommon to have situations like this:

public abstract class Shape
{
   public int x_position;   // x-coordinate of upper left corner of bounding rectangle
   public int y_position;   // y-coordinate of upper left corner of bounding rectangle
}

public class Rectangle extends Shape
{
   …         // additional functionality
}

…
Rectangle r = new Rectangle();

The above code is perfectly legal, and instantiates a Rectangle object that contains all the data defined in Rectangle, as well as all the data defined in Shape. It also executes all the initialization behavior specified by Shape, followed by all the initialization behavior specified by Rectangle.

Now, let’s say that we’re using JNBridgePro, and that Shape is a Java class, and we’re creating a .NET-to-Java project. We’ve proxied Shape to the .NET side, and want to create Rectangle as a C# class:

public class Rectangle : Shape
{
   …        // additional functionality
}

Before JNBridgePro 8.0, if we attempted to execute C# code that instantiates Rectangle:

Rectangle r = new Rectangle();

we’d get a java.lang.InstantiationException. Why? Because instantiating Rectangle causes the constructors for Rectangle and Shape to be executed, and one of the things that happens in Shape’s constructor, since Shape is a proxy, is for the underlying Java object to be instantiated. Since Shape is an abstract class on the Java side, which means that it can’t be instantiated, attempting to instantiate it on the Java side leads to an InstantiationException. (By the way, this problem also occurs in Java-to-.NET projects, where we proxy an abstract .NET class to the Java side, create a Java class that inherits from the proxy, and we instantiate the Java class.)

While there are ways to work around the problem, as is shown here, it requires adding additional code on the Java side (if it’s a Java abstract class). In .NET-to-Java projects, developers may not be able to touch the Java code (in any case, all the new development is happening on the .NET side), so the workaround might not be practical.

Starting with JNBridgePro 8.0, however, this problem no longer occurs. One can proxy the Java abstract class, subclass it in .NET, instantiate the .NET subclass, and it will just work. No InstantiationException will occur.

Cross-platform overrides

Let’s elaborate on the previous example.  We’ll start with the Java Shape class:

public abstract class Shape
{
   public int x_position;
   public int y_position;

   public abstract double area();
}

public class Rectangle extends Shape
{
   public double height = 5.0;
   public double width = 3.0;

   public double area()
   {
      return height * width;
   }
}

public class Circle extends Shape
{
   public double radius = 3.0;

   public double area()
   {
      return radius * radius * Math.PI;
   }
}

Of course, we can’t instantiate a Shape directly, but we can instantiate Rectangles and Circles:

Shape s1 = new Rectangle();
Shape s2 = new Circle();

If the area() method is called on the Shape object, the Rectangle or the Circle class’s area() method is actually called, as appropriate.  In other words, we can override Shape’s area() method, and the correct override method will be called.

Now, let’s change this so that the Rectangle and Circle classes are written in C# and inherit from the proxied Shape class:

public class Rectangle : Shape
{
   public double height = 5.0;
   public double width = 3.0;

   public override double area()
   {
      return height * width;
   }
}

public class Circle : Shape
{
   public double radius = 3.0;

   public override double area()
   {
      return radius * radius * Math.PI;
   }
}

Let’s also have made minor modifications to the Shape class on the Java side before we proxied it:

public abstract class Shape
{
   public int x_position;
   public int y_position;

   public abstract double area();

   public static Shape s1;
   public static Shape s2;
   public static double area1;
   public static double area2;

   public static void computeAreas()
   {
      area1 = s1.area();
      area2 = s2.area();
   }
}

If we write C# code so that the areas are computed on the .NET side, it should work just fine:

Shape s1 = new Rectangle();
Shape s2 = new Circle();
double area1 = s1.area();
double area2 = s2.area();

However, prior to JNBridgePro 8.0 if we do the calls to area() on the Java side, the Java side knows nothing about these overrides and the overriding area() methods will not be called. Consider the following C# code:

Shape.s1 = new Rectangle();
Shape.s2 = new Circle();
Shape.computeAreas();

The Java side knows nothing about the .NET classes Rectangle and Circle, but we can assign Rectangle and Circle to Shape.s1 and s2 because they’re both Shape objects and are recognized as such on the Java side. However, once they’re on the Java side, the call to area() will always result in a call to Shape’s area() method and, since Shape.area() is an abstract method, an AbstractMethodError will be thrown.

Prior to JNBridgePro 8.0, it was possible to work around the problem, as shown here.  However, a fair amount of extra code needs to be written, and as with the abstract class workaround, new code must be written on the remote platform (the Java side in this case), and that might not be practical.

Starting with JNBridgePro 8.0, cross-platform overrides will just work, with only a minimal amount of code added on the local side (in this case, the .NET side):

public class Rectangle : Shape
{
   static Rectangle()
   {
      Overrides.registerOverrides();
   }

   public double height = 5.0;
   public double width = 3.0;

   public override double area()
   {
      return height * width;
   }
}

public class Circle : Shape
{
   static Circle()
   {
      Overrides.registerOverrides();
   }

   public double radius = 3.0;

   public override double area()
   {
      return radius * radius * Math.PI;
   }
}

We need to tell JNBridgePro that there are overridden methods, so we include a call to Overrides.registerOverrides() in the static constructor of any class that needs to override proxy methods.  If this is done, the call to Shape.computeAreas()  from Java will automatically call the correct area() method, even if it’s written in .NET code.

Coming up

In the near future, we’ll be publishing a revised version of the Hadoop mapreducer lab, which will use the new cross-platform override capability. While the old version of the Hadoop lab incorporated an elegant workaround of the override issue that used callbacks, the new version will be much more straightforward. Watch for it!