views:

97

answers:

3

Is there any practical difference between the following approaches to print all elements in a range?

public static void printA(Iterable<?> range)
{
    for (Object o : range)
    {
        System.out.println(o);
    }
}

public static <T> void printB(Iterable<T> range)
{
    for (T x : range)
    {
        System.out.println(x);
    }
}

Apparently, printB involves an additional checked cast to Object (see line 16), which seems rather stupid to me -- isn't everything an Object anyway?

public static void printA(java.lang.Iterable);
  Code:
   0:   aload_0
   1:   invokeinterface #18,  1; //InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
   6:   astore_2
   7:   goto    24
   10:  aload_2
   11:  invokeinterface #24,  1; //InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
   16:  astore_1
   17:  getstatic   #30; //Field java/lang/System.out:Ljava/io/PrintStream;
   20:  aload_1
   21:  invokevirtual   #36; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
   24:  aload_2
   25:  invokeinterface #42,  1; //InterfaceMethod java/util/Iterator.hasNext:()Z
   30:  ifne    10
   33:  return

public static void printB(java.lang.Iterable);
  Code:
   0:   aload_0
   1:   invokeinterface #18,  1; //InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
   6:   astore_2
   7:   goto    27
   10:  aload_2
   11:  invokeinterface #24,  1; //InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
   16:  checkcast   #3; //class java/lang/Object
   19:  astore_1
   20:  getstatic   #30; //Field java/lang/System.out:Ljava/io/PrintStream;
   23:  aload_1
   24:  invokevirtual   #36; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
   27:  aload_2
   28:  invokeinterface #42,  1; //InterfaceMethod java/util/Iterator.hasNext:()Z
   33:  ifne    10
   36:  return
A: 

The second is more flexible. A better example is: It's saying something about the type. Whether or not that's useful to you depends on what the function does.

The second shows its usefulness when you want to return something from the method:

public static <T> List<T> reverse(List<T> list) {
  for (int i=0; i<n/2; i++) {
    T value = list.get(i);
    list.set(i, list.get(list.size() - i - 1));
    list.set(list.size() - i = 1, value);
  }
  return list;
}

It's possibly a better example to copy the array but the point remains that you can't really do the above with List<?>.

cletus
I don't get this. As far as I can tell, the two forms can be used in exactly the same contexts and one doesn't give one iota more or less type safety than the other. Am I missing something?
Marcelo Cantos
@Marcelo If you use the ? version the calling method would get back a List, and need to cast that List to a List<Whatever> itself. Which is a bit insecure as the compiler can't check that the implementation actually produces a List<Whatever>. The contract (with ?) states that a List shall be returned and that's true, even if it's not a List<T>. The T version thus would allow the compiler to check the return type and the calling method would be safer.
extraneon
Yeah but whenever the T just appears in one place in the argument list (as with the OP's example), it can be converted into a wildcard. This example is different (the T appears in two places, including in the return).
newacct
@extraneon: Both of the OP's methods return void. @cletus seems to be discussing the distinction in a more general context.
Marcelo Cantos
A: 

Not really. The resulting bytecode should be virtually identical.

Marcelo Cantos
It isn't, see my edit.
FredOverflow
Ah, yes. So the `<T>` version actually checks that the contents are of the correct type and can therefore fail if an `Iterable<T>` contains something that isn't a `T`. I've softened my answer a little.
Marcelo Cantos
@Marcello: No it doesn check if an Iterable<T> contains something that isn't a T. It just checks if it contains something that can't be cast to Object. But generics is Java are so stupidly implemented that I'd assume this is just another leftover...
Foxfire
Thank you Foxfire. Java's generics never cease to amaze me.
Marcelo Cantos
+3  A: 

In your example, the generic type is used in exactly one spot of the signature. In this scenario, a type T has no advantage over a wildcard for the caller. In your example, the type also has no advantage for the implementor of the method.

I find the wildcard version easier to understand for a caller, since it explicitly says "I don't care about the type at all".

In your example, the checkcast is indeed superfluous. It would be required if T was bounded, like in T extends Number. Then a checkcast for Number is required, because the local variable x will be of type Number, but the Iterator.next() method still returns Object. Seems like the Java compiler does not bother optimizing away the cast. The JIT probably will do so at runtime.

UPDATE:

If the generic type is used in several spots, like in cletus's answer, you have no option but to use a generic type T, or else the compiler sees no connection between the parameter type/return type (any two wildcards are distinct for the compiler).

A borderline case is when the signature only has the type in one spot, but the implementation needs it to be a generic type rather than a wildcard. Think of a void swap(List<T> list, int a, int b) method. It needs to take elements out of the list and put them back in. IIRC, Effective Java suggests using a public method with the wildcard, and an internal helper method with a type containing the actual implementation. This way, the user gets a simple API, and the implementer still has type safety.

public void swap(List<?> list, int a, int b){
    swapHelper(list, a, b);
}
private <T> void swapHelper(List<T> list, int a, int b){
    ...
}
Christian Semrau
I agree: *in this case* the wildcard version is better.
Jorn