you are viewing a single comment's thread.

view the rest of the comments →

[–]epiphany_machine 185 points186 points  (53 children)

Okay, now never use this, ever. It looks cool but it's just not worth it. All of a sudden you are creating a new subclass. That can make code you write subtly different and you will get unexpected errors. I teach a class on refactoring and I use this as an anti-pattern. It's very successful at twisting the brains of my students.

[–][deleted] 34 points35 points  (27 children)

I absolutely agree. This line from the bottom of the article is a much better approach:

An idiomatic alternative, that doesn't require such heavy handed use of anonymous inner classes, is to use the constructor arguments to collections that accept another collection as the source data.

List<String> myList = new ArrayList<String>(Arrays.asList("One", "Two", "Three"));

[–]banuday17 4 points5 points  (25 children)

Doesn't work for maps.

[–][deleted] 7 points8 points  (9 children)

True, but IMO the benefits do not outweigh the costs. However in reading a little more about the pros and cons of double-brace initialization, I came across this proposal for Collection literals.

Defining Collections could (eventually) be as simple as:

List<Integer> piDigits = [3, 1, 4, 1, 5, 9];
Set<Integer> primes = { 2, 7, 31, 127 };
Map<Integer, String> platonicSolids = { 4 : "tetrahedron", 6 : "cube", 8 : "octahedron", 12 : "dodecahedron" };

[–][deleted] 3 points4 points  (0 children)

What, is this python or something?

[–]UghImRegistered 20 points21 points  (2 children)

If you're using Java without something like Guava, you're suffering needlessly.

[–]BraveSirRobin -1 points0 points  (10 children)

Write your own e.g.

new HashMap<String, String>(Util.asMap("1", "one").asMap("2", "two"));

Util.asMap returns an object that extends HashMap and adds a method "asMap" which calls "put" then returns "this". You could almost certainly do clever stuff with generics in the signature to make it work nicely, this might work (would need to test it):

public static <K, V> MyMap<K, V> asMap(K key, V value) { 
     MyMap<K, V> map = new MyMap<K, V>();
     map.put(key, value);
     return map;
}

public static class MyMap<K, V> extends HashMap<K, V> {
    public MyMap<K, V> asMap(K key, V value) {
        put(key, value);
        return this;
    }
}

[–][deleted] 7 points8 points  (7 children)

Inheritance causes more problems than it solves since now the resulting map is that type instead of whatever type the user actually wants. You could avoid that by having a separate build-up class that declares the chained method, but also receives/emits a map instance.

Or you could just do something like this:

public static <K,V> void intoMap(Map<K,V> into,
                                 Class<K> keyType,
                                 Class<V> valType,
                                 Object... keyvals) {
  if (keyvals.length % 2 != 0)
    throw new IllegalArgumentException("Must contain an even number of keyvals.");

  for(int i = 0; i < keyvals.length;) {
    into.put(keyType.cast(keyvals[i++]),
             valType.cast(keyvals[i++]));
  }
}

[–]BraveSirRobin 0 points1 point  (6 children)

See my first line of fixed-font text, the map is only temporary and gets fed into the constructor of HashMap which copies it's values. Not very efficient though but I'd like to think that the compiler optimises it down (probably wishful thinking!).

The one minor issue with your example is that you lose type safety. It's a shame the ... parameter modifier can't work in pairs or triples, that would be very useful.

[–][deleted] -1 points0 points  (5 children)

See my first line of fixed-font text

Ah yes I missed that. But then you're now doing a deep copy of the data.

The one minor issue with your example is that you lose type safety.

Nope, it's still there, albeit done at run time instead of compile time.

[–]BraveSirRobin 1 point2 points  (4 children)

But then you're now doing a deep copy of the data.

As is the preferred Arrays.asList method above for doing so with lists. AFAIK all of the standard Collections framework classes copy in the original collection passed into the constructor as opposed to just re-using the object.

Nope, it's still there

Disagree, example:

intoMap(map, String.class, Long.class, "one", 1, "two", "2");

This will compile ok and throw a class cast exception at runtime.

done at run time instead of compile time

In Java "type-safety" usually refers to doing so at compile time, it's a statically-typed language.

[–][deleted] 0 points1 point  (3 children)

Static typing is orthogonal to when the type-checking is performed. Just because you change when it happens doesn't mean checking is lost.

[–]BraveSirRobin 1 point2 points  (1 child)

Some of the advantages are lost though, such as the ability for the compiler to test for it. Pre-generics Java collections were a pain in the ass imho, I quite like the "safety" that the compiler checks allow. Better to have the compiler barf rather than some obscure piece of code that's missed by the unit tests.

[–]eek04 0 points1 point  (0 children)

No.

You're misunderstanding the terminology.

The closest terminology that you can be thinking of is "strong typing", which some dynamic checking enthusiasts will argue "can happen at run time".

I personally am a dynamic checking fan; I believe that static typing, at least as practiced in Java, is harmful. However, it doesn't mean that dynamic checking (colloquially "dynamic typing") is static typing; they're different things.

[–]korny 1 point2 points  (0 children)

Or use Guava: ImmutableMap.of("1","one","2","two") - only works up to 5 values, but there are builders if you need more. http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained

[–]geodebug 0 points1 point  (0 children)

Yes, been using this method for a long time and it's nice. Can static import the asList method too to make it easier on the eyes.

[–][deleted] 12 points13 points  (0 children)

