Generic me this

So I was programming along the other day, tap tap tap enjoying my life as a java programmer. Recently I had attended JAOO in Sydney, and had the pleasure of watching Joshua Bloch of Effective Java fame do his thing. He was chatting about generics, and how as Java people we could possibly get our heads around understanding what was going on.

To assist our little brains, he presented a concept with an acronym he calls PECS.

First, Producer Extends.
If you put things into the object and never retrieve them, the object is a producer. Awesome. Producer extends:

ArrayList<? extends Object> list = new ArrayList<String>();

Secondly, Consumer Super:
If you only retrieve things from the object, the object is a consumer. Consumer super:

ArrayList<? super String> list = new ArrayList<Object>();

So you can imagine why I was so happy programming along in my little lunch box. I was eating this generic candy and it tasted sour, but sour in a way that is cool after you get over the part where you close your eyes and screw up your face. Then this:

ArrayList<ArrayList<? extends Object> list = new ArrayList<ArrayList<String>>();

This is the same example as before, but surrounded in another list. Intuitively I expect this to work, however it doesn’t compile due to “incompatible types”.

Seriously, what the? I don’t understand how this makes any sense. How are we supposed to understand the code that we write if we cant even understand the language? How can we be expected to teach children to learn how to read if they can’t even fit inside the building?

PECS is a useful concept. Unfortunately, Java generics at the moment are limited in their usefulness. They turn simple code into code that is clever and complicated. This slows us down.

Personally, I cant wait till we are all using groovy:

def list = []

13 Responses to “Generic me this”

  1. Mark Says:

    At first I thought it was your syntax error that might be causing the problem, but even with that fixed it didn’t compile.

    Then I dug a little deeper, it seems that this variation on a theme will compile:

    ArrayList<? extends ArrayList> list = new ArrayList<ArrayList>();

    I’m going to have to follow some of your links and find out if this is a logical extension of PECS or not.

  2. Mark Says:

    I meant something different, does this work:

    ArrayList<? extends ArrayList<? extends Object>> list = new ArrayList<ArrayList<String>>();

  3. Luis Says:

    I was interested in Groovy some time ago, but I was disappointed at the speed. Back then, it seemed to me that the holly grail was to make groovy only *half* the speed of plain java. I seemed to remember that back then Groovy was 10 to 40 times slower than java.

    I left Groovy in the attic of my mind, hoping that one day it would be a language usable in real life. Has it reached that point yet?

  4. Cam Swords Says:

    Cheers, Mark, that certainly makes that part compile. However I wasnt able to add or retrieve anything from the list. For example, the following dont compile:

    list.add(new ArrayList<String>());

    and

    ArrayList<Object> item = list.get(0);

    Were you able to get this working? I cant even work out what to put in that list you gave me to make it compile! Wow that is strange.

  5. Cam Swords Says:

    Luis, dont give up on Groovy yet!

    Ive experienced the same performance woes with Groovy. Im hoping that the Java 7 release will include the invokeDynamic fix, which as i understand will essentially cache method calls made using the Java reflection API which Groovy depends heavily on.

    Fingers crossed it makes a big difference!

  6. Mark Says:

    Hey Cam,
    I did not notice you could no longer add stuff to it, that is really weird….
    Luckily this is following the producer pattern, so we should only be getting not adding… right?

  7. Mark Says:

    just checked it out, if you use the extends wild card you can only retrieve and not add. Check out page 17 onwards here:

    http://books.google.com/books?id=3keGb40PWJsC&pg=PA15&lpg=PA15&dq=wildcards+in+generic+java+substitions&source=bl&ots=VAWmuJVpp6&sig=w8lgI5cjCNAbi-k89yTJv2BGKLE&hl=en&ei=9a1ASo6WEoTSjAfQ5ammCQ&sa=X&oi=book_result&ct=result&resnum=1

    I haven’t managed to work out how to do a consumer pattern with nested lists yet though ;-(

  8. Cam Swords Says:

    Me neither! Cheers for the link, interesting read.

  9. Dave Says:

    Hey Cam,

    From my (albeit post-technical) understanding of the PECS mnemonic, you use wild cards in method signature parameters to effectively indicate the type-based contract with a caller of a method.
    It’s like a generified version of the Liskov Substitution Principle (expect no more, promise no less).
    The other guideline from Effective Java is not to use wildcards in return types. I think that your example of assigning a wildcard-ed local variable to an instance of a concrete type falls in this category too.

    In the following:
    ArrayList<ArrayList list =
    new ArrayList<ArrayList>();

    list would, for example, happily accept
    list.add(new ArrayList());

    so it is no suprise that assignment to a specific type of ArrayList<ArrayList> should fail.

    Cheers
    Dave

  10. Dave Says:

    should have said

    in the followng:
    ArrayList<ArrayList list = new ArrayList<ArrayList>();

    shows how often I post code. Can’t even cut and paste right these days.

  11. Dave Says:

    Oh fuckit. I’m not going to start escaping less thans…
    Hope it made sense

  12. Prasanna Says:

    Cam,

    The code that you wrote (with the syntax error fixed):

    ArrayList<ArrayList> list = new ArrayList<ArrayList>();

    is guaranteed not to compile. The reason is very simple and explained beautifully in Section 3 of Gilad Bracha’s tutorial on Generics (available at: http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf).

    Consider the following (I am merely reusing the example in the article I refer to):

    ArrayList<ArrayList> lls = new ArrayList<ArrayList>();
    ArrayList<ArrayList> llo;

    lls.add(new ArrayList()); // So far so good.
    llo = lls; // Will not compile. Meant for illustration only.
    llo.add(new ArrayList());

    ArrayList ls = lls.get(1); // Violation of type safety.

    Now you see the problem. We are attempting to assign an ArrayList of Objects to an ArrayList of Strings.

    Hope this helps,
    Prasanna

  13. Prasanna Says:

    Huh? My Java code got mangled! Let me try again.

    The code that you wrote (with the syntax error fixed):

    ArrayList<ArrayList<? extends Object>> list = new ArrayList<ArrayList<String>>();

    is guaranteed not to compile. The reason is very simple and explained beautifully in Section 3 of Gilad Bracha’s tutorial on Generics (available at: http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf).

    Consider the following (I am merely reusing the example in the article I refer to):

    ArrayList<ArrayList<String>> lls = new ArrayList<ArrayList<String>>();
    ArrayList<ArrayList<? extends Object>> llo;

    lls.add(new ArrayList<String>()); // So far so good.
    llo = lls; // Will not compile. Meant for illustration only.
    llo.add(new ArrayList<Object>());

    ArrayList ls = lls.get(1); // Violation of type safety.

    Now you see the problem. We are attempting to assign an ArrayList of Objects to an ArrayList of Strings.

    Hope this helps,
    Prasanna

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s


Follow

Get every new post delivered to your Inbox.

%d bloggers like this: