Section 1.4. Generic Methods and Varargs


1.4. Generic Methods and Varargs

Here is a method that accepts an array of any type and converts it to a list:

 class Lists {   public static <T> List<T> toList(T[] arr) {     List<T> list = new ArrayList<T>();     for (T elt : arr) list.add(elt);     return list;   } } 

The static method toList accepts an array of type T[] and returns a list of type List<T>, and does so for any type T. This is indicated by writing <T> at the beginning of the method signature, which declares T as a new type variable. A method which declares a type variable in this way is called a generic method. The scope of the type variable T is local to the method itself; it may appear in the method signature and the method body, but not outside the method.

The method may be invoked as follows:

 List<Integer> ints = Lists.toList(new Integer[] { 1, 2, 3 }); List<String> words = Lists.toList(new String[] { "hello", "world" }); 

In the first line, boxing converts 1, 2, 3 from int to Integer.

Packing the arguments into an array is cumbersome. The vararg feature permits a special, more convenient syntax for the case in which the last argument of a method is an array. To use this feature, we replace T[] with T... in the method declaration:

 class Lists {   public static <T> List<T> toList(T... arr) {     List<T> list = new ArrayList<T>();     for (T elt : arr) list.add(elt);     return list;   } } 

Now the method may be invoked as follows:

 List<Integer> ints = Lists.toList(1, 2, 3); List<String> words = Lists.toList("hello", "world"); 

This is just shorthand for what we wrote above. At run time, the arguments are packed into an array which is passed to the method, just as previously.

Any number of arguments may precede a last vararg argument. Here is a method that accepts a list and adds all the additional arguments to the end of the list:

 public static <T> void addAll(List<T> list, T... arr) {   for (T elt : arr) list.add(elt); } 

Whenever a vararg is declared, one may either pass a list of arguments to be implicitly packed into an array, or explicitly pass the array directly. Thus, the preceding method may be invoked as follows:

 List<Integer> ints = new ArrayList<Integer>(); Lists.addAll(ints, 1, 2); Lists.addAll(ints, new Integer[] { 3, 4 }); assert ints.toString().equals("[1, 2, 3, 4]")); 

We will see later that when we attempt to create an array containing a generic type, we will always receive an unchecked warning. Since varargs always create an array, they should be used only when the argument does not have a generic type (see Section 6.8).

In the preceding examples, the type parameter to the generic method is inferred, but it may also be given explicitly, as in the following examples:

 List<Integer> ints = Lists.<Integer>toList(); List<Object> objs = Lists.<Object>toList(1, "two"); 

Explicit parameters are usually not required, but they are helpful in the examples given here. In the first example, without the type parameter there is too little information for the type inference algorithm to infer the correct type. It infers that the argument to toList is an empty array of an arbitrary generic type rather than an empty array of integers, and this triggers the unchecked warning described earlier. In the second example, without the type parameter there is too much information for the type inference algorithm to infer the correct type. You might think that Object is the only type that an integer and a string have in common, but in fact they also both implement the interfaces Serializable and Comparable. The type inference algorithm cannot choose which of these three is the correct type.

In general, the following rule of thumb suffices: in a call to a generic method, if there are one or more arguments that correspond to a type parameter and they all have the same type then the type parameter may be inferred; if there are no arguments that correspond to the type parameter or the arguments belong to different subtypes of the intended type then the type parameter must be given explicitly.

When a type parameter is passed to a generic method invocation, it appears in angle brackets to the left, just as in the method declaration. The Java grammar requires that type parameters may appear only in method invocations that use a dotted form. Even if the method toList is defined in the same class that invokes the code, we cannot shorten it as follows:

   List<Integer> ints = <Integer>toList();  // compile-time error 

This is illegal because it will confuse the parser.

Methods Arrays.asList and Collections.addAll in the Collections Framework are similar to toList and addAll shown earlier. (Both classes are in package java.util.) The Collections Framework version of asList does not return an ArrayList, but instead returns a specialized list class that is backed by a given array. Also, its version of addAll acts on general collections, not just lists.




Java Generics and Collections
Java Generics and Collections
ISBN: 0596527756
EAN: 2147483647
Year: 2006
Pages: 136

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net