## Why is this an issue?

A return type containing wildcards cannot be narrowed down in any context. This indicates that the developer’s intention was likely something else.

The core problem lies in type variance. Expressions at an input position, such as arguments passed to a method, can have a more specific type than the type expected by the method, which is called covariance. Expressions at an output position, such as a variable that receives the return result from a method, can have a more general type than the method’s return type, which is called contravariance. This can be traced back to the Liskov substitution principle.

In Java, type parameters of a generic type are invariant by default due to their potential occurrence in both input and output positions at the same time. A classic example of this is the methods `T get()` (output position) and `add(T element)` (input position) in interface `java.util.List`. We could construct cases with invalid typing in `List` if `T` were not invariant.

Wildcards can be employed to achieve covariance or contravariance in situations where the type parameter appears in one position only:

- `<? extends Foo>` for covariance (input positions)
- `<? super Foo>` for contravariance (output positions)

However, covariance is ineffective for the return type of a method since it is not an input position. Making it contravariant also has no effect since it is the receiver of the return value which must be contravariant (use-site variance in Java). Consequently, a return type containing wildcards is generally a mistake.

## How to fix it

The solution to this problem depends on the original intention of the developer. Given the examples:

    List<? extends Animal> getAnimals() { ... } // Noncompliant, wildcard with no use
    List<? super Plant> getLifeforms() { ... }  // Noncompliant, wildcard with no use

You can remove the wildcards to make the types invariant:

    List<Animal> getAnimals() { ... }           // Compliant, using invariant type instead
    List<Plant> getLifeforms() { ... }          // Compliant, using invariant type instead

Or replace them with a super- or subtypes (still invariant):

    List<Dog> getAnimals() { ... }              // Compliant, using subtype instead
    List<Lifeform> getLifeforms() { ... }       // Compliant, using supertype instead

## Resources

### Documentation

- The Java™ Tutorials - Wildcards

### Articles & blog posts

- Sinisa Louc - A Complete Guide to Variance in Java and Scala
- Kotlin Expertise Blog - Kotlin Generics and Variance (Compared to Java)
- Wikipedia - Covariance and contravariance (computer science)
- Schneide Blog - Declaration-site and use-site variance explained
- Wikipedia - Liskov substitution principle