Java gets a lot of blame for not allowing straight multiple inheritance and for not implementing closures. But according to Bruce Eckel, you can do a multiple inheritance in Java, you can do closures – sort of. You can accomplish that with inner classes!
In this final Part 4 entry, I will concentrate on these two advanced topics.
One way that you can do multiple inheritance is just by implementing two or more interfaces. Easy and already possible in Java. But what if you did not have an interface, but rather abstract or concrete class. You can no longer just extend two of them — Java limitation. Inner classes provide different options.
First, let’s take a look at two different ways you can implement multiple interfaces.
// Two ways that a class can implement multiple interfaces. // Thinking in Java example interface A {} interface B {} class X implements A, B {} class Y implements A { B makeB() { // Anonymous inner class: return new B() {}; } } public class MultiInterfaces { static void takesA(A a) { } static void takesB(B b) { } public static void main(String[] args) { X x = new X(); Y y = new Y(); takesA(x); takesA(y); takesB(x); takesB(y.makeB()); } } // /:~
Note how class Y implements multiple interfaces. I admit that I have never used it like that. But it does implement two interfaces.
What if you had an abstract or concrete class. You can’t extend two classes easily. Not without the use of inner classes! Here’s how inner classes allow you to do that.
// With concrete or abstract classes, innerÄ… // classes are the only way to produce the effect // of "multiple implementation inheritance." // Thinking in Java example class D {} abstract class E {} class Z extends D { E makeE() { return new E() {}; } } public class MultiImplementation { static void takesD(D d) {} static void takesE(E e) {} public static void main(String[] args) { Z z = new Z(); takesD(z); takesE(z.makeE()); } } // /:~
Possible? Yes. Clean? Not really. But you can!
Eckel says that with inner classes you have these additional features:
1. The inner class can have multiple instances, each with its own state information that is independent of the information in the outer-class object.
2. In a single outer class you can have several inner classes, each of which implements the same interface or inherits from the same class in a different way.
3. The point of creation of the inner-class object is not tied to the creation of the outer-class object.
4. There is no potentially confusing “is-a” relationship with the inner class; it’s a separate entity.
What is a closure? “A closure is a callable object that retains information from the scope in which it was created,” says Eckel. If you’ve been reading this series, you know that an inner class maintains a link to the outer class — that’s in fact a closure.
The following example illustrates a closure. It’s long, but it’s worth getting comfortable with. (Plus, it’s the final example in the series!)
// Using inner classes for callbacks interface Incrementable { void increment(); } // Very simple to just implement the interface: class Callee1 implements Incrementable { private int i = 0; public void increment() { i++; System.out.println(i); } } class MyIncrement { public void increment() { System.out.println("Other operation"); } static void f(MyIncrement mi) { mi.increment(); } } // If your class must implement increment() in// some other way, you must use an inner class: class Callee2 extends MyIncrement { private int i = 0; public void increment() { super.increment(); i++; System.out.println(i); } private class Closure implements Incrementable { public void increment() { // Specify outer-class method, otherwise // you'd get an infinite recursion: Callee2.this.increment(); } Incrementable getCallbackReference() { return new Closure(); } } class Caller { private Incrementable callbackReference; Caller(Incrementable cbh) { callbackReference = cbh; } void go() { callbackReference.increment(); } } public class Callbacks { public static void main(String[] args) { Callee1 c1 = new Callee1(); Callee2 c2 = new Callee2(); MyIncrement.f(c2); Caller caller1 = new Caller(c1); Caller caller2 = new Caller(c2.getCallbackReference()); caller1.go(); caller1.go(); caller2.go(); caller2.go(); } } /* Output:Other operation11 2 Other operation 2 Other operation 3 *///:~ }
This has been a long series — a first for me. I have learned a great deal about inner classes. I hope you find these helpful as well.
Inner classes have their uses. They can help you implement an elegant solution. They can help you accomplish things not easily doable using alternative ways. They can also complicate your code a great deal. They can make your code unreadable. Use it with care.
Reference
Thinking in Java (4th), Bruce Eckel
Java Inner Classes – Part 1 – Intro
Java Inner Classes – Part 2 – Anonymous
Java Inner Classes – Part 3 – Nested Classes