Effective Java Third edition--26. Do not use the original type

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.

Since Java 5, generics have become part of the language. Before generics, you must convert each object that is read from the collection. If someone accidentally inserts an object of the wrong type, it may fail at run time. Using generics, you tell the compiler which types of objects are allowed in each collection. The compiler automatically inserts a cast and tells you at compile time whether to attempt to insert an object of the wrong type. The result is a safe and clear procedure, but these benefits, not limited to collections, are at a cost. This chapter tells you how to maximize the benefits and minimize the complications.

26. Do not use the original type

First, there are a few terms. A class or interface whose declaration has one or more type parameters (type parameters), which is referred to as a generic class or generic interface [jls,8.1.2,9.1.2]. For example, the list interface has a single type parameter E, which represents its element type. The full name of the interface is List<E> (read as a list of "E"), but people often call it list. Generic classes and interfaces are collectively referred to as generic types (generic-types).

Each generic defines a set of parameterized types (parameterized types) that consist of a class or interface name, followed by an angle bracket <> list of the actual type parameter corresponding to the formal type parameter [jls,4.4,4.5] of the generic type. For example, List<String> (read as a "string list") is a parameterized type that represents a list whose element type is string. (string is the actual type parameter corresponding to the form type parameter e).

Finally, each generic defines an original type (raw type), which is the name of a generic type that does not have any type parameters [jls,4.8]. For example, List<E> the original type that corresponds to IS list. The behavior of the primitive type is like all generic type information is purged from the type declaration. They exist primarily to be compatible with code that is not pre-generic.

This is a typical collection declaration before generics are added to Java. Starting with Java 9, it's still legal, but it's not a typical way to declare it:

// Raw collection type - don't do this!// My stamp collection. Contains only Stamp instances.private final Collection stamps = ... ;

If you use this statement today, and then accidentally put the coin instance into your stamp collection, the wrong insert compiles and runs without errors (although the compiler emits a vague warning):

// Erroneous insertion of coin into stamp collectionstamps.add(new Coin( ... )); // Emits "unchecked call" warning

The error will not occur until you try to retrieve the coin instance from the stamp collection:

// Raw iterator type - don't do this!for (Iterator i = stamps.iterator(); i.hasNext(); )    Stamp stamp = (Stamp) i.next(); // Throws ClassCastException        stamp.cancel();

As mentioned in this book, it is worthwhile to find the error as soon as possible after the compilation is complete, ideally at compile time. In this case, the error is not found until run time, for a long period after the error occurs, and possibly away from the code that contains the error. Once you see ClassCastException, you must search the Code class library to find the method call that puts the coin instance into the stamp collection. The compiler can't help you because it doesn't understand the comment that says "only contains stamp instances."

For generics, a type declaration contains information, not a comment:

// Parameterized collection type - typesafeprivate final Collection<Stamp> stamps = ... ;

From this declaration, the compiler knows that the Stamps collection should contain only stamp instances and that it is true, assuming that your entire code class library compiles without (or suppresses; see entry 27) any warnings. When declaring stamps with a parameterized type declaration, an incorrect insert generates a compile-time error message telling you what the error is:

Test.java:9: error: incompatible types: Coin cannot be convertedto Stamp    c.add(new Coin());              ^

When retrieving elements from the collection, the compiler inserts an invisible cast for you and guarantees that they will not fail (assuming that none of your code will generate or prohibit any compiler warnings). Although the expectation of accidentally inserting an coin instance into the stamp collection may seem farfetched, the problem is true. For example, it is easy to imagine putting BigInteger into a collection that contains only BigDecimal instances.

As mentioned earlier, it is legal to use primitive types (generics without type parameters), but you should not do so. If you use the original type, you lose all of the security and expression benefits of generics . Given that you shouldn't use them, why do language designers first allow primitive types? The answer is for compatibility. When generics are added, Java is about to enter the second decade, and there is a lot of code that does not use generics. All of this code is legal, and interacting with new code that uses generics is considered critical. Passing an instance of a parameterized type to a method designed for the original type must be legal and vice versa. This requirement, known as migration compatibility, drives decisions to support the original type and uses Erasure to implement generics (entry 28).

Although primitive types such as list should not be used, you can use parameterized types to allow arbitrary objects (such as) to be inserted List<Object> . What is the difference between the original type list and the parameterized type List<Object> ? Loosely speaking, the former has chosen a generic type system, which explicitly tells the compiler that it can hold any type of object. Although you can List<String> pass a parameter to a list type, you cannot pass it to List<Object> a parameter of type. Generics have a rule of subtype, which List<String> is a subtype of the original type list, but not a List<Object> subtype of the parameterized type (entry 28). Therefore, if you use primitive types such as list, you lose type safety, but not if you use a parameterized type (for example List <Object> ).

For specific instructions, consider the following procedure:

// Fails at runtime - unsafeAdd method uses a raw type (List)!public static void main(String[] args) {    List<String> strings = new ArrayList<>();    unsafeAdd(strings, Integer.valueOf(42));    String s = strings.get(0); // Has compiler-generated cast}private static void unsafeAdd(List list, Object o) {    list.add(o);}

