How Java 10 changes the way you use anonymous inner classes

Original author: Adrian D. Finlay
  • Transfer
Friends, as you have already noticed, at the end of June, we will start a lot of new groups, among them the next stream of the “Java Developer” course that everyone loves . Right now we are sharing with you a new translation prepared for students of this course.



When adding new features to the programming language, language developers usually pay attention to conflicts with the current capabilities of the language, to changes that lead to incompatibility with previous versions, to errors and any situations that may lead to undefined or unexpected behavior.

However, often they do not pay enough attention to minor changes in new, practical techniques for writing code. These changes are often side effects of new features. Such changes are not, strictly speaking, new features. These are subtle changes that have arisen due to other features or their combinations.

Anonymous inner classes


In Java, inner classes are classes defined as members of a class. There are four kinds of inner classes:

  • static nested
  • internal
  • local (method local)
  • anonymous

Anonymous inner classes are unnamed classes that provide an implementation of an existing class. Such classes are widely used to handle events in event-oriented programming. Usually an anonymous inner class provides an on-the-fly implementation of an abstract class. However, this is not necessary. An anonymous inner class can be created from a non-abstract class.

I believe that there is a point that is not fully understood in relation to anonymous inner classes. The fact is that the programmer actually creates a subclass of the source class . This subclass is given a name Class$X, where it Classrepresents the outer class, and Xa number representing the order in which instances of inner classes are created in the outer class. For instance,AnonDemo$3- The third inner class created in AnonDemo. You cannot call these classes in the usual way. And, unlike other types of inner classes, an anonymous inner class is always implicitly a child class of the type on the basis of which it was created (with the exception of use var, which we will look at 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(); // Не скомпилируется!
   };
};

In this example, we instantiated an anonymous inner class based on the Anon class. Essentially, we created an unnamed subclass of a particular class . Prior to Java 10, anonymous inner classes were almost always implicitly polymorphic. I say “almost”, because such non-polymorphic code like this, of course, will execute.

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

However, if we want to assign the result of creating an instance of an anonymous inner class to the original type, then such an operation will be polymorphic in nature. The reasons for this lie in the fact that we implicitly create a subclass of the class that we specified as the source for the anonymous inner class and we won’t be able to access the specific type of object ( Class$X) in order to specify it in the source code.

Polymorphism and anonymous inner classes, practical consequences


Did you notice the code above? Since we use a base class reference to a subclass object, according to the laws of polymorphism we can refer only to 1) methods defined by the base class or 2) overridden virtual methods in the subclass.

Therefore, in the previous code fragment, a call toString()to an anonymous inner class object would give us an overridden “Overridden” value, however, the call doSomething()would result in a compilation error. What is the reason?

A subclass object with a reference to the type of the base class does not have access to the members of the subclass through this reference to the base class. The only exception to this rule is if a subclass overrides the base class method. In this case, Java, true to its polymorphic nature, uses the Dynamic Method Dispatch to select the version of the virtual subclass method at runtime.

If you did not already know, a virtual method is a method that can be overridden. In Java, all non-final, non-private and non-static methods are virtual by default. I speak by default, and not implicitly, because different jvm can perform optimizations that can change this.

What does Java 10 have to do with it?


A small feature called type inference. Take a look at 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(); // Работает!!
  }
}

It works, we can call hello()! The devil is in the details. If you are familiar with var, then you already understand what is happening here. Using the reserved type name var, Java was able to determine the exact type of the anonymous inner class. Therefore, we are no longer limited to referencing the base class to access the subclass object.

What did we do before Java 10 when we needed a reference to a subclass?


It is no secret that in the distant, but not forgotten past, there was great debate about the inner classes. And if this is a secret, then this is one of the worst secrets in the world. Undoubtedly, many will not approve that you need an exact reference to an anonymous inner class, since the idea is to avoid adding garbage to the class. Anonymous classes should be used to execute a contract on the fly, for an operation logically related to another class, for example, for handling events. However, probably, the curious Barbara wasn’t torn off her nose, and I bet that most developers are very curious. Perhaps to the detriment of common sense!

Prior to Java 10, we can achieve a similar effect using reflection, as follows:

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

Full source


Can take here

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 { };

We are waiting for your comments, as well as recall that a free webinar on the course will take place today at 20.00 .

Also popular now: