所以我通过官方的java教程https://docs.oracle.com/javase/tutorial/java/generics/index.html,也通过stackoverflow搜索,结果发现使用<?>
和<E>
之间没有太大区别,一个是我可以理解是普通的通用形式,另一个是通配符。到目前为止,我遇到的唯一区别是,当使用<E extetnds BlaBlaClass>
或E
时,我们可以将类型称为<?>
,否则我们不知道有关集合或数组的任何信息或类型。
我的问题是:使用<E>
(通配符)优于普通泛型<?>
是否有任何优势?如果是这样,这种情况的情况是什么?为什么有人会使用通配符呢?
我查看了Difference between generic type and wildcard type,Difference between ? (wildcard) and Type Parameter in Java,When 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();
}
}
}
答案 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的实例。
List<Foo>
或List<? super Foo>
,但不能添加到List<?>
或List<? extends 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
}
}