Effective Java-about generics

Source: Internet
Author: User
Tags comparable iterable

Generics, which have been used since Java 1.5, have a more intuitive impression of ... "the type in the angle brackets I do not have to check the type and do not have to go strong."


Indeed, what is the point of generics if you think of the problem from the user's point of view of the API?

Discover errors as soon as possible after they is made, ideally at compile time.

Generics provide precisely this capability.
For example, there is a collection of only strings that are allowed to join, and in the absence of a declaration type parameter, this restriction is usually guaranteed by gaze.
Then add an integer instance to the collection, use a strong turn when fetching elements from the collection, and result in classcastexception.
Such an error occurs at run time, and compiler is helpless, while declaring the type parameter is compile-time error.
Compared to the raw type, the benefits of generics are obvious-security and presentation.


So, would it be better to have generics than the raw type?
What if the type parameter is object?
What is the difference between this usage and the raw type?
If only the code is described, it can be said that the raw type does not necessarily support which type, and collection<object> supports any type.
Right, but it doesn't make sense.
Generics have a rule called subtyping rules, such as a list is a subtype of a list, but not a list<object> subtype.
The following code describes the situation:

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


The above situation causes the runtime to discover errors, and the following is a compilation that fails:

Test.java:5: unsafeAdd(List<Object>,Object) cannot be applied to (List<String>,Integer)    unsafeAdd(strings, new Integer(42));    ^

In response to this scenario, Java provides the unbounded wildcard type.
That is, use '? ' for indeterminate type parameters Replace.
For example, set<?> can be understood as a collection of a type.


The raw type is rarely used when encoding, but there are two exceptions, all related to generic erasure.

    • Must use raw types in class literals.
    • It is illegal to use the instanceof operator on parameterized type.

So why is Java still keeping the raw type usage?
Java 1.5 was released at the time of the first ten years of Java, there is already a lot of code.
The old code and the use of the new features of the code can be mutually important, namely migration compatibility .


OK, now let's talk about using generics.
With respect to the raw type, a warning appears with the use of the raw type in some IDES and a hint to add @suppresswarnings.
For example, in eclipse:


What does @SuppressWarnings really do?
The author reminds us: to try to eliminate these warnings .
The hint that we add @suppresswarnings is to convey the message that the security of type conversions cannot be checked at run time.
The programmer's elimination of these warnings is to convey a message that the runtime does not appear classcastexception.


Elimination of warnings takes precedence over the way the claim type parameters are used, and @suppresswarnings is used if for some reason the warning cannot be eliminated and the code is required to prove that there is no problem.
As shown, @SuppressWarnings can be used on variables and methods, which we prioritize for smaller granularity.
For @suppresswarnings, do not ignore, and not blind.


Then talk about subtyping, always feel that because of this characteristic, generics sometimes seem troublesome.
The book is covariant (covariant) and invaritant.
For example, an array is covariant, such as a sub is a subclass of super, then sub[] is a subclass of super[].
Conversely, generics are invariant, and list is not a subclass of list.


Given this distinction, arrays and generics are difficult to mix and use.
For example, here are some of the following are illegal:

new List<E>[]new List<String>[]new E[]


Here's a snippet of code that explains why a generic array is illegal:

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


First, assume that the first line is legal.
The second line itself is legal, since the first line is legal and the array is covariant, and the third line is legal.
Since the generic is implemented with erase, that is, the run-time type of the list is list, and the runtime type of list[] is list[], the fourth line is legal.
In line fifth, it becomes contradictory, what about the string type? Why did I declare a type parameter or a classcastexception?
That being the case, simply let the first line produce compile-time error, the generics have become beautiful again.


For example, the following code:

interface Function<T> {    T apply(T arg1, T arg2);}static Object reduce(List list, Function f, Object initVal) {    Object[] snapshot;    snapshot = list.toArray();    Object result = initVal;    for (Object e : snapshot)        result = f.apply(result, e);    return result;}


Now I want to change reduce to a generic method and change it to the following form:

