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

Chapter 6 Generics and Collections

Using the Object Class vs. Generics

In the previous section, we focused our discussion on type safety issues in using existing data structures that were implemented using the Object base class. In this section, we’ll turn our focus to defining your own data structures. With an example of a container class definition using the Object base class, you’ll understand why such containers are not type-safe. After that, we’ll introduce the concept of generics and show you how using generics can help define type-safe containers.

Container Implementation Using the Object Class

Assume that you want to print the object’s value within square brackets. For example, to print an Integer object with value 10, instead of printing “10” to the console, you want to print the value inside a “box” like this: “[10]”. The default toString() method just gives the String representation of the object’s value; you want an enhanced toString() method that will print “[” and “]” before and after the value. How can you do that? You can write a BoxPrinter class that overrides the toString() method. You’ll use Object for storing the value since it is the common base class for all types (see Listing 6-2).

Listing 6-2.  BoxPrinterTest1.java

// The program demonstrates "Object" based implementation and associated lack of type safety class BoxPrinter {

private Object val;

public BoxPrinter(Object arg) { val = arg;

}

public String toString() { return "[" + val + "]";

}

}

class BoxPrinterTest1 {

public static void main(String []args) {

BoxPrinter value1 = new BoxPrinter(new Integer(10)); System.out.println(value1);

BoxPrinter value2 = new BoxPrinter("Hello world"); System.out.println(value2);

}

}

It works fine and prints the following:

[10]

[Hello world]

You can pass object of any type, and this code will print its contents within “[” and “]” correctly. This is the same mechanism in which the legacy containers (like Vector) are implemented. So, what is the problem? Well, here is a scenario: assume that you want to add a method named getValue() that gets the value stored in BoxPrinter. How do you get back the original type you put in while creating the BoxPrinter object? This is where the problem starts. Listing 6-3 contains an example.

150

Chapter 6 Generics and Collections

Listing 6-3.  BoxPrinterTest2.java

// The program demonstrates "Object" based implementation and associated lack of type safety class BoxPrinter {

private Object val;

public BoxPrinter(Object arg) { val = arg;

}

public String toString() { return "[" + val + "]";

}

public Object getValue() { return val;

}

}

class BoxPrinterTest2 {

public static void main(String []args) {

BoxPrinter value1 = new BoxPrinter(new Integer(10)); System.out.println(value1);

Integer intValue1 = (Integer) value1.getValue();

BoxPrinter value2 = new BoxPrinter("Hello world"); System.out.println(value2);

// OOPs! by mistake, we did (Integer) cast instead of (String) Integer intValue2 = (Integer) value2.getValue();

}

}

Here is the output:

[10]

[Hello world]

Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer at BoxPrinterTest2.main(Main.java:22)

In the line

Integer intValue2 = (Integer) value2.getValue();

by mistake you performed a downcast from Object to Integer instead of Object to String. There is no way for the compiler to detect the mistake that you are trying to convert the value from String to Integer. What you remember as value is of Object type, and the only way to check the correct type before doing the downcast is to check for the type using the instanceof operator. In this case, you can do an instanceof check for String and make sure that the cast is done correctly. In general, however, when you don’t know the type of object stored in BoxPrinter, you can never perform an instanceof check in the code to do a downcast! This is the reason why the old Java container classes (which used the Object class for storing elements) are not type-safe.

151

Chapter 6 Generics and Collections

Container Implementation Using Generics

Listing 6-4 contains a generic version of the BoxPrinter class you saw in the preceding section.

Listing 6-4.  BoxPrinterTest3.java

// This program shows container implementation using generics class BoxPrinter<T> {

private T val;

public BoxPrinter(T arg) { val = arg;

}

public String toString() { return "[" + val + "]";

}

}

class BoxPrinterTest3 {

public static void main(String []args) {

BoxPrinter<Integer> value1 = new BoxPrinter<Integer>(new Integer(10)); System.out.println(value1);

BoxPrinter<String> value2 = new BoxPrinter<String>("Hello world"); System.out.println(value2);

}

}

It prints the following:

[10]

[Hello world]

There are many things you need to note here.

1.See the declaration of BoxPrinter:

class BoxPrinter<T>

You gave the BoxPrinter class a type placeholder <T>—the type name T within angle brackets “<” and “>” following the class name. You can use this type name inside the class to indicate that it is a placeholder for the actual type to be provided later. (Note that you’ve given an unusually short identifier name of T to indicate the type name: this is intentional, and you’ll see the naming conventions for generic types a bit later in this chapter.)

2.Inside the class you first use T in field declaration:

private T val;

You are declaring val of the generic type—the actual type will be specified later when you use BoxPrinter. In main(), you declare a variable an Integer like this:

BoxPrinter<Integer> value1

152

Chapter 6 Generics and Collections

Here, you are specifying that T is of type Integer—identifier T (a placeholder) is replaced with the type Integer. So, the val inside BoxPrinter becomes Integer because T gets replaced with Integer.

3.Now, here is another place where you use T:

public BoxPrinter(T arg) { val = arg;

}

Similar to the declaration of val with type T, you are saying that the argument for BoxPrinter constructor is of type T. Later in the main() method, when the constructor is called in new, you specify that T is of type Integer:

new BoxPrinter<Integer>(new Integer(10));

Now, inside the BoxPrinter constructor, arg and val should be of same type since both are of type T. For example, if you change the constructor as follows:

new BoxPrinter<String>(new Integer(10));

the BoxPrinter is of type String, and the argument passed is of type Integer, so you’ll get a compiler error for type mismatch in using the generics (which is good because you’ll find the problem earlier).

Now, let’s add the getValue() method to return the value contained in the BoxPrinter class. Listing 6-5 contains the enhanced version.

Listing 6-5.  BoxPrinterTest4.java

// This program demonstrates the type-safety feature of generics class BoxPrinter<T> {

private T val;

public BoxPrinter(T arg) { val = arg;

}

public String toString() { return "[" + val + "]";

}

public T getValue() { return val;

}

}

class BoxPrinterTest4 {

public static void main(String []args) {

BoxPrinter<Integer> value1 = new BoxPrinter<Integer>(new Integer(10)); System.out.println(value1);

Integer intValue1 = value1.getValue();

BoxPrinter<String> value2 = new BoxPrinter<String>("Hello world"); System.out.println(value2);

// OOPs! by mistake, we did put String in an Integer Integer intValue2 = value2.getValue();

}

}

153

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