Welcome to the home of MIJava.
MIJava provides multiple inheritance in Java. The Java language itself does not provide multiple inheritance for classes, and for valid reasons. (See: No More Multiple Inheritance)
Instead of using multiple inheritance in classes, it is recommended that developers use a combination of interfaces and delegation to solve the problem. For example, to mimic the behavior of a Sub class defined like this:
// This IS NOT valid Java public class Sub extends Super1, Super2 { // This Sub class autmatically inherits both // Super1.printName() and Super2.getName() } class Super1 { public void printName() { System.out.println("Super1"); } } class Super2 { public String getName() { return "Super2"; } } |
// This IS valid Java public interface Sub extends Super1, Super2 { } interface Super1 { public void printName(); } interface Super2 { public void printHello(); } public class SubI implements Sub { private Super1 super1 = new Super1I(); private Super2 super2 = new Super2I(); public void printName() { super1.printName(); } public String getName() { return super2.getName(); } } class Super1I implements Super1 { public void printName() { System.out.println("Super1"); } } class Super2I implments Super2 { public void printHello() { System.out.println("Hello"); } } |
This works okay for a small amount of inheritance, but quickly becomes tedious and error prone as the number of inherited methods and classes increases. (And some might argue that this kind of code generates more problems than multiple inheritance would.) This is where MIJava comes in. MIJava is a preprocessor which takes source code that is structured like the former example and emits source code that is structured like the latter example. The MIJava application works under the [perhaps generous] assumption that a programmatic process will make fewer mistakes than a developer would.
Currently, the MIJava application is in beta. To download a copy, click here. Please try it out and provide feedback on the project page. (The source code for the project is available on the project page as well.)
If you are running in a Unix environment, you can extract a script from the .jar file, which you then invoke to compile miclasses:
$ jar xf MIJava.jar mijava $ chmod +x mijava
First, you need to create a miclass. A miclass is just like a standard Java class, except that it can extend more than one class. (More specifically, it can extend more than one miclass. See Restrictions and Limitations below.) Example:
// This is valid MIJava public miclass Sub extends Super1, Super2 { // This Sub miclass autmatically inherits both // Super1.printName() and Super2.getName() } miclass Super1 { public void printName() { System.out.println("Super1"); } } miclass Super2 { public String getName() { return "Super2"; } } |
Next, you need to compile your miclass. If you are running in a Unix environment, you can simply invoke the mijava script just as you would execute the javac compiler:
$ mijava <args>If you are running on Windows, or some other environment, you would invoke the MIJava compiler like this:
> java -jar MIJava.jar <args>Where <args> are the same arguments that you would pass to the javac compiler.
There are a few restrictions to what exactly can be done with MIJava. Many of these "restrictions" are simply features that are intentionally left out of the 1.0 release criteria for MIJava, since they are issues that do not cause great problems for most users, and would be extra difficult to resolve. They will be added in to later releases of MIJava, based on popular demand.
Prior to releasing the version 1.0 of MIJava, the following items must be addressed:
Future versions of MIJava will likely add support for other Java compilers and eliminate some of the restrictions mentioned above. These improvements will be made according to popular demand.
In order to illustrate the transformations that are performed by MIJava, lets take a look at some expanded example miclasses:
public miclass Sub extends Super1, Super2 { // Overrides Super1.getSecret() public int getSecret() { return 5; } // Inherits Super1.addSecret(), // Super1.printSecret() and Super2.getName() } miclass Super1 { public int getSecret() { return 3; } public int addSecret(int x) { return x + getSecret(); } public void printSecret() { System.out.println(getSecret()); } } miclass Super2 { public String getName() { return "Super2"; } } |
A straightforward transformation of this code, like that shown in the above introduction, would result in this:
public interface Sub extends Super1, Super2 { public int getSecret(); } interface Super1 { public int getSecret(); public int addSecret(int x); public void printSecret(); } interface Super2 { public String getName(); } public class SubI implements Sub { private Super1 super1 = new Super1I(); private Super2 super2 = new Super2I(); public int getSecret() { return 5; } public int addSecret(int x) { return super1.addSecret(x); } public void printSecret() { super1.printSecret(); } public String getName() { return super2.getName(); } } class Super1I implements Super1 { public int getSecret() { return 3; } public int addSecret(int x) { return x + getSecret(); } public void printSecret() { System.out.println(getSecret()); } } class Super2I implement Super2 { public String getName() { return "Super2"; } } |
However, there are a few problems with that transformation. One problem is in the output of this code:
Sub sub = new SubI(); int added = sub.addToSecret(5); System.out.print(added); System.out.print(" "); sub.printSecret(); |
Another small problem is that it isn't safe to simply tack an "I" to the end of a class name, willy-nilly, and expect it to work out okay. There might be another class defined in the package that is named "SubI", which would cause a name conflict. So instead, we will prepend a "$" to the class name, which should work better, since the '$' character is reserved for use in mechanically generated source code, which is what we have here.
So after correcting these problems, and applying the same systematic changes to Sub, Super1, and Super2, we come out with this transformation:
public interface Sub extends Super1, Super2 { public int getSecret(); } interface Super1 { public int getSecret(); public int addSecret(int x); public void printSecret(); } interface Super2 { public String getName(); } public class $Sub implements Sub { private $Super1 $super1; private $Super2 $super2; private Sub $delegator = null; public $Sub() { $super1 = new $Super1(); $super1.set$delegator(this); $super2 = new $Super2(); $super2.set$delegator(this); } public set$delegator(Sub sub) { $delegator = sub; $super1.set$delegator(sub); $super2.set$delegator(sub); } public int getSecret() { if ($delegator == null) return $getSecret(); else return $delegator.getSecret(); } public int $getSecret() { return 5; } public int addSecret(int x) { if ($delegator == null) return $super1.$addSecret(x); else return $delegator.addSecret(x); } public void printSecret() { if ($delegator == null) $super1.$printSecret(); else $delegator.printSecret(); } public String getName() { if ($delegator == null) return $super2.$getName(); else return $delegator.getName(); } } class $Super1 implements $Super1 { private Super1 $delegator = null; public $Super1() { } public set$delegator(Super1 super1) { $delegator = super1; } public int getSecret() { if ($delegator == null) return $getSecret(); else return $delegator.getSecret(); } public int $getSecret() { return 3; } public int addSecret(int x) { if ($delegator == null) return $addSecret(x); else return $delegator.addSecret(x); } public int $addSecret(int x) { return x + getSecret(); } public void printSecret() { if ($delegator == null) $printSecret(); else $delegator.printSecret(); } public void $printSecret() { System.out.println(getSecret()); } } class $Super2 implements Super2 { private Super2 $delegator = null; public $Super2() { } public set$delegator(Super2 super2) { $delegator = super2; } public String getName() { if ($delegator == null) return $getName(); else return $delegator.getName(); } public String $getName() { return "Super2"; } } |
This code would produce the correct "10 5" output for test above. The main point of difference in this code is that the $Super1.getSecret() method first checks to see if there is a $delegator subclass. If there is a subclass, then $Super1.getSecret() calls that class's version of getSecret() rather than its own.
This code is quite a bit more convoluted, but lets trace a call to $Sub.addSecret(5) to see how it works. First, $Sub.addSecret() checks to see if $delegator == null. We'll assume for this example that this instance of $Sub does not have a subclass that is delegating method calls to it, so the "if" is true and we then call $Super1.$addSecret(5). $Super1.$addSecret() calls getSecret(), which is actually $Super1.getSecret(). $Super1.getSecret() first checks to see if $delegator is set. In this case, $delegator is set (to $Sub), so it then calls the getSecret() of the $delegator, which is $Sub.getSecret(). Since $Sub does not have a delegator, $Sub.getSecret() calls $Sub.$getSecret(), which returns 5.