泛型。使用通配符`<! - ? - >`vs使用类型参数`<e>`

时间:2017-07-13 16:26:03

标签: java generics wildcard

所以我通过官方的java教程https://docs.oracle.com/javase/tutorial/java/generics/index.html,也通过stackoverflow搜索,结果发现使用<?><E>之间没有太大区别,一个是我可以理解是普通的通用形式,另一个是通配符。到目前为止,我遇到的唯一区别是,当使用<E extetnds BlaBlaClass>E时,我们可以将类型称为<?>,否则我们不知道有关集合或数组的任何信息或类型。

我的问题是:使用<E>(通配符)优于普通泛型<?>是否有任何优势?如果是这样,这种情况的情况是什么?为什么有人会使用通配符呢?

我查看了Difference between generic type and wildcard typeDifference between ? (wildcard) and Type Parameter in JavaWhen to use generic methods and when to use wild-card?Java Generics ? , E and T what is the difference?。到目前为止,<E>似乎是package jsjf; import jsjf.exceptions.*; import java.util.*; /** * LinkedList represents a linked implementation of a list. * * @author Java Foundations * @version 4.0 */ public class LinkedList<T> implements ListADT<T>, Iterable<T> { protected int count; protected LinearNode<T> head, tail; protected int modCount; /** * Creates an empty list. */ public LinkedList() { count = 0; head = tail = null; modCount = 0; } /** * Adds a new element to the end of the list. * * @param element the element to add. */ public void add(T element) { // If the list is empty... if(this.tail == null){ // Creates a new node for the new element. LinearNode<T> newElement = new LinearNode(element); // Assigns the new node to be the head and the tail. this.tail = this.head = newElement; } // Otherwise... else { // Temporary caches the old tail. LinearNode<T> temp = this.tail; // Creates a new node for the new element. LinearNode<T> newElement = new LinearNode(element); // Assigns the new node to follow the old tail. temp.setNext(newElement); // Assigns the old tail to precede the new node. newElement.setPrevious(temp); // Assigns the new node to be the tail. this.tail = newElement; } } /** * Adds a new element at a specific index, bumping the current element at * that index to the next index. * * @param index the index to add to. * @param element the element to add. */ public void add(int index, T element) { // Find the node at the index requested. LinearNode<T> desiredIndex = this.head; for(int i = 1; i <= index; i++){ desiredIndex = desiredIndex.getNext(); } // Hold the previous element of the old element. LinearNode<T> prev = desiredIndex.getPrevious(); // Create a new node for the new element. LinearNode<T> newElement = new LinearNode(element); // Assign prev to precede the new element. newElement.setPrevious(prev); // Assign the old element to follow the new element. newElement.setNext(desiredIndex); // Assign the new element to precede the old element. desiredIndex.setPrevious(newElement); } /** * Returns an element from a specific index, without removing it. * * @param index the index to check. * @return the element at that index. */ public T get(int index) { // Find the node at the index requested. LinearNode<T> desiredIndex = this.head; for(int i = 1; i <= index; i++){ desiredIndex = desiredIndex.getNext(); } // Return that element. return desiredIndex.getElement(); } /** Removes the first element in this list and returns a reference * to it. Throws an EmptyCollectionException if the list is empty. * * @return a reference to the first element of this list * @throws EmptyCollectionException if the list is empty */ public T removeFirst() throws EmptyCollectionException { if (isEmpty()) throw new EmptyCollectionException("list"); T result = head.getElement(); head = head.getNext(); count--; if(isEmpty()) tail = null; modCount++; return result; } /** * Removes the last element in this list and returns a reference * to it. Throws an EmptyCollectionException if the list is empty. * * @return the last element in this list * @throws EmptyCollectionException if the list is empty */ public T removeLast() throws EmptyCollectionException { if (isEmpty()) throw new EmptyCollectionException("list"); T result = tail.getElement(); tail = tail.getPrevious(); count--; if(isEmpty()) tail = null; modCount++; return result; } /** * Removes the first instance of the specified element from this * list and returns a reference to it. Throws an EmptyCollectionException * if the list is empty. Throws a ElementNotFoundException if the * specified element is not found in the list. * * @param targetElement the element to be removed from the list * @return a reference to the removed element * @throws EmptyCollectionException if the list is empty * @throws ElementNotFoundException if the target element is not found */ public T remove(T targetElement) throws EmptyCollectionException, ElementNotFoundException { if (isEmpty()) throw new EmptyCollectionException("LinkedList"); boolean found = false; LinearNode<T> previous = null; LinearNode<T> current = head; while (current != null && !found) if (targetElement.equals(current.getElement())) found = true; else { previous = current; current = current.getNext(); } if (!found) throw new ElementNotFoundException("LinkedList"); if (size() == 1) // only one element in the list head = tail = null; else if (current.equals(head)) // target is at the head head = current.getNext(); else if (current.equals(tail)) // target is at the tail { tail = previous; tail.setNext(null); } else // target is in the middle previous.setNext(current.getNext()); count--; modCount++; return current.getElement(); } /** * Changes the element at a specific index. * * @param index the index to change. * @param element the element to change to. */ public void set(int index, T element) { // Find the node at the index requested. LinearNode<T> desiredIndex = this.head; for(int i = 1; i <= index; i++){ desiredIndex = desiredIndex.getNext(); } // Change the element at that index. desiredIndex.setElement(element); } /** * Returns the first element in this list without removing it. * * @return the first element in this list * @throws EmptyCollectionException if the list is empty */ public T first() throws EmptyCollectionException { if (isEmpty()) throw new EmptyCollectionException("list"); T result = head.getElement(); return result; } /** * Returns the last element in this list without removing it. * * @return the last element in this list * @throws EmptyCollectionException if the list is empty */ public T last() throws EmptyCollectionException { if (isEmpty()) throw new EmptyCollectionException("list"); T result = tail.getElement(); return result; } /** * Returns true if the specified element is found in this list and * false otherwise. Throws an EmptyCollectionException if the list * is empty. * * @param targetElement the element that is sought in the list * @return true if the element is found in this list * @throws EmptyCollectionException if the list is empty */ public boolean contains(T targetElement) throws EmptyCollectionException { if(isEmpty()) throw new EmptyCollectionException("list"); boolean found = false; LinearNode<T> previous = null; LinearNode<T> current = head; while (current != null && !found) if (targetElement.equals(current.getElement())) found = true; else { previous = current; current = current.getNext(); } if (!found) throw new ElementNotFoundException("LinkedList"); return found; } /** * Returns true if this list is empty and false otherwise. * * @return true if the list is empty, false otherwise */ public boolean isEmpty() { return (count == 0); } /** * Returns the number of elements in this list. * * @return the number of elements in the list */ public int size() { return count; } /** * Returns a string representation of this list. * * @return a string representation of the list */ public String toString() { String result = ""; LinearNode current = head; while (current != null) { result = result + current.getElement() + "\n"; current = current.getNext(); } return result; } /** * Returns an iterator for the elements in this list. * * @return an iterator over the elements of the list */ public Iterator<T> iterator() { return new LinkedListIterator(); } /** * LinkedIterator represents an iterator for a linked list of linear nodes. */ private class LinkedListIterator implements Iterator<T> { private int iteratorModCount; // the number of elements in the collection private LinearNode<T> current; // the current position /** * Sets up this iterator using the specified items. * * @param collection the collection the iterator will move over * @param size the integer size of the collection */ public LinkedListIterator() { current = head; iteratorModCount = modCount; } /** * Returns true if this iterator has at least one more element * to deliver in the iteration. * * @return true if this iterator has at least one more element to deliver * in the iteration * @throws ConcurrentModificationException if the collection has changed * while the iterator is in use */ public boolean hasNext() throws ConcurrentModificationException { if (iteratorModCount != modCount) throw new ConcurrentModificationException(); return (current != null); } /** * Returns the next element in the iteration. If there are no * more elements in this iteration, a NoSuchElementException is * thrown. * * @return the next element in the iteration * @throws NoSuchElementException if the iterator is empty */ public T next() throws ConcurrentModificationException { if (!hasNext()) throw new NoSuchElementException(); T result = current.getElement(); current = current.getNext(); return result; } /** * The remove operation is not supported. * * @throws UnsupportedOperationException if the remove operation is called */ public void remove() throws UnsupportedOperationException { throw new UnsupportedOperationException(); } } }

的较差版本

2 个答案:

答案 0 :(得分:6)

无界通配符?在通用类型不重要的情况下非常有用。我们假设您有一种检查列表大小的方法,如果它太大,则清除它,并且您希望它接受包含任何类型元素的列表:

public static <E> void checkList(List<E> list, int max) {
    if (list.size() > max) list.clear();
}

您声明了类型变量E,但size()clear()方法并不需要它,因此它未被使用。相反,你可以这样做:

public static void checkList(List<?> list, int max) {
    if (list.size() > max) list.clear();
}

这简化了方法声明,并向其他程序员明确表示此方法不关心列表元素的类型。

无界通配符也可用于字段或局部变量声明(无法声明类型变量),以允许赋值为任何泛型类型。

public static void main(String[] args) {
    List<?> list;
    list = new ArrayList<Object>();
    list = new ArrayList<String>();
    list = new ArrayList<Integer>();
}

如果这是List<Object>,则最后两行不会编译。它可以是<? extends Object>,但这相当于<?>

对于更实际的示例,假设您想要一个值可以是任何列表的地图:

public class MyClass {
    public final Map<String, List<?>> lists = new HashMap<>();
}

最后,如果您需要将值转换为泛型类,并且您不确定其类型参数,则必须使用?。 (永远不要使用raw types,它会禁用通用类型安全检查。)一个很好的例子就是常见的equals()实现:

@Override
public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null) return false;
    if (getClass() != obj.getClass()) return false;
    MyClass<?> other = (MyClass<?>) obj;
    if (!myField.equals(other.myField)) return false;
    return true;
}

有关Java泛型的更多信息,请查看Angelika Langer的Generics FAQ

答案 1 :(得分:2)

假设您正在使用某些类Foo的实例。

  • 如果您有Foo,可以将其添加到List<Foo>List<? super Foo>,但不能添加到List<?>List<? extends Foo>
  • 如果你想要一个Foo,你可以从List<Foo>List<? extends Foo>但不能从List<?>List<? super Foo>获得它(它会被归类为仅仅对象)

所以,这就是优势:你可以根据Foo元素与列表进行交互。

如果您关心的是List是否为空,或其他不依赖于其元素的特定形状的东西,那么List<?>就可以了。

这同样适用于您使用泛型方法的情况,并且正在处理泛型E的实例而不是特定的类Foo:

public <E> void addIfEmpty(E element) {
    List<? super E> listOfE = ...; // or List<E>
    if (!listOfE.isEmpty()) {
        listOfE.add(element);
    }

    List<?> listOfWild = ...;
    if (!listOfWild.isEmpty()) {
        listOfWild.add(element); // compilation error
    }
}