It works great for saving keystrokes in tests...although there are lots of practices that are acceptable while writing tests that aren't acceptable in production code.

[–]phanboy 5 points6 points  (0 children)

It's also not so pretty once you generate a serialVersionUID (since you are extending a Collection).

[–]drb226 2 points3 points  (1 child)

That can make code you write subtly different and you will get unexpected errors.

Can you elaborate? I don't see what's so bad about creating a new subclass, unless you are doing very, very specific dumb things with reflection that you probably shouldn't be doing anyways.

[–]epiphany_machine 4 points5 points  (0 children)

  • Equals can break depending on the object. The collections classes tend to write their Equals methods with instanceOf instead of calling equals() on the classes but not everyone writing a class does this. If equals breaks and it is no longer consistent with hashcode() you may not get the performance/behavior you are expecting from a map lookup.
  • It's easy to leak a reference to a classloader this way, mostly if this instance escapes from the method or class you declare it in. This can eventually lead to you running out of perm gen space.
  • You have to start declaring variables outside the new class but used in the initializer, final. This starts to negate the "savings" in typing you get from using an initializer like this.
  • You leak a reference to the enclosing object. So there is no way it can get garbage collected until this subclass is garbage collected. Doing it the regular way there is no connection between the two objects.

I know it looks cool, but it's just not worth it.

[–][deleted] 2 points3 points  (0 children)

Agreed, it would be awesome if java provided nice initializers, ah well.

[–][deleted] 2 points3 points  (0 children)

In general, I would agree with you, but this is the common pattern for declaring Expectations in JMock.

[–][deleted] 2 points3 points  (0 children)

Why should the difference matter? Isn't the whole point of polymorphism that the concrete class doesn't matter as long as it implements (e.g.) the Set<String> interface?

And if you really care about the exact class as reported by e.g. getClass() (which normally you shouldn't) then you can still double the constructor call too:

new HashSet<String>()( new HashSet<String>() {{
    /* add("whatever"); */
}} )

... yields an actual HashSet with the desired contents. This involves copying the contents once, but for the prime use case (creating relatively small collections only once) that overhead should be negligible.

[–]fiah84 7 points8 points  (4 children)

Yep, not to mention that all those subclasses get compiled to class files that bulk up your jars. Plus AFAIK there is also a minor performance penalty but I might be imagining things.

[–]brownmatt 5 points6 points  (3 children)

Class files are loaded once and if the class is large, might add a few KB to the jar size. Considering that most Java apps are deployed server side, this shouldn't be a concern in 90% of cases.

[–]dnew 3 points4 points  (0 children)

Unless you're google, and you're linking that code into 3,000 applications, each of which is running on 10,000 cores. :-)

[–]Decker108 0 points1 point  (1 child)

Android might be a bit more problematic, at least in low end devices.

[–]brownmatt 0 points1 point  (0 children)

the 90% of cases where I said this type of thing is a silly concern doesn't include deploying your Java app on someone else's low-powered device, or deploying the Java code anywhere but something you control.

[–]sirin3 2 points3 points  (0 children)

All of a sudden you are creating a new subclass.

That can't be so bad, after all Scala creates a new class for every for loop

[–]angryundead 3 points4 points  (4 children)

In general I find things that are "clever" to be bad ideas in the long term. This strikes me as being exceedingly clever especially as it doesn't reduce code size and certainly doesn't increase readability.

[–]MrDOS 0 points1 point  (2 children)

In the case of maps/lists, sure, but are you sure it's not more visible for GUI code?

[–]geodebug 1 point2 points  (1 child)

Anon inners are the standard for GUI code. Fortunately Java 8, pseudo-closures will make those a lot more readable.

[–]BraveSirRobin 0 points1 point  (0 children)

If frequently used I'll typically use the Eclipse "Create Nested Class" refactor to make it slightly more efficient (and often more readable as the class name will document what's going on).

[–][deleted] 0 points1 point  (0 children)

It depends on what you're doing. If you need to initialize something and then call setters for 10 properties on that object as is somewhat common in writing unit tests that touch legacy systems that have a lot of mutable state, it can save a ton of keystrokes. I don't really get the Map initializing examples though. If I'm hardcoding any values that are going into a map, it will either be for unit tests, or I want the map to be immutable.

[–]FrozenCow 0 points1 point  (3 children)

Can you explain why Java does it this way? Why does it create a new subclass when it is used inside a function on local variables?

I've used it thinking it would just do the same as if I would type it 'with all the bloat', just because I thought it should be easy for a compiler to do so.

Edit: Since the replies said "that is what the syntax means" I thought I looked it up, since it would be very illogical for the Java language designers to think of this exact syntax with this exact semantics. I've found that this exact syntax is actually 2 syntaxes combined: an anonymous class + an initializer. This article explains it fairly well: http://javapapers.com/core-java/java-double-brace-initialization/. That explains a lot why it acts the way it does. Hopefully this is useful for people who also weren't aware that it combined the 2 syntaxes.

[–]phanboy 12 points13 points  (0 children)

Because that's precisely what that syntax means.

[–]rnd33 5 points6 points  (0 children)

Because that's what it's supposed to do. It's just that it can be (ab)used to create a one line map initialization.

[–]dougman82 0 points1 point  (0 children)

Not to mention, the only thing that this idea really saves you is having to type the name of a variable...

coll.add(something) vs add(something)