Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wildcards vs. generic methods

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
like image 957
fredoverflow Avatar asked May 02 '10 09:05

fredoverflow


2 Answers

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){
    ...
}
like image 106
Christian Semrau Avatar answered Sep 21 '22 05:09

Christian Semrau


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<?>.

like image 43
cletus Avatar answered Sep 21 '22 05:09

cletus