Is it feasible to say that generic wildcard types should not be used in return parameters of a method?
In other words, does make sense to declare an interface like t
The main benefit of using wildcard types, say in method formal parameter, is to provide flexibility to the user to pass, say any type of Collection
, or List
or anything that implements Collection (assuming that the collection is declared like Collection<?>
). You would often find yourself using wildcard types in formal parameters.
But ideally you should avoid using them as return type of your method. Because that way, you would force the user of that method to use wildcard types at the caller end, even if they didn't want to. By using wildcard types, you're saying that, hey! this method can return any type of Collection
, so it's your job to take care of that. You shouldn't do that. Better to use bounded type parameter. With bounded type parameter, the type will be inferred based on the type you pass, or the target type of the method invocation.
And here's a quote from Effective Java Item 28:
Do not use wildcard types as return types. Rather than providing additional flexibility for your users, it would force them to use wildcard types in client code.
Properly used, wildcard types are nearly invisible to users of a class. They cause methods to accept the parameters they should accept and reject those they should reject. If the user of a class has to think about wildcard types, there is probably something wrong with the class’s API.
No, it is not feasible to say this.
Or to put it that way: It does make sense to have such an interface.
Imagine the following
interface Foo<T>
{
Collection<? extends T> next();
}
class FooInteger implements Foo<Number>
{
private final List<Integer> integers = new ArrayList<Integer>();
void useInternally()
{
integers.add(123);
Integer i = integers.get(0);
}
@Override
public Collection<? extends Number> next()
{
return integers;
}
}
// Using it:
Foo<Number> foo = new FooInteger();
Collection<? extends Number> next = foo.next();
Number n = next.iterator().next();
If you wrote the return type as Collection<T>
, you could not return a collection containing a subtype of T
.
Whether or not it is desirable to have such a return type depends on the application case. In some cases, it may simply be necessary. But if it is easy to avoid, then you can do this.
EDIT: Edited the code to point out the difference, namely that you might not always be able to choose the type internally. However, in most cases returning something that involves a wildcard can be avoided - and as I said, if possible, it should be avoided.
The example sketched above should still be considered as an example to emphasize the key point. Although, of course, such an implementation would be a bad practice, because it is exposing an internal state.
In this and similar cases, one can often return something like a
return Collections.<Number>unmodifiableList(integers);
and by this, declare the return type as Colletion<Number>
: The unmodifiableList
method solves the problem of the exposed internal state, and has the neat property that it allows changing the type parameter to a supertype, because the list is then... well, unmodifiable anyhow.
It is highly recommended not to use wildcard types as return types. Because the type inference rules are fairly complex it is unlikely the user of that API will know how to use it correctly. Let's take the example of method returning a "List". Is it possible on this list to add a Dog, a Cat, ... we simply don't know. And neither does the compiler, which is why it will not allow such a direct use. The use of wildcard types should be limited to method parameters.
This rule raises an issue when a method returns a wildcard type.
Noncompliant Code Example
List<? extends Animal> getAnimals(){...}
Compliant Solution
List<Animal> getAnimals(){...}
or
List<Dog> getAnimals(){...}
EDIT
SOURCE - https://rules.sonarsource.com/java/RSPEC-1452