Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

How Java 10 Changes the way we Use Anonymous Inner Classes

DZone's Guide to

How Java 10 Changes the way we Use Anonymous Inner Classes

Want to learn more about how Java 10 changes the way we use the anonymous inner classes? Check out this post to learn more.

· Java Zone ·
Free Resource

Java-based (JDBC) data connectivity to SaaS, NoSQL, and Big Data. Download Now.

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, and 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 that provide an implementation of an existing class. Typically, this finds a 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 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 the AnonDemoand and so on. You may not invoke these classes yourself, in an ordinary way, using the Java launcher. Unlike the other forms of inner classes, an anonymous inner class is always implicitly a child class of its reference type (with the exception of var, which we will discuss 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 pre-Java 10, this meant that, most of the time, anonymous inner classes were almost implicitly polymorphic. I say almost because non-polymorphic code like this was, of course, legal.

new Anon() { public void doSomething() { System.out.println("Woah"); } }.hello();

However, we wanted to assign the result of an anonymous inner class instantiation to a reference type, such an operation, which was inherently polymorphic. The simple reasons are because we are implicitly subclassing the class that we specified as the reference type for the anonymous inner class object, and 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: 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) override 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 overridden value of “Overridden” in our example, however, a call to doSomething() will cause compilation to fail. The reason?

A subclass object with a superclass reference type does not have access to the subclass’s members via the superclass reference. The only exception to this rule is that if the subclass overrides a method of the superclass that, in this case, is true to its polymorphic nature, Java uses Dynamic Method Dispatch and 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 that may change this fact.

What's Java 10 Got to Do With it All?

This looks at a small detail called the 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!!
    };
};


If 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 that it is one of the world’s worst hidden secrets. 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);


The full script for this post can be found below:

public class VarAnonInner {
public static void main (String[] args) throws Exception {
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();

Anon anonInner2 = new Anon() {
public void hello() { System.out.println("Woah! "); };
};
anonInner2.getClass().getMethod("hello").invoke(anonInner2);

new Anon() { public void hello() { System.out.println("Woah!!! "); };}.hello();

// VarAnonInner$1 vw = anonInner;

/*

Anon anonInner4 = new Anon() {
public void hello() { 
System.out.println("New method here!\n" +
"The class is:  " + this.getClass()
); 
};
};
anonInner4.hello();

*/

}
}

class Anon { };


That’s all folks... 




Connect any Java based application to your SaaS data.  Over 100+ Java-based data source connectors.

Topics:
java ,java 10 ,class ,anonymous inner classes

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}