tags:

views:

881

answers:

2

When initialising an instance of a Generic class in Java is there any benefit to specifying the Type on both sides of the statement?

Or to put it another way, what's the difference between these two valid statements:

ArrayList<String> test = new ArrayList<String>();

and:

ArrayList<String> test = new ArrayList();

(It seems second statement is not equivalent to:

ArrayList<String> test = new ArrayList<Object>();

as the third statement in invalid and causes an incompatible types compile error.)

+14  A: 

The second statement winds up being more or less equivalent to the first, but only because generics are erased at runtime. You'll get an "unchecked conversion" warning, which is why I don't like it.

A better way is to have a static generic method like this:

public static <T> List<T> newList() {
    return new ArrayList<T>();
}

and then do

List<String> test = newList();

This is what Google Collections does.

(And you should almost always be declaring your lists as List, not as ArrayList. Makes it easy to switch the implementation later.)

Edit: dribeas asked in the comments what the exact difference is between the two declarations, and why I said they are "more or less equivalent". Because of type erasure, the only difference between them is the warning. Here's a small piece of code comparing them:

import java.util.*;

class GenericDeclarationTest {
    public static void main(String[] args) {
        List<String> list1 = new ArrayList<String>();
        list1.add("");
        String s1 = list1.get(0);
        List<String> list2 = new ArrayList();
        list2.add("");
        String s2 = list2.get(0);
    }
}

And here's the generated bytecode (as printed by javap -c GenericDeclarationTest):

Compiled from "GenericDeclarationTest.java"
class GenericDeclarationTest extends java.lang.Object{
GenericDeclarationTest();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   new     #2; //class java/util/ArrayList
   3:   dup
   4:   invokespecial   #3; //Method java/util/ArrayList."<init>":()V
   7:   astore_1
   8:   aload_1
   9:   ldc     #4; //String
   11:  invokeinterface #5,  2; //InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
   16:  pop
   17:  aload_1
   18:  iconst_0
   19:  invokeinterface #6,  2; //InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
   24:  checkcast       #7; //class java/lang/String
   27:  astore_2
   28:  new     #2; //class java/util/ArrayList
   31:  dup
   32:  invokespecial   #3; //Method java/util/ArrayList."<init>":()V
   35:  astore_3
   36:  aload_3
   37:  ldc     #4; //String
   39:  invokeinterface #5,  2; //InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
   44:  pop
   45:  aload_3
   46:  iconst_0
   47:  invokeinterface #6,  2; //InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
   52:  checkcast       #7; //class java/lang/String
   55:  astore  4
   57:  return

}

As you can see (if you have the patience), the two are identical.

Incidentally, this may become easier in Java 7. There is a proposal in Project Coin for "Improved Type Inference for Generic Instance Creation". If it makes the final cut, the syntax will be:

List<String> test = new ArrayList<>();
// or
Map<String, Object> test2 = new HashMap<>();

Not too hard to type, is it?

Michael Myers
Was the <T> after "static" in your code intentional ? What is it's purpose ?
euphoria83
@euphoria83: The <T> is to let the compiler know it's a generic method. See page 7 and following of http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf.
Michael Myers
What is the difference from: List<String> x = new ArrayList(); and your intermediate function? And when you say that the first and second example are more or less equivalent, what are the differences?
David Rodríguez - dribeas
It's pretty much the same thing once the code is compiled, but you should use the first method anyway because otherwise javac will give you warnings. The intermediate function will detect that you are looking for a List<String> and return one. That way you only have to specify <String> once.
Adam Jaskiewicz
And the main benefit of the improved type inference isn't so much less typing, it's not typing the same thing twice (and thus less of a chance to make a mistake).
Adam Jaskiewicz
Totally awesome answer, thanks. They key difference for me is the compiler warning. The problem is we've only just moved from Java 1.4 to 1.6 so we see the "Note: Some input files use unchecked or unsafe operations." warning all the time due to our pre-Generics code so I usually just ingnore it.
Dave Webb
+2  A: 

That redundancy is annoying. You may wish to look at Google Collections that have factory methods which create Lists like this:

List<Double> doubleList = Lists.newLinkedList();

Also note that Java Generics are not covariant.

Julien Chastang
Google Collections looks very handy, thanks.
Dave Webb