Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the harm in using Anonymous class?

The question arose while reading a answer to this question - How do I join two lists in java. This answer gave the solution

List<String> newList = new ArrayList<String>() { { addAll(listOne); addAll(listTwo); } }; 

Reading the comments, users said it was evil and ugly and should not be used in production.

I would like to know what's the harm in using this? Why is it ugly, evil or bad to use in production?

like image 776
mtk Avatar asked May 06 '13 10:05

mtk


People also ask

What is the point of Anonymous classes?

Anonymous classes enable you to make your code more concise. They enable you to declare and instantiate a class at the same time. They are like local classes except that they do not have a name. Use them if you need to use a local class only once.

What is an anonymous class?

In Java, a class can contain another class known as nested class. It's possible to create a nested class without giving any name. A nested class that doesn't have any name is known as an anonymous class. An anonymous class must be defined inside another class. Hence, it is also known as an anonymous inner class.

What are the advantages of defining and instantiating anonymous inner classes?

The anonymous inner class has advantage over the inner class (as in the question example code) in that it closes over the local variables of the method (although only final locals are usable). Generally an inner class can be easily converted into a method with anonymous inner class, which helps reduce verbosity.

What do you mean by Anonymous class 9?

It is an inner class without a name and for which only a single object is created. An anonymous inner class can be useful when making an instance of an object with certain “extras” such as overriding methods of a class or interface, without having to actually subclass a class.


1 Answers

Except for the already mentioned issues regarding good programming style and inheritance misuse, there is one more subtle problem - inner classes and (non-static) anonymous class instances act as closures. This means that they keep an implicit reference to the enclosing class instance. This can result in preventing of garbage collection and in the end, a memory leak.

Given an example piece of source code:

public interface Inner {     void innerAction(); }  public class Outer {      public void methodInOuter() {}      private Inner inner = new Inner() {         public void innerAction() {             // calling a method outside of scope of this anonymous class             methodInOuter();           }     } } 

What happens at compilation time, is that the compiler creates a class file for the new anonymous subclass of Inner which gets a so-called synthetic field with the reference to the instance of the Outer class. The generated bytecode will be roughly equivalent to something like this:

public class Outer$1 implements Inner {      private final Outer outer; // synthetic reference to enclosing instance      public Outer$1(Outer outer) {         this.outer = outer;     }      public void innerAction() {         // the method outside of scope is called through the reference to Outer         outer.methodInOuter();     } } 

Such capture of reference to the enclosing instance happens even for anonymous classes that never actually access any of methods or fields of the enclosing class, such as the double-brace initialized (DBI) list in your question.

This results in the fact that the DBI list keeps a reference to the enclosing instance as long as it exists, preventing the enclosing instance from being garbage collected. Suppose the DBI list happens to live for a long time in the application, for example as a part of the model in MVC pattern, and the captured enclosing class is for example a JFrame, which is quite a large class with lots of fields. If you created a couple of DBI lists, you would get a memory leak very quickly.

One possible solution would be using DBI only in static methods, because there is no such enclosing instance available in their scope.

On the other hand, I would still argue that using DBI is still not necessary in most cases. As for the list joining, I would create a simple reusable method, which is not only safer, but also more concise and clear.

public static <T> List<T> join(List<? extends T> first, List<? extends T> second) {     List<T> joined = new ArrayList<>();     joined.addAll(first);     joined.addAll(second);     return joined; } 

And then the client code becomes simply:

List<String> newList = join(listOne, listTwo); 

Further reading: https://stackoverflow.com/a/924536/1064809

like image 102
Natix Avatar answered Sep 18 '22 10:09

Natix