DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
  1. DZone
  2. Coding
  3. Java
  4. How Java 10 Changes the way we Use Anonymous Inner Classes

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.

Adrian D. Finlay user avatar by
Adrian D. Finlay
·
Oct. 05, 18 · Presentation
Like (14)
Save
Tweet
Share
14.66K Views

Join the DZone community and get the full member experience.

Join For Free

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




Java (programming language)

Published at DZone with permission of Adrian D. Finlay, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Last Chance To Take the DZone 2023 DevOps Survey and Win $250! [Closes on 1/25 at 8 AM]
  • The Role of Data Governance in Data Strategy: Part II
  • A Beginner's Guide to Back-End Development
  • Debugging Threads and Asynchronous Code

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: