36

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?


Note: Asked it as a question because, the post referenced is too old (2008) and the answerer is away since several months.

Community
  • 1
  • 1
mtk
  • 11,504
  • 15
  • 67
  • 104
  • 1
    may be it will confuse other developers :-), afterall it is not one line code. – Subin Sebastian May 06 '13 at 10:46
  • 2
    possible duplicate of [Efficiency of Java "Double Brace Initialization"?](http://stackoverflow.com/questions/924285/efficiency-of-java-double-brace-initialization) – assylias May 06 '13 at 10:52
  • 4
    @assylias: the linked-to question asks mainly about performance. I don't think it's a duplicate. – Joachim Sauer May 06 '13 at 12:48
  • 2
    @assylias You are marking this question about ugliness as an _exact duplicate_ of a question about performance? Yes, it touches the same topic. No, you haven't read either post. – Daniel A.A. Pelsmaeker May 07 '13 at 01:30
  • @Virtlink I have read both questions. This one focuses on *evil, ugly and bad* and could have been closed as not constructive. Now the facts are that this idiom is functionally equivalent to alternatives and is as readable if you know the Java language. So the only objective difference is implementation hence efficiency. Which by the way is what the most upvoted answer focuses on. Just my 2c. – assylias May 07 '13 at 08:34

6 Answers6

49

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

Community
  • 1
  • 1
Natix
  • 13,037
  • 7
  • 49
  • 67
  • 3
    This is true, as well. Abusing inheritance, instead of setting perfectly-well usable data properties (values/ or collection items), is just thoroughly evil. – Thomas W May 06 '13 at 12:45
  • 2
    +1 for showing that using DBI in `static` methods does much less harm. – gaborsch May 06 '13 at 15:54
  • 1
    Also, DBI means that many `equals` comparisons will fail because they check the class, and the classes are technically different (not `ArrayList`, but `MyClass$1`). – wchargin May 06 '13 at 21:37
  • 1
    @WChargin: `ArrayList.equals()` does **not** check the class, as that would break the `equals()` contract of `List`! – Joachim Sauer Jul 08 '13 at 06:24
19

The "ugly" and "do not use in production" comments refer to this specific use of anonymous classes, not to anonymous classes in general.

This specific use assigns newList an anonymous subclass of ArrayList<String>, an entirely new class created with a single purpose in mind - namely, initializing a list with the content of two specific lists. This is not very readable (even an experienced reader would spend a few seconds figuring it out), but more importantly, it can be achieved without subclassing in the same number of operations.

Essentially, the solution pays for a small convenience with creating a new subclass, which may result in problems down the road, for example, in situations when you try to persist this collection using an automated framework that expects collections to have specific types.

Sergey Kalinichenko
  • 675,664
  • 71
  • 998
  • 1,399
16

This particular use of anonymous classes has several problems:

  1. it's a little-known idiom. Developers that don't know it (or know it an don't use it a lot) will be slowed down when reading and/or modifying code that uses it.
  2. it's actually misusing a language feature: you're not trying to define a new kind of ArrayList, you just want some array list with some existing values in it
  3. it creates a new class that takes up resources: disk space to hold the class definition, time to parse/verify/... it, permgen to hold the class definition, ...
  4. even if the "real code" is slightly longer, it can easily be moved into an aptly-named utility method (joinLists(listOne, listTwo))

In my opinion #1 is the most important reason to avoid it, closely followed by #2. #3 is usually not that much of a problem, but should not be forgotten.

Smi
  • 12,505
  • 9
  • 53
  • 61
Joachim Sauer
  • 278,207
  • 54
  • 523
  • 586
  • 3
    It's mis-using inheritance, when inheritance is not required. – Thomas W May 06 '13 at 10:50
  • @ThomasW: yes, I think that's my #2, or did you interpret that differently? – Joachim Sauer May 06 '13 at 10:51
  • 3. [...], time for JVM to verify the class during the loading of ".class" file, ... – Joker_vD May 06 '13 at 10:51
  • @Joker_vD: I added verify to #3. But that's not supposed to be an extensive list, I just wanted to mention *some* resources es examples. An *exact* calculation of how much resources this approach costs is outside the scope of this answer. – Joachim Sauer May 06 '13 at 10:52
  • 2
    Thanks Joachim. My point is that #2 is the key one -- inheritance should never be used, when simply changing/setting the data of a mutable object can correctly accomplish the goal. – Thomas W May 06 '13 at 10:53
4

Because you don't need a separate subclass - you just need to create a new ArrayList of the normal class, and addAll() both lists into it.

Like so:

public static List<String> addLists (List<String> a, List<String> b) {
    List<String> results = new ArrayList<String>();
    results.addAll( a);
    results.addAll( b); 
    return results;
}

It's evil to create a subclass, that isn't needed. You don't need to extend or subclass behaviour - just change data values.

Thomas W
  • 13,374
  • 3
  • 50
  • 70
  • You could replace `` with `` and add a `` after `static` and it would work on all Lists. Also, adding varargs would be nice, so you can join any number of lists. – Joachim Sauer May 06 '13 at 11:51
  • 1
    I have this in my libraries :) But one hesitates to show anything complex, where someone has gone so wrong to start with. Correctness should be like a bolt-from-the-blue of simplicity. – Thomas W May 06 '13 at 12:46
2

It's not a bad approach per se, say, in performance or anything like that, but the approach is a bit obscure and when using something like this, you always (say, 99%) have to explain this approach. I think that's one of the biggest reasons not to use this approach, and while typing:

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

is a little more typing, it's a bit easier to read, which helps a lot in understanding or debugging code.

Erik Pragt
  • 11,804
  • 10
  • 44
  • 55
1

In your example it really looks evil and ugly, at least to me - it's difficult to understand what's going on in the code. But there are some patterns of using anonymous classes that people are used to because they see them very often, eg

    Arrays.sort(args, new Comparator<String>() {
        public int compare(String o1, String o2) {
            return  ... 
        }});

I would call the above a best practice case.

Evgeniy Dorofeev
  • 124,221
  • 27
  • 187
  • 258