August 7th, 2008

New java puzzler

Oliver, a friend of mine, decided to upgrade his java knowledge from java1.4 to java1.5. He managed to stumble into a neat java puzzler* almost immediatly.

What does the following code print, when compiled and run with java 1.5?

import java.util.Arrays;

public class Puzzler {
    public static void main(String... args) {
        test1();
        test2();
    }

    public static void test1() {
        int[] array = {1, 2, 3};
        System.out.println(
         Arrays.asList(array).size());
    }

    public static void test2() {
        Integer[] array = {1, 2, 3};
        System.out.println(
         Arrays.asList(array).size());
    }
}

Answer and explanation/discussion is in the first comment.



*) For those of you who don’t know what puzzlers are: Josh Bloch and Neal Gafter, both java bigwigs (incidentally also both working for google these days instead of sun) wrote a book called Java Puzzlers. It contains a lot of code snippets with a catch. Aside from testing your knowledge of java ideosyncracies, they also reveal design mistakes in the java language and in that way teach you what to avoid. In fact, most puzzlers come with an alternative ‘right’ way that avoids the problem or weird thing that makes the code snippet puzzling.

4 Responses to 'New java puzzler'

  1. 1rzwitserloot
    March 22nd, 2006 at 3:18

    You probably expected the output to be:
    3
    3

    but it’s actually:
    1
    3

    The reason is this: asList expects as argument T[]; that is, an array of any type. This ‘any type’ we’ll call T. It returns a List of T’s, a.k.a. List<T>. however, the exact implementation is actually of the ‘varargs’ variety, a new feature of java1.5 which allows you to supply a bunch of similarly typed arguments instead of an array, which get ‘compressed’ into an array at runtime. This varargs facility only works if the method is expressly declared to work that way. asList is one of those methods.

    The problem is this: int[] cannot be matched to T[], because ‘int’ is a primitive, and T, a generic type, can only be some sort of ‘real’ object-based type, not a primitive. Ordinarily this means the primitive gets autoboxed, but autoboxing only works for primitive types themselves, not arrays of primitive types. So, now we expect a compile-time error but this does not happen either: There’s a still a way to interpret the asList call to be legal: By assuming we’re using varargs notion to create a list of integer arrays, with exactly 1 entry (the ‘array’ entry). This is legal, because arrays of primitives are objects. Thus, the asList statement generates a List<int[]> with one item in it, namely the int[] with [1,2,3] inside. Hence the size of the list itself is 1.

    Needless to say, this isn’t very nice. The usual advice I give in these situations is to not use arrays, but this is exactly the kind of code you’d use to function as a bridge either when rewriting internal code to be less array-oriented, or when cooperating with legacy code. The only work-around I can come up with so far is to just go with a for loop.

    Note that this statement works perfectly fine and as you expect:

    List<Integer> list = Arrays.asList(1, 2, 3);


  2. 2Cristiano Betta
    March 22nd, 2006 at 15:52

    I showed this to Thomas Schaap, and he wondered if this isn’t actually a bug that should be reported?


  3. 3Alper
    March 22nd, 2006 at 21:31

    Java is teh suck!


  4. 4Mind Gone Haywire » Method got back
    February 8th, 2007 at 8:25

    […] Here’s another wacky one related to Arrays.asList(): […]


Leave a Response

(Note: if you use a new name from an unknown ip address, your comment won't appear until I approve it. Anti-spam measure only, I don't censor).

Imhotep theme designed by Chris Lin. Proudly powered by Wordpress.
XHTML | CSS | RSS | Comments RSS