Java 5.0 基础 - Generic

Unbounded Wildcard Types

      If you to use a generic type but you don’t know or care what the actual type parameter is, you can use a question mark instead. For example, the unbounded wildcard type for the generic type Set<E> (Set<?>) ( read "set of some type" ).  

 

What the difference between Raw Type and the Unbounded Wildcard Types?    

      Generic speaking - the wildcard type is safe and the raw type isn’t.

      More details,

               you can put any type into a raw type collection, easliy corrupting the collection's type invariant (collection 的不可变性). ( For example, you can put an Integer into a raw type String Collection )

               And you can't put any element(other than Null) into a Collection<?> to protect the collection's type invariant ( here the collection<?> means a somekind of unknown type, so we can not put any element into it ). If you want to do so, you will suffered the compile errors as below.

 The compiler just done its job to prevent you from corrupting the collection's type invariant. 

 

The rules to use the WildCardType

      1. You can't assume anything about the type of the objects that you get out

      2. You can't put any element into the Collection (cause you don't know what the type of the collection)

      If you have the 2 restriction above, then using the Unbounded Wildcard Type.

      or else you can use <? extends Object> or <? super Object>

The two minor exceptions that you must to use the Raw Type.

      1.  you must raw types in class iterals

           one word, List.class, String[].class, and Integer.class are all legal; but List<String>.class and List<?>.class are not legal.

      2.  instance of

           Because generic type information is erased at runtime, it is illegal to use the instanceof operator on parameterized types other than unbounded wildcard types. The use of unbounded wildcard types in place of raw types does not affect the behavior of the instanceof operator in any way.

          So the 2 clause if( o instance of Set<?>) and if( o instance of Set ) are equal 

SuppressWarings annotation

      Always use the SuppressWarnings annotation on the smallest scope possible.

      You might be tempted to put the annotation on the entire method, but don’t. Instead, declare a local variable to hold the return value and annotate its declaration, like

@SuppressWarnings("unchecked") T[] result = (T[]) Arrays.copyOf(elements, size, a.getClass());

 

Bounded  type parameter & Recursive type bound

Bounded type parameter: such as <E extends Number>

Recursive type bound:  such as < T extends Comparable<T> >

 

Recursive type bound

    For a type parameter to be bounded by some expression involving that type parameter itself. The most common use of recursive type bounds is in connection with the Comparable interface.

public interface Comparable<T> {
          int compareTo(T o);
}

    In practice, nearly all types can be compared only to elements of their own type.

    for example, String implements Comparable<String>, Integer implements Comparable<Integer>, and so on.

    There are many methods that take a list of elements that implement Comparable, in order to sort the list, search within it, calculate its minimum or maximum, and the like. To do any of these things, it is required that every element in the list be comparable to every other element in the list, in other words, that the elements of the list be mutually comparable. Here is how to express that constraint:

// Using a recursive type bound to express mutual comparability
public static <T extends Comparable<T>> T max(List<T> list) {...}

    The type bound <T extends Comparable<T>> may be read as “for every type T that can be compared to itself,” which corresponds more or less exactly to the notion of mutual comparability.

// Returns the maximum value in a list - uses recursive type bound
public static <T extends Comparable<T>> T max(List<T> list) {
          Iterator<T> i = list.iterator();
          T result = i.next();
          while (i.hasNext()) {
                  T t = i.next();
                  if (t.compareTo(result) > 0)  result = t;
          }
          return result;
}

 

Use bounded wildcards to increase API flexibility

    Suppose that we have the Stack

public class Stack<E> {
          public Stack(); 
          public void push(E e);
          public E pop();
          public boolean isEmpty();
}

1. Suppose we want to add a method that takes a sequence of elements and pushes them all onto the stack. Here’s a first attempt:

// pushAll method without wildcard type - deficient!
public void pushAll(Iterable<E> src) {
         for (E e : src)   push(e);
}

   This method compiles cleanly, but it isn’t entirely satisfactory. If the element type of the Iterable src exactly matches that of the stack, it works fine. But suppose you have a Stack<Number> and you invoke push(intVal), where intVal is of type Integer. where intVal is of type Integer. This works, because Integer is a subtype of Number.

    So logically, it seems that this should work, too:

    (main problem: parameterized type Iterator<Integer> is not compatible with Iterator<Number> )

Stack<Number> numberStack = new Stack<Number>();
Iterable<Integer> integers = ... ;
numberStack.pushAll(integers);

    This time the errors printed as below,

    StackTest.java:7: pushAll(Iterable<Number>) in Stack<Number>
    cannot be applied to (Iterable<Integer>)  numberStack.pushAll(integers);

    cause parameterized types are invariant,

    ( So the basic solution is to change the parameterized type from Iterator<Number> to be Iterator<? extends Number>, then Iterator<Integer> is compatible with Iterator<? extends Number>)

    So what we can do to fix the issue above is to use the bounded wildchar type <? extends E>, The language provides a special kind of parameterized type call a bounded wildcard type to deal with situations like this. The type of  the input parameter to pushAll should not be "Iterable of E" but "Iterable of some subtype of E," and there is a wildcard type that means precisely that: Iterable<?
extends E>. (The use of the keyword extends is defined so that every type is a subtype of itself(E), even though it does not extend itself(E).)

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

    

2.  Now suppose you want to write a popAll method to go with pushAll. The popAll method pops each element off the stack and adds the elements to the given collection

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

    But again, it doesn’t seem entirely satisfactory. Suppose you have a Stack<Number> and variable of type Object. 

  Stack<Number> numberStack = new Stack<Number>();
  Collection<Object> objects = ... ;
  numberStack.popAll(objects); 

    (The main problem: Collection<Object> is not compatible with Collection<Number> )

    If you try to compile this client code against the version of popAll above, you’ll get an error very similar to the one that we got with our first version of pushAll: Collection<Object> is not a subtype of Collection<Number> (Collection<Number> is also not the sub type of Collection<Object>.

    (The solution is using the parameterized Collection<? super E> to replace Collection<E>)

    Once again, wildcard types provide a way out. The type of the input parameter to popAll should not be “collection of E” but “collection of some supertype of E” (where supertype is defined such that E is a supertype of itself [JLS, 4.10]). Again, there is a wildcard type that means precisely that: Collection<? super E>. Let’s modify popAll to use it:

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

    For maximum flexibility, use wildcard types on input parameters that represent producers or consumers.

     in one words, if a parameterized type represents a T producer, use <? extends T>; if it represents a T consumer, use <? super T>..

 Integer i = new Integer(0);
 Set<? super Number> nums = new HashSet<Number>();
 nums.add(i); // only Number or Number's father can consume ( here means add the Integer )

     ( here the Set<? super Number> means it was a consumer that it will consume all Number, and most important is that only the Number itself or Number's father can consume the Integer (here, the consume means the behavior of nums.add(i) - and the type of nums is Number itself or its fathers ) )

 

     ( But if we use Set<? extends Number> to replace the Set<? super Number> that it will get the compile error, of cause that Set<? extends Number> means a producer which can not consume anything, but deeping thought on this is that any Type that extends from Number can not consume the Integer except for Integer and the Number itself, but it's not fitted for all the other scenarios such(Double, Float...), So according with its semantic, it mustn't consume anything.  )

 

   (Basic rule: producer-extends, consumer-super (PECS). And remember that all comparables and comparators are consumers. That means when declear the bounded wildchar for Comparable should be always defined as Collection<? super E> )

    

      If an input parameter is both a producer and a consumer, then wildcard types will do you no good: you need an exact type match, which is what you get without any wildcards.

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值