Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Ganesh_JavaSE7_Programming_1z0-804_study_guide.pdf
Скачиваний:
94
Добавлен:
02.02.2015
Размер:
5.88 Mб
Скачать

Chapter 6 GeneriCs and ColleCtions

class TestPair {

public static void main(String []args) {

Pair<Integer, String> worldCup = new Pair<>(2010, "South Africa"); System.out.println("World cup " + worldCup.getFirst() +

" in " + worldCup.getSecond());

}

}

This program will compile cleanly and print the following statement:

World cup 2010 in South Africa

Note that it is a common mistake to forget the diamond operator < > in the initialization expression, as in

Pair<Integer, String> worldCup = new Pair (2010, "South Africa");

Figure 6-2 shows the compiler warning when you forget to use the diamond syntax. Since Pair is a generic type and you forgot to use the < > or provide the type parameters explicitly, the compiler treats it as a raw type with Pair taking two Object type parameters. Though this behavior did not cause any problem in this particular code segment, it is dangerous and can cause bugs, as the next section shows.

Figure 6-2. Compiler warning when you forget to use diamond syntax

Interoperability of Raw Types and Generic Types

A generic type can be used without specifying its associated type; in that case, the type is referred to as raw type. For instance, List<T> should be used along with an associated type (i.e., List<String>); however, it can be used without specifying the accompanied type (i.e., List). In the latter case, the List is referred to as raw type.

When you use a raw type, you lose the advantage of type safety afforded by generics. For instance, the type Vector in Listing 6-1 is a raw type. At the time of compilation, the compiler generates a warning, as shown in Figure 6-1.

Raw types bypass the type checking at compile time; however, they might throw runtime exceptions (for instance, ClassCastException). Therefore, it is not recommended to use raw types in new code.

158

Chapter 6 Generics and Collections

Okay, now you understand that you should not use raw types. But, you may ask, why does the compiler itself deny such type declarations? The answer is backward compatibility. Java generics were introduced in Java SDK 5. Java supports raw types in order to make the generics-based code compatible with legacy code. However, it is strongly recommended that you should not use raw types going forward.

Why? What will happen if you use raw types along with generics? Let’s use both types in Listing 6-9 and examine the effect.

Listing 6-9.  RawTest.java

//This program demonstrates usage of raw types along with generics class RawTest{

public static void main(String []args) { List list = new LinkedList(); list.add("First"); list.add("Second");

List<String> strList = list; //#1

for(Iterator<String> itemItr = strList.iterator(); itemItr.hasNext();) System.out.println("Item : " + itemItr.next());

List<String> strList2 = new LinkedList<>(); strList2.add("First"); strList2.add("Second");

List list2 = strList2; //#2

for(Iterator<String> itemItr = list2.iterator(); itemItr.hasNext();) System.out.println("Item : " + itemItr.next());

}

}

What you expect from the above program? Do you think it will compile/execute properly? Well, yes—it will compile (with warnings) and execute without any problem. It prints the following:

Item : First

Item : Second

Item : First

Item : Second

Listing 6-10 introduces a couple of changes; observe the output.

Listing 6-10.  RawTest2.java

// This program demonstrates usage of raw types along with generics class RawTest{

public static void main(String []args) { List list = new LinkedList(); list.add("First"); list.add("Second"); List<String> strList = list;

strList.add(10); //#1: generates compiler error for(Iterator<String> itemItr = strList.iterator(); itemItr.hasNext();) System.out.println("Item : " + itemItr.next());

159

Chapter 6 Generics and Collections

List<String> strList2 = new LinkedList<>(); strList2.add("First"); strList2.add("Second");

List list2 = strList2;

list2.add(10); //#2: compiles fine, results in runtime exception for(Iterator<String> itemItr = list2.iterator(); itemItr.hasNext();) System.out.println("Item : " + itemItr.next());

}

}

In the above example, you added two statements. The first statement is as follows:

strList.add(10); //#1: generates compiler error

You are trying to add an integer item in a List<String> type list, so you get a compile-time error. As discussed earlier, this type checking at the compiler level is good, as without it in a runtime exception might have resulted later on. Here is the second statement you added:

list2.add(10); //#2: compiles fine, results in runtime exception

Here, the list2 linked-list (raw type) is initialized with a generic type List<String>. After the initialization, you added an integer in the list raw type. This is allowed since list2 is a raw type. However, it will result in a

ClassCastException.

What can you learn from these two examples? The lesson is to avoid mixing raw types and generic types in your programs, since it might result in erroneous behavior at runtime. If you need to use both in a program, make sure you add a single type of items in the containers and retrieve using the same type.

  Avoid mixing raw types with generic types.

Generic Methods

Similarly to generic classes, you can create generic methods—that is, methods that take generic parameter types. Generic methods are useful for writing methods that are applicable to a wide range of types while the functionality remains the same. For example, there are numerous generic methods in the java.util.Collections class.

Let’s implement a simple method named fill(). Given a container, the fill() method fills all the container elements with value val. Listing 6-11 contains the implementation of the fill() method in the Utilities class.

Listing 6-11.  Utilities.java

// This program demonstrates generic methods class Utilities {

public static <T> void fill(List<T> list, T val) { for(int i = 0; i < list.size(); i++) list.set(i, val);

}

}

160

Chapter 6 Generics and Collections

class UtilitiesTest {

public static void main(String []args) {

List<Integer> intList = new ArrayList<Integer>(); intList.add(10);

intList.add(20);

System.out.println("The original list is: " + intList); Utilities.fill(intList, 100);

System.out.println("The list after calling Utilities.fill() is: " + intList);

}

}

It prints the following:

The original list is: [10, 20]

The list after calling Utilities.fill() is: [100, 100]

Let’s look step-by-step at this code:

1.You create a method named fill() in the Utilities class with this declaration:

public static <T> void fill(List<T> list, T val)

You declare the generic type parameter T in this method. After the qualifiers public and static, you put <T> and then followed it by return type, method name, and its parameters. This declaration is different from generic classes—you give the generic type parameters after the class name in generic classes.

2.In the body, you write the code as if it’s a normal method.

for(int i = 0; i < list.size(); i++) list.set(i, val);

You loop over the list from 0 until it’s sized and set each of the elements to value val in each iteration. You use the set() method in List, which takes the index position in the container as the first argument and the actual value to be set as the second argument.

3.In the main() method in the UtilitiesTest class, this is how you call the fill() method:

Utilities.fill(intList, 100);

Note that you didn’t give the generic type parameter value explicitly. Since intList is of type Integer and 100 is boxed to type Integer, the compiler inferred that the type T in the fill() method is of type Integer.

It is a common mistake to import List from java.awt instead of java.util. Your program may not compile (or it may produce warnings) if you are not using the correct import. Remember, List from java.util is a generic type while List from java.awt is not.

161

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]