📜 ⬆️ ⬇️

How java 10 changes the way anonymous inner classes are used

Friends, as you have already noticed, at the end of June, we will start a lot of new groups, among them another stream of the Java Developer course, beloved by everyone. Right now we are sharing with you a new translation prepared for the students of this course.



When adding new features to a programming language, language developers usually pay attention to conflicts with current language features, changes leading to incompatibility with previous versions, errors and any situations that may lead to undefined or unexpected behavior.
')
However, they often do not pay enough attention to minor changes in new, practical methods of writing code. These changes are often side effects of new features. Such changes are not, strictly speaking, new features. These are subtle changes that appeared 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:


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

I believe that there is a moment that is not fully understood with regard to anonymous inner classes. The fact is that the programmer actually creates a subclass of the original class . This subclass is named Class$X , where Class is the outer class and X is a number that represents the order in which the inner classes are created in the outer class. For example, AnonDemo$3 is the third inner class created in AnonDemo . You cannot call these classes in the usual way. And, unlike other kinds of inner classes, an anonymous inner class is always implicitly a child class of the type from which it was created (with the exception of using var , which we will soon consider).
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 created an instance of an anonymous inner class based on the class Anon. In essence, we created a nameless subclass of a particular class . Before Java 10, anonymous inner classes were almost always implicitly polymorphic. I say “almost” because such non-polymorphic code like this will, of course, be executed.

 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 an anonymous inner class and we will not have access to a specific type of object ( Class$X ) in order to specify it in the source code.

Polymorphism and anonymous inner classes, practical implications


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

Therefore, in the previous code snippet, calling toString() for an object of an anonymous inner class would give us the overridden value “Overridden”, but calling doSomething() will 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 the subclass overrides the base class method. In this case, Java, true to its polymorphic nature, uses Dynamic Method Dispatch to select the version of the virtual method of the subclass at run time.

If you did not already know, a virtual method is a method that you can override. In Java, everything is not final, not private, and non-static methods are virtual by default. I speak by default, 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. 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 name of the var type, Java was able to determine the exact type of the anonymous inner class. Therefore, we are no longer limited to referring to the base class for accessing an object of a subclass.

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 were big debates about internal classes. And if it is a secret, then it is one of the worst secrets in the world. Of course, many people will not approve of the fact that you need an exact reference to an anonymous inner class, because 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 that is logically related to another class, for example, to handle events. However, probably, the curious Barbara didn’t get his nose off, and I’m ready to argue that most developers are very curious. Perhaps to the detriment of common sense!

Before 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, and also remind you that today at 20.00 a free webinar on the course will take place.

Source: https://habr.com/ru/post/456010/


All Articles