Effective Java Third edition--29. Prioritize generics

Source: Internet
Author: User

Tips
"Effective Java, third Edition" an English version has been published, the second edition of this book presumably many people have read, known as one of the four major Java books, but the second edition of 2009 published, to now nearly 8 years, but with Java 6, 7, 8, and even 9 of the release, the Java language has undergone profound changes.
In the first time here translated into Chinese version. For everyone to learn to share.

29. Prioritize generics

Parameterized declarations and use of generic types and methods provided by the JDK are usually not too difficult. But it's hard to write your own generic type, but it's worth studying hard.

Consider the simple stack implementation in entry 7:

 //object-based collection-a Prime candidate for Genericspublic class Stack {private object[] elements;    private int size = 0;    private static final int default_initial_capacity = 16;    Public Stack () {elements = new object[default_initial_capacity];        } public void push (Object e) {ensurecapacity ();    elements[size++] = e;        Public Object Pop () {if (size = = 0) throw new emptystackexception ();        Object result = Elements[--size]; Elements[size] = null;    Eliminate obsolete reference return result;    } public boolean IsEmpty () {return size = = 0;  private void Ensurecapacity () {if (elements.length = = size) elements = arrays.copyof (elements, 2 *    Size + 1); }}

This class should already be parameterized, but because it is not, we can generics it. In other words, we can parameterize it without compromising the original non-parameterized version of the client. For the moment, the client must cast the object that was popped from the stack, and these casts may fail at run time. The first step in a generic class is to add one or more type parameters to its declaration. In this case, there is a type parameter that represents the element type of the stack, and the general name of this type parameter is E (entry 68).

The next step is to replace all used object types with the corresponding type parameters, and then try to compile the generated program:

// Initial attempt to generify Stack - won't compile!public class Stack<E> {    private E[] elements;    private int size = 0;    private static final int DEFAULT_INITIAL_CAPACITY = 16;    public Stack() {        elements = new E[DEFAULT_INITIAL_CAPACITY];    }    public void push(E e) {        ensureCapacity();        elements[size++] = e;    }    public E pop() {        if (size == 0)            throw new EmptyStackException();        E result = elements[--size];        elements[size] = null; // Eliminate obsolete reference        return result;    }    ... // no changes in isEmpty or ensureCapacity}

You usually get at least one error or warning, and this class is no exception. Fortunately, this class produces only one error:

Stack.java:8: generic array creation        elements = new E[DEFAULT_INITIAL_CAPACITY];                   ^

As described in entry 28, you cannot create an array of non-materialized types, such as types E . This problem occurs whenever you write a generic that is supported by an array. There are two reasonable ways to solve it. The first solution directly avoids disabling the creation of generic arrays: Create an Object array and convert it to a generic array type. Now that there are no errors, the compiler will issue a warning. This usage is legal, but not (generally) type-safe:

Stack.java:8: warning: [unchecked] unchecked castfound: Object[], required: E[]        elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];                       ^

The compiler may not be able to prove that your program is type-safe, but you can. You have to convince yourself that unrestricted type coercion does not compromise the type safety of the program. The problematic array (element) is stored in a private property and is never returned to the client or passed to any other method. The only elements that are stored in the array are those that are passed to push the method, and they are E of the type, so an unchecked cast does not cause any harm.

Once you have proven that an unchecked cast is safe, narrow the scope as far as possible (entry 27). In this case, the construction method only contains an unchecked array creation, so it is appropriate to suppress the warning throughout the construction method. By adding an annotation to do this, the stack can be cleanly compiled and can be used without an explicit cast or fear of classcastexception exceptions:

// The elements array will contain only E instances from push(E).// This is sufficient to ensure type safety, but the runtime// type of the array won't be E[]; it will always be Object[]!@SuppressWarnings("unchecked")public Stack() {    elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];}

The second way to eliminate generic array creation errors in a stack is to change the type of the property element from to E [] Object [] . If you do this, you get a different error:

Stack.java:19: incompatible typesfound: Object, required: E        E result = elements[--size];                           ^

You can change this error to a warning by converting the element retrieved from the array to E :

Stack.java:19: warning: [unchecked] unchecked castfound: Object, required: E        E result = (E) elements[--size];                               ^

Because E is a non-materialized type, the compiler cannot check for casts at run time. Once again, you can easily prove to yourself that unrestricted conversions are safe, so you can suppress warnings appropriately. As suggested in entry 27, we suppress the warning only on allocations that contain unchecked casts, not on the entire pop method:

// Appropriate suppression of unchecked warningpublic E pop() {    if (size == 0)        throw new EmptyStackException();    // push requires elements to be of type E, so cast is correct    @SuppressWarnings("unchecked") E result =        (E) elements[--size];    elements[size] = null; // Eliminate obsolete reference    return result;}

Both of the techniques for eliminating generic array creation have their followers. The first one is more readable: the array is declared as a E [] type, which clearly indicates that it contains only the E instance. It's also more concise: In a typical generic class, you read an array from many points in the code; The first technique requires only one conversion (where the array is created), and the second technique requires a separate conversion for each reading of the array elements. Therefore, the first technique is preferred and is more commonly used in practice. However, it does cause heap contamination (heap pollution) (entry 32): The run-time type of the array does not match the compile-time type (unless e happens to be an object). This makes some programmers very uncomfortable, they choose the second technology, although in this case the heap pollution is harmless.

The following program demonstrates the use of a generic stack class. The program prints its command-line arguments in reverse order and converts them to uppercase. A method that calls a string on an element that pops out of the stack toUpperCase does not require an explicit cast, and an auto-generated cast will guarantee success:

// Little program to exercise our generic Stackpublic static void main(String[] args) {    Stack<String> stack = new Stack<>();    for (String arg : args)        stack.push(arg);    while (!stack.isEmpty())        System.out.println(stack.pop().toUpperCase());}

The above example seems to contradict entry 28, and entry 28 encourages the use of lists over arrays. The use of lists in generic types is not always feasible or desirable. Java itself is not natively supported by lists, so some generic types (such as ArrayList) must be implemented on arrays. Other generic types, such as HashMap, are implemented to improve performance.

The vast majority of generic types are like our stack examples, with no restrictions on their type parameters: You can create one Stack <Object> , Stack <int []> Stack <List <String >> or the stack reference type of any other object. Note that you cannot create stacks of basic types: attempts to create Stack<int> or Stack<double> will cause compile-time errors. This is a basic limitation of the Java generic type system. You can use the basic type of wrapper class (entry 61) to resolve this limitation.

There are some generic types that limit the allowable values of their type parameters. For example, consider java.util.concurrent.DelayQueue that its declaration is as follows:

class DelayQueue<E extends Delayed> implements BlockingQueue<E>

The type parameter list ( <E extends Delayed> ) requires that the actual type parameter E is a java.util.concurrent.Delayed subtype. This allows the DelayQueue implementation and its clients to take advantage of DelayQueue the methods on the element without the Delayed risk of explicit conversions or classcastexception exceptions. The type parameter e is called a qualified type parameter. Note that subtype relationships are defined as each type is its own subtype [jls,4.10], so creation DelayQueue <Delayed> is legal.

In summary, a generic type is more secure and easier to use than a type that needs to be cast in client code. When you design new types, make sure they can be used without such casts. This usually means making a type generic. If you have any existing types that should be generic but actually not, then make them generic. This makes it easier to use these types of new users without breaking existing clients (entry 26).

Effective Java Third edition--29. Prioritize generics

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.