What's the difference between unbounded wildca

2019-01-07 11:05发布

问题:

Could you help me understand the difference between unbounded wildcard type List and raw type List?

List<?> b;    // unbounded wildcard type
List a;       // raw type


Along with this can anybody help me understand what is a bounded type parameter list?

List<E extends Number> c;

回答1:

Here's a summary of the three:

  • List: A list with no type parameter. It is a list whose elements are of any type -- the elements may be of different types.

  • List<?>: A list with an unbounded type parameter. Its elements are of a specific, but unknown, type; the elements must all be the same type.

  • List<T extends E>: A list with a type parameter called T. The supplied type for T must be of a type that extends E, or it is not a valid type for the parameter.



回答2:

You should really look at Effective Java, Item 23: Don't use raw types in new code.

To use the example from that book, consider the following example... what if you have a collection where you do not care what types of elements are in it. For example, you want to see how many elements are in common between two sets. You might come up with the following:

public static int numElementsInCommon(Set s1, Set s2) {
  int result = 0;
  for (Object o : s1) {
    if (s2.contains(o)) {
      ++result;
    }
  }
  return result;
}

This example, while it works, is not a good idea to use because of the use of raw types. Raw types just aren't type safe at all... you could end up modifying the set in a way that is not type safe and corrupt your program. Instead, err on the side of caution and use the type safe alternative:

public static int numElementsInCommon(Set<?> s1, Set<?> s2) {
  int result = 0;
  for (Object o : s1) {
    if (s2.contains(o)) {
      ++result;
    }
  }
  return result;
}

The difference is that you can only add null to a Set<?>, and you CANNOT assume anything about the element you take out of a Set<?>. If you use a raw Set, you can add anything you want to it. The numElementsInCommon method is a good example where you don't even need to add anything and you don't need to assume anything about what is in the set. That's why it's a good candidate for using the ? wildcard.

Hope this helps. Read that whole Item in Effective Java and it will really become clear.

To answer the second part of your question... remember that I said when you use the ? wildcard, you cannot assume anything about the element you take out of the set? What if you do need to make an assumption about the interface of the object you removed from the set. For example, suppose you want to keep track of a set of Cool things.

public interface Cool {
  // Reports why the object is cool
  void cool();
}

Then you might have some code like this:

public static void reportCoolness(Set s) {
  for (Object item : s) {
    Cool coolItem = (Cool) item;
    coolItem.cool();
  }
}

This is not type safe... you need to make sure you passed in a set with only Cool objects. To fix it, you might say:

public static void reportCoolness(Set<Cool> s) {
  for (Cool coolItem : s) {
    coolItem.cool();
  }
}

This is great! Does exactly what you want and is type safe. But what if later you have this:

public interface ReallyCool extends Cool {
  // Reports why the object is beyond cool
  void reallyCool();
}

Since all ReallyCool objects are Cool, you ought to be able to do the following:

Set<ReallyCool> s = new HashSet<ReallyCool>();
// populate s
reportCoolness(s);

But you can't do that because generics have the following property: Suppose B is a subclass of A, then Set<B> is NOT a subclass of Set<A>. The technical talk for this is "Generic types are invariant." (As opposed to covariant).

To get the last example to work you would need to create a Set<Cool> by casting (safely) every element in the Set<ReallyCool>. To avoid letting clients of your api go through this nasty, unnecessary code, you can just make the reportCoolness method more flexible like this:

public static void reportCoolness(Set<? extends Cool> s) {
  for (Cool coolItem : s) {
    coolItem.cool();
  }
}

Now your method takes any Set that contains elements that are Cool or any subclass of Cool. All of these types adhere to the Cool api... so we can safely call the cool() method on any element

Make sense? Hope this helps.



回答3:

On your first question, the difference between List and List<?>:

One significant difference between the two is that when you have an wildcard as the type, the type of the Collection is unknown, so the add method will throw a compile time error.

You can still get values out of the List<?>, but you need an explicit cast.