How Java 10 changes the way we use Anonymous Inner Classes.
When a new feature is introduced to a programming language specification, language designers typically look out for conflicts with existing language features, breaking changes, bugs, & any situation that can lead to undefined or unintended behavior.
Often enough, however, subtle changes in the new, practical ways we can now write code from version to version go without much notice. These changes are often the side-effects of a new addition to a programming language specification. These sorts of changes, are not, strictly speaking, new language features. However, they are subtle changes brought on by the advent of a feature or combination of features.
Anonymous Inner Classes
In Java, inner classes are classes defined as a member of a class. They may take one of four forms (anonymous, static, method-local, or instance member).
Anonymous inner classes are unnamed classes which provide an implementation of an existing class. Typically, this finds common practical use in event-driven programming for the handling of events. Usually, the anonymous inner class provides a concrete implementation for an abstract class, on the fly. This is, however, not required; Anonymous inner classes may be made from concrete classes.
A detail I believe is often enough not fully grasped about anonymous inner classes is that the programmer is actually subclassing the original class. This subclass is given the name Class$X
where Class
represents the Outer class and X
represents a number that represents the instantiation order of inner classes within that class. For example, AnonDemo$3
for the third inner class instantiated within AnonDemoand so on. You may not invoke these classes yourself, in the ordinary way, using the java launcher. Unlike the other forms of inner classes, an anonymous inner class is always implicitly a child class of it’s reference type (with the exception of var, coming up soon).
Let’s look at an example.
/* AnonDemo.java */class Anon { };public class AnonDemo {
public static void main (String[] args) { Anon anonInner = new Anon () {
public String toString() { return "Overriden"; }; public void doSomething() {
System.out.println("Blah");
};
}; System.out.println(anonInner.toString());
anonInner.doSomething(); // Won't compile!
};
};
In this example, we instantiated an anonymous inner class based on the concrete class Anon. Essentially, what we have done, is created an unnamed subclass of a concrete class. In java pre Java 10, this meant that most of the times, anonymous inner classes were almost implicitly polymorphic. I say almost, because non-polymorphic code like this was of course legal.
new Anon() { public void foo() { System.out.println("Woah"); } }.foo();
However, if we wanted to assign the result of an anonymous inner class instantiation to a reference type, such an operation was inherently polymorphic. The simple reasons are because we are implicitly subclassing the class which we specified as the reference type for the anonymous inner class object & the object’s most specific type (Class$X
)is not available for us to type within our application code.
A subclass object with a super class reference type does not have access to the subclass’s members via the super class reference.
Polymorphism and Anonymous Inner Classes, The practical consequence
Did you catch it in the code above? Because we are using a superclass reference to a subclass object, per the laws of polymorphism we can only refer to 1) methods defined by the superclass or 2) overriden, virtual methods in the subclass.
So in the previous code snippet, a call on the anonymous inner class object to toString() would give us the overriden value of “Overridden” in our example, however, a call to doSomething() will cause compilation to fail. The reason?
A subclass object with a super class reference type does not have access to the subclass’s members via the super class reference. The only exception to this rule is if the subclass overrides a method of the super class. In this case, true to it’s polymorphic nature, Java, through Dynamic Method Dispatch selects the subclass’s version of the virtual method at runtime.
In case you didn’t already know, a virtual method is a method that is able to be overridden. In java, all non-final, non-private and non-static methods are virtual by default. I say by default as opposed to implicitly because various java virtual machines may perform optimizations which may change this fact.
What’s Java 10 got to do with it all?
A small detail called type inference. Notice the following example:
/* AnonDemo.java */class Anon { };public class AnonDemo {
public static void main (String[] args) {
var anonInner = new Anon() {
public void hello() {
System.out.println("New method here, and you can
easily access me in Java 10!\n" +
"The class is: " + this.getClass()
);
anonInner.hello(); // Works!!
};
};
It works, we can call hello()! The devil is in the details. To folks familiar with var you will already see what’s happening here. By using the reserved type name var
Java was able to deduce the exact type of the anonymous inner class. Consequently, we are no longer stuck with using a superclass reference to access the subclass object.
What did we do when we needed a subclass reference Pre Java 10?
It is no secret that the inner class debates have been responsible for one too many flame wars in the distant, not-to-forgotten past. And if it is a secret, it’s no question one of the world’s worst hidden. Indubitably, there are many who are frowning at you for needing an exact reference to an anonymous inner class in the first place as the idea is to avoid adding any cruft to the class. Rather, they should be used to fulfill a contract on the fly, typically to facilitate for an operation logically linked to another class, such as is the common case with event handling. However, curiosity probably didn’t actually kill the cat and I’m willing to bet most developers are curious. Perhaps, to the detriment of our sanity!
With a bit of reflection magic, we can achieve a similar effect in Pre-Java 10 code as follows:
Anon anonInner2 = new Anon() {
public void hello() { System.out.println("Woah! "); };
};
anonInner2.getClass().getMethod("hello").invoke(anonInner2)
Reference
Source https://medium.com/the-java-report/how-java-10-changes-the-way-we-use-anonymous-inner-classes-b3735cf45593
Comments
Post a Comment