static <E> E reduce(List<E> list, Function<E> f, E initVal) {    E[] snapshot = (E[])list.toArray();    E result = initVal;    for (E e : snapshot)        result = f.apply(result, e);    return result;}


Obviously, the result is that the compilation fails.
The result is that there is no problem except that the hint on the List.toarray is added to @suppresswarnings, and it works perfectly.
Don't ignore @suppresswarnings!. Prompting me to add @suppresswarnings is telling me that the security of type conversions cannot be checked at run time.
So should I add @suppresswarnings? And if you add it, how do you guarantee it?


In fact, the solution is very simple, is not mixed with arrays and generics, namely:

static <E> E reduce(List<E> list, Function<E> f, E initVal) {    List<E> snapshot;    synchronized (list) {        snapshot = new ArrayList<E>(list);    }    E result = initVal;    for (E e : snapshot)        result = f.apply(result, e);    return result;}


This is good, can use the generic type.
But in a different position, as a provider rather than a user, will the problem be so easy?
For example, describe a stack:

// 无法通过编译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;    }    // 检查方法略}


Obviously, the array is materialized, and new E[defaultINITIALcapacity] prompts cannot create a generic array of E.
So I changed the mechanism to (e[]) new Object[defaultINITIALcapacity].
This can be completely passed, and then prompted to let me add @suppresswarnings ...
I can't ignore it, but I can prove that there's no classcastexception.
That is, elements is private and there is no way to directly access it, and the type of push and pop is safe.
Or I can change the type of elements to object[], and it's OK to convert the element type to E at pop.


Rather than having a user perform a strong turn, the provider provides a safe generic.
But not all the cases are as smooth as the example above.
For example, I added one to the stack:

public void pushAll(Iterable<E> src) {    for (E e : src)        push(e);}


Then I will iterable into the stack, because generics are not covariant, decisive to a compile-time error.
But throw away these don't want to, put a bunch of integers into a bunch of number and appear so in the should.
So we have bounded wildcard type, the key is this bounded.
A '? ' is the wildcard, for which add the limit is bounded wildcard, namely:

public void pushAll(Iterable<? extends E>  src) {    for (E e : src)        push(e);}


Accordingly, we then provide a Popall method that adds the pop-out element to the specified collection.
For example, the elements in a stack can inevitably be added to Collection<object>, which is:

public void popAll(Collection<? super E>  dst) {    while (!isEmpty())        dst.add(pop());}


For the use of generic wildcard, the author states: Pecs,stands for Producer-extends, Consumer-super.
That is, the type parameter indicates that a producer uses < Extends T> the consumer uses < Super t>.
For example, for example, we want to merge elements of two sets, because this is a production behavior, the declaration is:

public static <E> Set<E> union(Set<? extends E> s1,Set<? extends E> s2);


Can I use wildcard characters in return types?
The author recommends that you try not to use it in the return type, because it complicates the call.
Let's take a slightly more complicated example, like I'm going to find the largest element in a list of parameter types.
The initial declaration is:

public static <T extends Comparable<T>> T max(List<T> list)


The returned result is obtained from the list parameter, so the parameter declaration is changed to LIST< Extends T> list.
That <t extends comparable> how to deal with it.
Take scheduledfuture and delayed in java.util.concurrent as an example.
(Ps:interface Scheduledfuture extends Delayed and interface Delayed extends comparable.)
That is, the type T itself does not implement comparable, but his parent class implements the comparable and is declared as:

public static <T extends Comparable<? super T> > T max(List<? extends T> list)


Finally, there is an interesting example, first look at the code:

public static void swap(List<?> list, int i, int j) {    list.set(i, list.set(j, list.get(i)));}


This code cannot be compiled because elements other than null cannot be added to list<?>.
Of course, there is no problem if you declare a type parameter directly, but now suppose we only use wildcards and cannot use raw type.
Now that we know the type parameter can be solved, we can:

public static void swap(List<?> list, int i, int j) {    swapHelper(list, i, j);}private static <E> void swapHelper(List<E> list, int i, int j) {    list.set(i, list.set(j, list.get(i)));}

Effective Java-about generics

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.