I gather that you cannot bind a Java generics type parameter to a lower bound (i.e. using the super
keyword). I was reading what the Angelika Langer Generics FAQ had to say on the subject. They say it basically comes down to a lower bound being useless ("not making any sense").
I'm not convinced. I can imagine a use for them to help you be more flexible to callers of a library method that produces a typed result. Imagine a method that created an array list of a user-specified size and filled it with the empty string. A simple declaration would be
public static ArrayList<String> createArrayListFullOfEmptyStrings(int i);
But that's unnecessarily restrictive to your clients. Why can't they invoke your method like this:
//should compile
List<Object> l1 = createArrayListFullOfEmptyStrings(5);
List<CharSequence> l2 = createArrayListFullOfEmptyStrings(5);
List<String> l3 = createArrayListFullOfEmptyStrings(5);
//shouldn't compile
List<Integer> l4 = createArrayListFullOfEmptyStrings(5);
At this point I would be tempted to try the following definition:
public static <T super String> List<T> createArrayListFullOfEmptyStrings(int size) {
List<T> list = new ArrayList<T>(size);
for(int i = 0; i < size; i++) {
list.add("");
}
return list;
}
But it will not compile; the super
keyword is illegal in this context.
Is my example above a bad example (ignoring what I say below)? Why isn't a lower bound useful here? And if it would be useful, what's the real reason that it is not permitted in Java?
P.S.
I know that a better organization might be something like this:
public static void populateListWithEmptyStrings(List<? super String> list, int size);
List<CharSequence> list = new ArrayList<CharSequence>();
populateListWithEmptyStrings(list, 5);
Can we for the purpose of this question pretend that due to a requirement, we need to do both operations in one method call?
Edit
@Tom G (justifiably) asks what benefit having a List<CharSequence>
would have over a List<String>
. For one, nobody said the returned list is immutable, so here's one advantage:
List<CharSequence> l2 = createArrayListFullOfEmptyStrings(5);
l2.add(new StringBuilder("foo").append("bar"));
Collection.toArray(T[])
. By changing the type variable from<T>
to<T super E>
, you could preventArrayStoreException
s from happening with that method. – BonnetoArray
isn't bound in any way to the element type of the collection. – Zsolway<T, C extends Collection<T> super List<T>> C internIfEmpty(C collection) { return collection.isEmpty() ? Collections.emptyList() : collection; }
– Unweighed<T> Collection<T> internIfEmpty(Collection<T>)
and another<T> List<T> internIfEmpty(List<T>)
. The compiler will call the more specific one for you, and you can just have the first return the result of the second. – Zsolway