Sometimes you'll write a method where you don't care about the type to which a parameter is bound. Suppose you need a utility method that creates a single string by concatenating elements in a list, separating the printable representation of each element with a new line. The StringUtilTest method testConcatenateList demonstrates this need: package sis.util; import junit.framework.*; import java.util.*; public class StringUtilTest extends TestCase { ... public void testConcatenateList() { List<String> list = new ArrayList<String>(); list.add("a"); list.add("b"); String output = StringUtil.concatenate(list); assertEquals(String.format("a%nb%n"), output); } } In the StringUtil method concatenate, you will append each list element's string representation to a StringBuilder. You can get the string representation of any object, provided by the toString method, without knowing or caring about its type. Thus, you want to be able to pass a List that is bound to any type as the argument to concatenate. You might think that you can bind the list parameter to Object: // this won't work public static String concatenate(List<Object> list) { StringBuilder builder = new StringBuilder(); for (Object element: list) builder.append(String.format("%s%n", element)); return builder.toString(); } By binding list to Object, you constrain it to hold objects only of type Objectand not of any Object subclasses. You cannot assign a List<String> reference to a List<Object> reference. If you could, client code could add an Object to list using the List<Object> reference. Code that then attempted to extract from list using the List<String> reference would unexpectedly retrieve an Object. Instead, Java allows you to use a wildcard character (?) to represent any possible type: package sis.util; import java.util.*; public class StringUtil { ... public static String concatenate(List<?> list) { StringBuilder builder = new StringBuilder(); for (Object element: list) builder.append(String.format("%s%n", element)); return builder.toString(); } } Within the concatenate method body, you cannot use the ? directly as a naked type variable. But since list can contain any type of object, you can assign each of its elements to an Object reference in the for-each loop. Additionally, you can constrain a wildcard to an upper bound using the extends clause.
public void testConcatenateFormattedDecimals() { List<BigDecimal> list = new ArrayList<BigDecimal>(); list.add(new BigDecimal("3.1416")); list.add(new BigDecimal("-1.4142")); String output = StringUtil.concatenateNumbers(list, 3); assertEquals(String.format("3.142%n-1.414%n"), output); } public void testConcatenateFormattedIntegers() { List<Integer> list = new ArrayList<Integer>(); list.add(12); list.add(17); String output = StringUtil.concatenateNumbers(list, 0); assertEquals(String.format("12%n17%n"), output); } The implementation in StringUtil: public static String concatenateNumbers( List<? extends Number> list, int decimalPlaces) { String decimalFormat = "%." + decimalPlaces + "f"; StringBuilder builder = new StringBuilder(); for (Number element: list) { double value = element.doubleValue(); builder.append(String.format(decimalFormat + "%n", value)); } return builder.toString(); } You'll need to import java.math.* to get the above code to compile. The declaration of the list parameter in concatenateNumbers specifies that it is a List bound to either Number or a subclass of Number. The code in concatenateNumbers can then assign each element in list to a Number reference. |