This program compiles, uses the original type list, but receives a warning:

Test.java:10: warning: [unchecked] unchecked call to add(E) as amember of the raw type List    list.add(o);            ^

In fact, if you run the program, you get a ClassCastException exception when the program attempts to invoke strings.get(0) the result (an integer) into a string. This is a compiler-generated cast, which is usually guaranteed to be successful, but in this case we ignore the compiler warning and pay the price.

If you replace the original type list with a parameterized type in the Unsafeadd declaration List <Object> and try to recompile the program, you will notice that it is no longer compiled, but instead emits an error message:

Test.java:5: error: incompatible types: List<String> cannot beconverted to List<Object>    unsafeAdd(strings, Integer.valueOf(42));

You might try to use primitive types to handle an unknown and insignificant collection of element types. For example, suppose you want to write a method that requires two sets of merges to return the number of elements that they collectively own. If you're new to generics, you can write this:

// Use of raw type for unknown element type - don't do this!static int numElementsInCommon(Set s1, Set s2) {    int result = 0;    for (Object o1 : s1)        if (s2.contains(o1))            result++;    return result;}

This method can work, but it uses the original type, which is dangerous. The security alternative is to use the unrestricted wildcard type (unbounded wildcard types). If you want to use a generic type, but do not know or care what the actual type parameters are, you can use a question mark instead. For example, Set<E> the unrestricted wildcard type of a generic type is Set <?> (reads "A collection of some kind"). It is the most versatile parameterized set type, capable of maintaining any set. Here's numElementsInCommon how the method uses the unrestricted wildcard type declaration:

// Uses unbounded wildcard type - typesafe and flexiblestatic int numElementsInCommon(Set<?> s1, Set<?> s2) { ... }

Set <?>What is the difference between an unrestricted wildcard and a set of primitive types? Do you really want to put anything in the question mark? This is not the point, but the wildcard type is safe and the original type is not. You can easily break the collection's type invariance by placing any element in a collection of the original type (as shown in the Unsafeadd method on page 119th); You cannot put any element (other than NULL) into one Collection <?> . Trying to do this produces a compile-time error message like this:

WildCard.java:13: error: incompatible types: String cannot beconverted to CAP#1    c.add("verboten");          ^  where CAP#1 is a fresh type-variable:    CAP#1 extends Object from capture of ?

Admittedly, this error message leaves something to be desired, but the compiler has done its job, regardless of its element type, and does not break the collection's type invariance. Not only can you put any element (other than NULL) into one Collection <?> , but you can't guarantee the type of object you get. If these restrictions are unacceptable, you can use a generic method (entry 30) or a restricted-character type (entry 31).

There are some small exceptions to rules that should not use the original type. You must use the original type in the class literal (literals) . Parameterized types are not allowed in the specification (although it allows array types and base types) [jls,15.8.2]. In other words, List.class String [] .class and int.class both are legal, but List <String> .class and List <?>.class not legal.

The second exception to the rule involves an instanceof operator. Because generic type information is deleted at run time, it is illegal to use the instanceof operator on a parameterized type other than an unrestricted wildcard type. Using the unrestricted wildcard type instead of the original type does not affect the behavior of the instanceof operator in any way. In this case, the angle brackets and question marks appear superfluous. The following is the preferred method for using the instanceof operator of a generic type:

// Legitimate use of raw type - instanceof operatorif (o instanceof Set) {       // Raw type    Set<?> s = (Set<?>) o;    // Wildcard type    ...}

Note that once the o object is determined to be a set, it must be converted to a wildcard character instead of the Set <?> original type set. This is a cast, so it does not cause a compiler warning.

In summary, using primitive types can cause run-time exceptions, so do not use them. They are only used for compatibility and interoperability with traditional code before the introduction of generics. As a quick review, a Set<Object> parameterized type that represents a collection that can contain any type of object, Set<?> is a wildcard type that represents a collection that can contain only certain unknown types of objects, set is an original type, and it is not listed in the generic type system. The first two types are safe and the last one is not.

For quick reference, the following table summarizes the terms described in this article (and some later in this chapter):

of the country
Terminology Chinese meaning Example articles
Parameterized type parameterized types List<String> Entry 26
Actual type parameter Actual type parameters String Entry 26
Generic type Generic type List<E> Entry 26
Formal type parameter Form type parameters E Entry 26
Unbounded wildcard Type Unrestricted wildcard type List<?> Entry 26
Raw type Original type List Entry 26
Bounded type parameter Restriction type parameters <E extends Number> Entry 29
Recursive type bound Recursive type restrictions <T extends Comparable<T>> Entry 30
Bounded wildcard type restricting wildcard types List<? extends Number> Entry 31
Generic method Generic methods static <E> List<E> asList(E[] a) Entry 30
Type token Type token String.class Entry 33

Effective Java Third edition--26. Do not use the original type

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.