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.
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!