Effective Java Third edition--28. List is better than array

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.

Arrays differ from generics in two important ways. First, the array is covariant (covariant). This scary word means that if Sub it is a Super subtype, the array type Sub [] is a subtype of the array type Super [] . In contrast, generics are immutable (invariant): For any two different types Type1 and Type2 , List<Type1> neither List <Type2> subtype nor parent type. [jls,4.10; NAFTALIN07,2.5]. You may think that this means that generics are insufficient, but can be said to be array defects. This code is legal:

// Fails at runtime!Object[] objectArray = new Long[1];objectArray[0] = "I don't fit in"; // Throws ArrayStoreException

But this is not:

// Won't compile!List<Object> ol = new ArrayList<Long>(); // Incompatible typesol.add("I don't fit in");

Either way, you can't put a string type in a long container, but with an array you'll see an error at run time, and for the list, you can find the error at compile time. Of course, you'd rather find errors at compile time.

The second major difference between arrays and generics is that arrays are materialized (reified) [jls,4.7]. This means that arrays know and enforce their element types at run time. As mentioned earlier, if you try to put a string into a long array, you get a arraystoreexception exception. Instead, generics are implemented by erasing (erasure) to achieve [jls,4.6]. This means that they only perform type constraints at compile time and discard (or erase) their element type information at run time. Erasing is to allow free interoperability of generic types with legacy code that does not use generics (entry 26), thus ensuring a smooth transition to generics in Java 5.

Because of these fundamental differences, arrays and generics do not mix well with each other. For example, creating arrays of generic types, arrays of parameterized types, and arrays of type parameters are illegal. Therefore, these array-creation expressions are not valid: new List <E> [] , new List <String> [] new E [] . All will cause generic array creation errors at compile-time.

Why is it illegal to create a generic array? Because it is not type-safe. If this is legal, the compiler-generated casting program may fail at run time because of a classcastexception exception. This violates the basic guarantees provided by the generic type system.

For specific instructions, consider the following code snippet:

// Why generic array creation is illegal - won't compile!List<String>[] stringLists = new List<String>[1];  // (1)List<Integer> intList = List.of(42);               // (2)Object[] objects = stringLists;                    // (3)objects[0] = intList;                              // (4)String s = stringLists[0].get(0);                  // (5)

Let's assume that line 1th creates a generic array that is legal. Line 2nd creates and initializes the containing single element List<Integer> . The 3rd line List<String> stores the array in the object array variable, which is legal because the array is covariant. The 4th row List <Integer> is stored in the unique element of the object array because the generics are implemented by erasing: List<Integer> The runtime type of the instance is simply list, and List<String> [] The instance is List [] , so this assignment does not produce an arraystoreexception exception. Now we're in trouble. Stores an List<Integer> instance in an array that is declared as a save-only List<String> instance. In line 5th, we retrieve the unique element from the unique list of this array. The compiler automatically converts the retrieved element to a string, but it is an integer, so we get a classcastexception exception at run time. To prevent this from happening, line 1th (creating a generic array) must produce a compile-time error.

Types E , List<E> and List<String> so on are technically known as non-materialized types (nonreifiable types) [jls,4.7]. Intuitively, a type that is not materialized is a type whose runtime represents less information than its compile-time representation. Because of the erase, the only parameterized type that can be determined is the infinitely-fixed wildcard type, such as List <?> and Map <?, ?> (entry 26). Although rarely useful, it is legal to create an array of unqualified wildcard types.

It can be annoying to disallow the creation of generic arrays. This means, for example, that a generic collection is generally not likely to return an array of its element type (but see the partial solution in Item 33). This also means that when using the variable parameter method (entry 53) and generics, a confusing warning is generated. This is because each time a variadic method is called, an array is created to hold the mutable parameters. If the element type of this array is not deterministic, you will receive a warning. SafeVarargsannotations can be used to solve this problem (entry 32).

When you are casting to an array type, you get an error in the creation of generics arrays, or an unchecked cast warning, the best solution is usually to use the collection type instead of the List <E> array type E [] . This may sacrifice some simplicity or performance, but in exchange you get better type security and interoperability.

For example, suppose you want to write a class with a constructed method with a collection Chooser , and have a method that returns an element of a randomly selected collection. Depending on the collection passed to the construction method, you can use the selector as a game stencil, a Magic 8 ball, or a data source for Monte Carlo simulations. This is a simple implementation that does not have a generic type:

// Chooser - a class badly in need of generics!public class Chooser {    private final Object[] choiceArray;    public Chooser(Collection choices) {        choiceArray = choices.toArray();    }    public Object choose() {        Random rnd = ThreadLocalRandom.current();        return choiceArray[rnd.nextInt(choiceArray.length)];    }}

To use this class, each time the method is called, the return value of the method of object must be choose converted to the desired type, and if the type is wrong, the conversion fails at run time. We first tried to modify the Chooser class to make it generic, as suggested in article 29.

// A first cut at making Chooser generic - won't compilepublic class Chooser<T> {    private final T[] choiceArray;    public Chooser(Collection<T> choices) {        choiceArray = choices.toArray();    }    // choose method unchanged}

If you attempt to compile this class, you will get this error message:

Chooser.java:9: error: incompatible types: Object[] cannot beconverted to T[]        choiceArray = choices.toArray();                                     ^  where T is a type-variable:    T extends Object declared in class Chooser

It's no big deal. Convert an object array to a T array:

choiceArray = (T[]) choices.toArray();

This is not a mistake, but a warning is given:

Chooser.java:9: warning: [unchecked] unchecked cast        choiceArray = (T[]) choices.toArray();                                           ^  required: T[], found: Object[]  where T is a type-variable:T extends Object declared in class Chooser

The compiler tells you that you cannot guarantee the security of coercion at run time because the program does not know what type T represents--remember that element type information is deleted at run time by generics. Does the program work correctly? Yes, but the compiler cannot prove it. You can prove this by presenting evidence in the comments and using annotations to suppress the warning, but it is best to eliminate the cause of the warning (entry 27).

To eliminate unchecked cast warnings, use a list instead of an array. The following is another version of the Chooser class, with no errors or warnings at compile time:

// List-based Chooser - typesafepublic class Chooser<T> {    private final List<T> choiceList;    public Chooser(Collection<T> choices) {        choiceList = new ArrayList<>(choices);    }    public T choose() {        Random rnd = ThreadLocalRandom.current();        return choiceList.get(rnd.nextInt(choiceList.size()));    }}

This version is a bit verbose and may be slow to run, but it's worth mentioning that you won't get an exception at run time ClassCastException .

In summary, arrays and generics have very different types of rules. Arrays are covariant and materialized; Generics are immutable, type-erased. Therefore, the array provides the security of the run-time type, but does not provide security for compile-time types, and vice versa. In general, arrays and generics do not work well together. If you find that mixing them together and getting a compile-time error or warning, your first impulse should be to replace the array with a list.

Effective Java Third edition--28. List is better than array

Related Article

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.